@ -9,6 +9,9 @@
# ifdef _MSC_VER
# include <intrin.h>
# endif
# ifdef __BMI2__
# include <immintrin.h>
# endif
# include <cstdint>
# include <type_traits>
@ -20,11 +23,33 @@ ASSERT_FEATURE_COMPAT_HEADER();
namespace ROCKSDB_NAMESPACE {
// Fast implementation of extracting the bottom n bits of an integer.
// To ensure fast implementation, undefined if n bits is full width or more.
template < typename T >
inline T BottomNBits ( T v , int nbits ) {
static_assert ( std : : is_integral_v < T > , " non-integral type " ) ;
static_assert ( ! std : : is_reference_v < T > , " use std::remove_reference_t " ) ;
assert ( nbits > = 0 ) ;
assert ( nbits < int { 8 * sizeof ( T ) } ) ;
# ifdef __BMI2__
if constexpr ( sizeof ( T ) < = 4 ) {
return static_cast < T > ( _bzhi_u32 ( static_cast < uint32_t > ( v ) , nbits ) ) ;
}
if constexpr ( sizeof ( T ) < = 8 ) {
return static_cast < T > ( _bzhi_u64 ( static_cast < uint64_t > ( v ) , nbits ) ) ;
}
# endif
// Newer compilers compile this down to bzhi on x86, but some older
// ones don't, thus the need for the intrinsic above.
return static_cast < T > ( v & ( ( T { 1 } < < nbits ) - 1 ) ) ;
}
// Fast implementation of floor(log2(v)). Undefined for 0 or negative
// numbers (in case of signed type).
template < typename T >
inline int FloorLog2 ( T v ) {
static_assert ( std : : is_integral < T > : : value , " non-integral type " ) ;
static_assert ( std : : is_integral_v < T > , " non-integral type " ) ;
static_assert ( ! std : : is_reference_v < T > , " use std::remove_reference_t " ) ;
assert ( v > 0 ) ;
# ifdef _MSC_VER
static_assert ( sizeof ( T ) < = sizeof ( uint64_t ) , " type too big " ) ;
@ -63,6 +88,8 @@ inline int FloorLog2(T v) {
// Constexpr version of FloorLog2
template < typename T >
constexpr int ConstexprFloorLog2 ( T v ) {
// NOTE: not checking is_integral so that this works with Unsigned128
static_assert ( ! std : : is_reference_v < T > , " use std::remove_reference_t " ) ;
int rv = 0 ;
while ( v > T { 1 } ) {
+ + rv ;
@ -74,7 +101,8 @@ constexpr int ConstexprFloorLog2(T v) {
// Number of low-order zero bits before the first 1 bit. Undefined for 0.
template < typename T >
inline int CountTrailingZeroBits ( T v ) {
static_assert ( std : : is_integral < T > : : value , " non-integral type " ) ;
static_assert ( std : : is_integral_v < T > , " non-integral type " ) ;
static_assert ( ! std : : is_reference_v < T > , " use std::remove_reference_t " ) ;
assert ( v ! = 0 ) ;
# ifdef _MSC_VER
static_assert ( sizeof ( T ) < = sizeof ( uint64_t ) , " type too big " ) ;
@ -115,6 +143,9 @@ namespace detail {
template < typename T >
int BitsSetToOneFallback ( T v ) {
static_assert ( std : : is_integral_v < T > , " non-integral type " ) ;
static_assert ( ! std : : is_reference_v < T > , " use std::remove_reference_t " ) ;
const int kBits = static_cast < int > ( sizeof ( T ) ) * 8 ;
static_assert ( ( kBits & ( kBits - 1 ) ) = = 0 , " must be power of two bits " ) ;
// we static_cast these bit patterns in order to truncate them to the correct
@ -140,7 +171,9 @@ int BitsSetToOneFallback(T v) {
// Number of bits set to 1. Also known as "population count".
template < typename T >
inline int BitsSetToOne ( T v ) {
static_assert ( std : : is_integral < T > : : value , " non-integral type " ) ;
static_assert ( std : : is_integral_v < T > , " non-integral type " ) ;
static_assert ( ! std : : is_reference_v < T > , " use std::remove_reference_t " ) ;
# ifdef _MSC_VER
static_assert ( sizeof ( T ) < = sizeof ( uint64_t ) , " type too big " ) ;
if ( sizeof ( T ) < sizeof ( uint32_t ) ) {
@ -192,7 +225,9 @@ inline int BitsSetToOne(T v) {
template < typename T >
inline int BitParity ( T v ) {
static_assert ( std : : is_integral < T > : : value , " non-integral type " ) ;
static_assert ( std : : is_integral_v < T > , " non-integral type " ) ;
static_assert ( ! std : : is_reference_v < T > , " use std::remove_reference_t " ) ;
# ifdef _MSC_VER
// bit parity == oddness of popcount
return BitsSetToOne ( v ) & 1 ;
@ -214,7 +249,8 @@ inline int BitParity(T v) {
// encode/decode big endian.
template < typename T >
inline T EndianSwapValue ( T v ) {
static_assert ( std : : is_integral < T > : : value , " non-integral type " ) ;
static_assert ( std : : is_integral_v < T > , " non-integral type " ) ;
static_assert ( ! std : : is_reference_v < T > , " use std::remove_reference_t " ) ;
# ifdef _MSC_VER
if ( sizeof ( T ) = = 2 ) {
@ -244,6 +280,9 @@ inline T EndianSwapValue(T v) {
// Reverses the order of bits in an integral value
template < typename T >
inline T ReverseBits ( T v ) {
static_assert ( std : : is_integral_v < T > , " non-integral type " ) ;
static_assert ( ! std : : is_reference_v < T > , " use std::remove_reference_t " ) ;
T r = EndianSwapValue ( v ) ;
const T kHighestByte = T { 1 } < < ( ( sizeof ( T ) - 1 ) * 8 ) ;
const T kEveryByte = kHighestByte | ( kHighestByte / 255 ) ;
@ -277,7 +316,8 @@ inline T ReverseBits(T v) {
// is that all square sub-matrices that include the top row are invertible.
template < typename T >
inline T DownwardInvolution ( T v ) {
static_assert ( std : : is_integral < T > : : value , " non-integral type " ) ;
static_assert ( std : : is_integral_v < T > , " non-integral type " ) ;
static_assert ( ! std : : is_reference_v < T > , " use std::remove_reference_t " ) ;
static_assert ( sizeof ( T ) < = 8 , " only supported up to 64 bits " ) ;
uint64_t r = static_cast < uint64_t > ( v ) ;
@ -296,4 +336,16 @@ inline T DownwardInvolution(T v) {
return static_cast < T > ( r ) ;
}
// Bitwise-And with typing that allows you to avoid writing an explicit cast
// to the smaller type, or the type of the right parameter if same size.
template < typename A , typename B >
inline std : : conditional_t < sizeof ( A ) < sizeof ( B ) , A , B > BitwiseAnd ( A a , B b ) {
static_assert ( std : : is_integral_v < A > , " non-integral type " ) ;
static_assert ( std : : is_integral_v < B > , " non-integral type " ) ;
static_assert ( ! std : : is_reference_v < A > , " use std::remove_reference_t " ) ;
static_assert ( ! std : : is_reference_v < B > , " use std::remove_reference_t " ) ;
using Smaller = std : : conditional_t < sizeof ( A ) < sizeof ( B ) , A , B > ;
return static_cast < Smaller > ( a & b ) ;
}
} // namespace ROCKSDB_NAMESPACE