Rocket.Chat.ReactNative/ios/Pods/Folly/folly/lang/Bits.h

413 lines
12 KiB
C
Raw Normal View History

/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* Various low-level, bit-manipulation routines.
*
* findFirstSet(x) [constexpr]
* find first (least significant) bit set in a value of an integral type,
* 1-based (like ffs()). 0 = no bits are set (x == 0)
*
* findLastSet(x) [constexpr]
* find last (most significant) bit set in a value of an integral type,
* 1-based. 0 = no bits are set (x == 0)
* for x != 0, findLastSet(x) == 1 + floor(log2(x))
*
* extractFirstSet(x) [constexpr]
* extract first (least significant) bit set in a value of an integral
* type, 0 = no bits are set (x == 0)
*
* nextPowTwo(x) [constexpr]
* Finds the next power of two >= x.
*
* isPowTwo(x) [constexpr]
* return true iff x is a power of two
*
* popcount(x)
* return the number of 1 bits in x
*
* Endian
* convert between native, big, and little endian representation
* Endian::big(x) big <-> native
* Endian::little(x) little <-> native
* Endian::swap(x) big <-> little
*
* @author Tudor Bosman (tudorb@fb.com)
*/
#pragma once
#include <cassert>
#include <cinttypes>
#include <cstdint>
#include <cstring>
#include <limits>
#include <type_traits>
#include <folly/ConstexprMath.h>
#include <folly/Portability.h>
#include <folly/Traits.h>
#include <folly/Utility.h>
#include <folly/lang/Assume.h>
#include <folly/portability/Builtins.h>
namespace folly {
#if __cpp_lib_bit_cast
using std::bit_cast;
#else
// mimic: std::bit_cast, C++20
template <
typename To,
typename From,
std::enable_if_t<
sizeof(From) == sizeof(To) && is_trivially_copyable<To>::value &&
is_trivially_copyable<From>::value,
int> = 0>
To bit_cast(const From& src) noexcept {
aligned_storage_for_t<To> storage;
std::memcpy(&storage, &src, sizeof(From));
return reinterpret_cast<To&>(storage);
}
#endif
namespace detail {
template <typename Dst, typename Src>
constexpr std::make_signed_t<Dst> bits_to_signed(Src const s) {
static_assert(std::is_signed<Dst>::value, "unsigned type");
return to_signed(static_cast<std::make_unsigned_t<Dst>>(to_unsigned(s)));
}
template <typename Dst, typename Src>
constexpr std::make_unsigned_t<Dst> bits_to_unsigned(Src const s) {
static_assert(std::is_unsigned<Dst>::value, "signed type");
return static_cast<Dst>(to_unsigned(s));
}
} // namespace detail
/// findFirstSet
///
/// Return the 1-based index of the least significant bit which is set.
/// For x > 0, the exponent in the largest power of two which does not divide x.
template <typename T>
inline constexpr unsigned int findFirstSet(T const v) {
using S0 = int;
using S1 = long int;
using S2 = long long int;
using detail::bits_to_signed;
static_assert(sizeof(T) <= sizeof(S2), "over-sized type");
static_assert(std::is_integral<T>::value, "non-integral type");
static_assert(!std::is_same<T, bool>::value, "bool type");
// clang-format off
return static_cast<unsigned int>(
sizeof(T) <= sizeof(S0) ? __builtin_ffs(bits_to_signed<S0>(v)) :
sizeof(T) <= sizeof(S1) ? __builtin_ffsl(bits_to_signed<S1>(v)) :
sizeof(T) <= sizeof(S2) ? __builtin_ffsll(bits_to_signed<S2>(v)) :
0);
// clang-format on
}
/// findLastSet
///
/// Return the 1-based index of the most significant bit which is set.
/// For x > 0, findLastSet(x) == 1 + floor(log2(x)).
template <typename T>
inline constexpr unsigned int findLastSet(T const v) {
using U0 = unsigned int;
using U1 = unsigned long int;
using U2 = unsigned long long int;
using detail::bits_to_unsigned;
static_assert(sizeof(T) <= sizeof(U2), "over-sized type");
static_assert(std::is_integral<T>::value, "non-integral type");
static_assert(!std::is_same<T, bool>::value, "bool type");
// If X is a power of two X - Y = 1 + ((X - 1) ^ Y). Doing this transformation
// allows GCC to remove its own xor that it adds to implement clz using bsr.
// clang-format off
using size = index_constant<constexpr_max(sizeof(T), sizeof(U0))>;
return v ? 1u + static_cast<unsigned int>((8u * size{} - 1u) ^ (
sizeof(T) <= sizeof(U0) ? __builtin_clz(bits_to_unsigned<U0>(v)) :
sizeof(T) <= sizeof(U1) ? __builtin_clzl(bits_to_unsigned<U1>(v)) :
sizeof(T) <= sizeof(U2) ? __builtin_clzll(bits_to_unsigned<U2>(v)) :
0)) : 0u;
// clang-format on
}
/// extractFirstSet
///
/// Return a value where all the bits but the least significant are cleared.
template <typename T>
inline constexpr T extractFirstSet(T const v) {
static_assert(std::is_integral<T>::value, "non-integral type");
static_assert(std::is_unsigned<T>::value, "signed type");
static_assert(!std::is_same<T, bool>::value, "bool type");
return v & -v;
}
/// popcount
///
/// Returns the number of bits which are set.
template <typename T>
inline constexpr unsigned int popcount(T const v) {
using U0 = unsigned int;
using U1 = unsigned long int;
using U2 = unsigned long long int;
using detail::bits_to_unsigned;
static_assert(sizeof(T) <= sizeof(U2), "over-sized type");
static_assert(std::is_integral<T>::value, "non-integral type");
static_assert(!std::is_same<T, bool>::value, "bool type");
// clang-format off
return static_cast<unsigned int>(
sizeof(T) <= sizeof(U0) ? __builtin_popcount(bits_to_unsigned<U0>(v)) :
sizeof(T) <= sizeof(U1) ? __builtin_popcountl(bits_to_unsigned<U1>(v)) :
sizeof(T) <= sizeof(U2) ? __builtin_popcountll(bits_to_unsigned<U2>(v)) :
0);
// clang-format on
}
template <class T>
inline constexpr T nextPowTwo(T const v) {
static_assert(std::is_unsigned<T>::value, "signed type");
return v ? (T(1) << findLastSet(v - 1)) : T(1);
}
template <class T>
inline constexpr T prevPowTwo(T const v) {
static_assert(std::is_unsigned<T>::value, "signed type");
return v ? (T(1) << (findLastSet(v) - 1)) : T(0);
}
template <class T>
inline constexpr bool isPowTwo(T const v) {
static_assert(std::is_integral<T>::value, "non-integral type");
static_assert(std::is_unsigned<T>::value, "signed type");
static_assert(!std::is_same<T, bool>::value, "bool type");
return (v != 0) && !(v & (v - 1));
}
/**
* Endianness detection and manipulation primitives.
*/
namespace detail {
template <size_t Size>
struct uint_types_by_size;
#define FB_GEN(sz, fn) \
static inline uint##sz##_t byteswap_gen(uint##sz##_t v) { \
return fn(v); \
} \
template <> \
struct uint_types_by_size<sz / 8> { \
using type = uint##sz##_t; \
};
FB_GEN(8, uint8_t)
#ifdef _MSC_VER
FB_GEN(64, _byteswap_uint64)
FB_GEN(32, _byteswap_ulong)
FB_GEN(16, _byteswap_ushort)
#else
FB_GEN(64, __builtin_bswap64)
FB_GEN(32, __builtin_bswap32)
FB_GEN(16, __builtin_bswap16)
#endif
#undef FB_GEN
template <class T>
struct EndianInt {
static_assert(
(std::is_integral<T>::value && !std::is_same<T, bool>::value) ||
std::is_floating_point<T>::value,
"template type parameter must be non-bool integral or floating point");
static T swap(T x) {
// we implement this with bit_cast because that is defined behavior in C++
// we rely on compilers to optimize away the bit_cast calls
constexpr auto s = sizeof(T);
using B = typename uint_types_by_size<s>::type;
return bit_cast<T>(byteswap_gen(bit_cast<B>(x)));
}
static T big(T x) {
return kIsLittleEndian ? EndianInt::swap(x) : x;
}
static T little(T x) {
return kIsBigEndian ? EndianInt::swap(x) : x;
}
};
} // namespace detail
// big* convert between native and big-endian representations
// little* convert between native and little-endian representations
// swap* convert between big-endian and little-endian representations
//
// ntohs, htons == big16
// ntohl, htonl == big32
#define FB_GEN1(fn, t, sz) \
static t fn##sz(t x) { \
return fn<t>(x); \
}
#define FB_GEN2(t, sz) \
FB_GEN1(swap, t, sz) \
FB_GEN1(big, t, sz) \
FB_GEN1(little, t, sz)
#define FB_GEN(sz) \
FB_GEN2(uint##sz##_t, sz) \
FB_GEN2(int##sz##_t, sz)
class Endian {
public:
enum class Order : uint8_t {
LITTLE,
BIG,
};
static constexpr Order order = kIsLittleEndian ? Order::LITTLE : Order::BIG;
template <class T>
static T swap(T x) {
return folly::detail::EndianInt<T>::swap(x);
}
template <class T>
static T big(T x) {
return folly::detail::EndianInt<T>::big(x);
}
template <class T>
static T little(T x) {
return folly::detail::EndianInt<T>::little(x);
}
#if !defined(__ANDROID__)
FB_GEN(64)
FB_GEN(32)
FB_GEN(16)
FB_GEN(8)
#endif
};
#undef FB_GEN
#undef FB_GEN2
#undef FB_GEN1
template <class T, class Enable = void>
struct Unaligned;
/**
* Representation of an unaligned value of a POD type.
*/
FOLLY_PACK_PUSH
template <class T>
struct Unaligned<T, typename std::enable_if<std::is_pod<T>::value>::type> {
Unaligned() = default; // uninitialized
/* implicit */ Unaligned(T v) : value(v) {}
T value;
} FOLLY_PACK_ATTR;
FOLLY_PACK_POP
/**
* Read an unaligned value of type T and return it.
*/
template <class T>
inline T loadUnaligned(const void* p) {
static_assert(sizeof(Unaligned<T>) == sizeof(T), "Invalid unaligned size");
static_assert(alignof(Unaligned<T>) == 1, "Invalid alignment");
if (kHasUnalignedAccess) {
return static_cast<const Unaligned<T>*>(p)->value;
} else {
T value;
memcpy(&value, p, sizeof(T));
return value;
}
}
/**
* Read l bytes into the low bits of a value of an unsigned integral
* type T, where l < sizeof(T).
*
* This is intended as a complement to loadUnaligned to read the tail
* of a buffer when it is processed one word at a time.
*/
template <class T>
inline T partialLoadUnaligned(const void* p, size_t l) {
static_assert(
std::is_integral<T>::value && std::is_unsigned<T>::value &&
sizeof(T) <= 8,
"Invalid type");
assume(l < sizeof(T));
auto cp = static_cast<const char*>(p);
T value = 0;
if (!kHasUnalignedAccess || !kIsLittleEndian) {
// Unsupported, use memcpy.
memcpy(&value, cp, l);
return value;
}
auto avail = l;
if (l & 4) {
avail -= 4;
value = static_cast<T>(loadUnaligned<uint32_t>(cp + avail)) << (avail * 8);
}
if (l & 2) {
avail -= 2;
value |= static_cast<T>(loadUnaligned<uint16_t>(cp + avail)) << (avail * 8);
}
if (l & 1) {
value |= loadUnaligned<uint8_t>(cp);
}
return value;
}
/**
* Write an unaligned value of type T.
*/
template <class T>
inline void storeUnaligned(void* p, T value) {
static_assert(sizeof(Unaligned<T>) == sizeof(T), "Invalid unaligned size");
static_assert(alignof(Unaligned<T>) == 1, "Invalid alignment");
if (kHasUnalignedAccess) {
// Prior to C++14, the spec says that a placement new like this
// is required to check that p is not nullptr, and to do nothing
// if p is a nullptr. By assuming it's not a nullptr, we get a
// nice loud segfault in optimized builds if p is nullptr, rather
// than just silently doing nothing.
assume(p != nullptr);
new (p) Unaligned<T>(value);
} else {
memcpy(p, &value, sizeof(T));
}
}
template <typename T>
T bitReverse(T n) {
auto m = static_cast<typename std::make_unsigned<T>::type>(n);
m = ((m & 0xAAAAAAAAAAAAAAAA) >> 1) | ((m & 0x5555555555555555) << 1);
m = ((m & 0xCCCCCCCCCCCCCCCC) >> 2) | ((m & 0x3333333333333333) << 2);
m = ((m & 0xF0F0F0F0F0F0F0F0) >> 4) | ((m & 0x0F0F0F0F0F0F0F0F) << 4);
return static_cast<T>(Endian::swap(m));
}
} // namespace folly