X-Git-Url: http://plrg.eecs.uci.edu/git/?a=blobdiff_plain;f=folly%2FBits.h;h=a3cff3ea962f66f3c1ad78d08032833b42a1bb13;hb=d5b67c256c7423d74f4794d7fe421ed239807be4;hp=9795f8f5650f1337d0509c48769dceb1b37fe8c0;hpb=8ff6fe9e5593c1a5a0de24577f1e423fb6eae503;p=folly.git diff --git a/folly/Bits.h b/folly/Bits.h index 9795f8f5..a3cff3ea 100644 --- a/folly/Bits.h +++ b/folly/Bits.h @@ -1,5 +1,5 @@ /* - * Copyright 2012 Facebook, Inc. + * Copyright 2017 Facebook, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,538 +14,4 @@ * limitations under the License. */ -/** - * Various low-level, bit-manipulation routines. - * - * findFirstSet(x) - * 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) - * 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, findFirstSet(x) == 1 + floor(log2(x)) - * - * nextPowTwo(x) - * Finds the next power of two >= 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 - * - * BitIterator - * Wrapper around an iterator over an integral type that iterates - * over its underlying bits in MSb to LSb order - * - * findFirstSet(BitIterator begin, BitIterator end) - * return a BitIterator pointing to the first 1 bit in [begin, end), or - * end if all bits in [begin, end) are 0 - * - * @author Tudor Bosman (tudorb@fb.com) - */ - -#ifndef FOLLY_BITS_H_ -#define FOLLY_BITS_H_ - -#include "folly/Portability.h" - -#ifndef _GNU_SOURCE -#define _GNU_SOURCE 1 -#endif - -#include "folly/detail/BitIteratorDetail.h" -#include "folly/Likely.h" - -#include -#include -#include -#include // for ffs, ffsl, ffsll -#include -#include -#include -#include -#include -#include - -namespace folly { - -// Generate overloads for findFirstSet as wrappers around -// appropriate ffs, ffsl, ffsll functions from glibc. -// We first define these overloads for signed types (because ffs, ffsl, ffsll -// take int, long, and long long as arguments, respectively) and then -// define an overload for unsigned that forwards to the overload for the -// corresponding signed type. -template -typename std::enable_if< - (std::is_integral::value && - std::is_signed::value && - (std::numeric_limits::digits <= std::numeric_limits::digits)), - unsigned int>::type - findFirstSet(T x) { - return ::ffs(static_cast(x)); -} - -template -typename std::enable_if< - (std::is_integral::value && - std::is_signed::value && - (std::numeric_limits::digits > std::numeric_limits::digits) && - (std::numeric_limits::digits <= std::numeric_limits::digits)), - unsigned int>::type - findFirstSet(T x) { - return ::ffsl(static_cast(x)); -} - -#ifdef FOLLY_HAVE_FFSLL - -template -typename std::enable_if< - (std::is_integral::value && - std::is_signed::value && - (std::numeric_limits::digits > std::numeric_limits::digits) && - (std::numeric_limits::digits <= std::numeric_limits::digits)), - unsigned int>::type - findFirstSet(T x) { - return ::ffsll(static_cast(x)); -} - -#endif - -template -typename std::enable_if< - (std::is_integral::value && - !std::is_signed::value), - unsigned int>::type - findFirstSet(T x) { - // Note that conversion from an unsigned type to the corresponding signed - // type is technically implementation-defined, but will likely work - // on any impementation that uses two's complement. - return findFirstSet(static_cast::type>(x)); -} - -namespace detail { - -// Portable, but likely slow... -inline unsigned int findLastSetPortable(uint64_t x) { - unsigned int r = (x != 0); // 1-based index, except for x==0 - while (x >>= 1) { - ++r; - } - return r; -} - -} // namespace detail - -#ifdef __GNUC__ - -// findLastSet: return the 1-based index of the highest bit set -// for x > 0, findLastSet(x) == 1 + floor(log2(x)) -template -typename std::enable_if< - (std::is_integral::value && - std::is_unsigned::value && - (std::numeric_limits::digits <= - std::numeric_limits::digits)), - unsigned int>::type - findLastSet(T x) { - return x ? 8 * sizeof(unsigned int) - __builtin_clz(x) : 0; -} - -template -typename std::enable_if< - (std::is_integral::value && - std::is_unsigned::value && - (std::numeric_limits::digits > - std::numeric_limits::digits) && - (std::numeric_limits::digits <= - std::numeric_limits::digits)), - unsigned int>::type - findLastSet(T x) { - return x ? 8 * sizeof(unsigned long) - __builtin_clzl(x) : 0; -} - -template -typename std::enable_if< - (std::is_integral::value && - std::is_unsigned::value && - (std::numeric_limits::digits > - std::numeric_limits::digits) && - (std::numeric_limits::digits <= - std::numeric_limits::digits)), - unsigned int>::type - findLastSet(T x) { - return x ? 8 * sizeof(unsigned long long) - __builtin_clzll(x) : 0; -} - -#else /* !__GNUC__ */ - -template -typename std::enable_if< - (std::is_integral::value && - std::is_unsigned::value), - unsigned int>::type - findLastSet(T x) { - return detail:findLastSetPortable(x); -} - -#endif - -template -typename std::enable_if< - (std::is_integral::value && - std::is_signed::value), - unsigned int>::type - findLastSet(T x) { - return findLastSet(static_cast::type>(x)); -} - -namespace detail { - -template -inline -typename std::enable_if< - std::is_integral::value && std::is_unsigned::value, - T>::type -nextPowTwoPortable(T v) { - if (UNLIKELY(v == 0)) { - return 1; - } - - --v; - for (uint32_t i = 1; i < sizeof(T) * 8; i <<= 8) { - v |= (v >> i); - v |= (v >> (i << 1)); - v |= (v >> (i << 2)); - v |= (v >> (i << 3)); - v |= (v >> (i << 4)); - v |= (v >> (i << 5)); - v |= (v >> (i << 6)); - v |= (v >> (i << 7)); - } - return v + 1; -} - -} // namespace detail - -#ifdef __GNUC__ - -template -inline -typename std::enable_if< - std::is_integral::value && std::is_unsigned::value, - T>::type -nextPowTwo(T v) { - if (UNLIKELY(v == 0)) { - return 1; - } - return 1ul << findLastSet(v - 1); -} - -#else /* __GNUC__ */ - -template -inline -typename std::enable_if< - std::is_integral::value && std::is_unsigned::value, - T>::type -nextPowTwo(T v) { - return detail::nextPowTwoPortable(v); -} - -#endif /* __GNUC__ */ - - - -/** - * Endianness detection and manipulation primitives. - */ -namespace detail { - -template -struct EndianIntBase { - public: - static T swap(T x); -}; - -#define FB_GEN(t, fn) \ -template<> inline t EndianIntBase::swap(t x) { return fn(x); } - -// fn(x) expands to (x) if the second argument is empty, which is exactly -// what we want for [u]int8_t -FB_GEN( int8_t,) -FB_GEN(uint8_t,) -FB_GEN( int64_t, bswap_64) -FB_GEN(uint64_t, bswap_64) -FB_GEN( int32_t, bswap_32) -FB_GEN(uint32_t, bswap_32) -FB_GEN( int16_t, bswap_16) -FB_GEN(uint16_t, bswap_16) - -#undef FB_GEN - -#if __BYTE_ORDER == __LITTLE_ENDIAN - -template -struct EndianInt : public detail::EndianIntBase { - public: - static T big(T x) { return EndianInt::swap(x); } - static T little(T x) { return x; } -}; - -#elif __BYTE_ORDER == __BIG_ENDIAN - -template -struct EndianInt : public detail::EndianIntBase { - public: - static T big(T x) { return x; } - static T little(T x) { return EndianInt::swap(x); } -}; - -#else -# error Your machine uses a weird endianness! -#endif /* __BYTE_ORDER */ - -} // 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(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: - template static T swap(T x) { - return detail::EndianInt::swap(x); - } - template static T big(T x) { - return detail::EndianInt::big(x); - } - template static T little(T x) { - return detail::EndianInt::little(x); - } - - FB_GEN(64) - FB_GEN(32) - FB_GEN(16) - FB_GEN(8) -}; - -#undef FB_GEN -#undef FB_GEN2 -#undef FB_GEN1 - -/** - * Fast bit iteration facility. - */ - - -template class BitIterator; -template -BitIterator findFirstSet(BitIterator, - BitIterator); -/** - * Wrapper around an iterator over an integer type that iterates - * over its underlying bits in LSb to MSb order. - * - * BitIterator models the same iterator concepts as the base iterator. - */ -template -class BitIterator - : public bititerator_detail::BitIteratorBase::type { - public: - /** - * Return the number of bits in an element of the underlying iterator. - */ - static size_t bitsPerBlock() { - return std::numeric_limits< - typename std::make_unsigned< - typename std::iterator_traits::value_type - >::type - >::digits; - } - - /** - * Construct a BitIterator that points at a given bit offset (default 0) - * in iter. - */ - explicit BitIterator(const BaseIter& iter, size_t bitOffset=0) - : bititerator_detail::BitIteratorBase::type(iter), - bitOffset_(bitOffset) { - assert(bitOffset_ < bitsPerBlock()); - } - - size_t bitOffset() const { - return bitOffset_; - } - - void advanceToNextBlock() { - bitOffset_ = 0; - ++this->base_reference(); - } - - BitIterator& operator=(const BaseIter& other) { - this->~BitIterator(); - new (this) BitIterator(other); - return *this; - } - - private: - friend class boost::iterator_core_access; - friend BitIterator findFirstSet<>(BitIterator, BitIterator); - - typedef bititerator_detail::BitReference< - typename std::iterator_traits::reference, - typename std::iterator_traits::value_type - > BitRef; - - void advanceInBlock(size_t n) { - bitOffset_ += n; - assert(bitOffset_ < bitsPerBlock()); - } - - BitRef dereference() const { - return BitRef(*this->base_reference(), bitOffset_); - } - - void advance(ssize_t n) { - size_t bpb = bitsPerBlock(); - ssize_t blocks = n / bpb; - bitOffset_ += n % bpb; - if (bitOffset_ >= bpb) { - bitOffset_ -= bpb; - ++blocks; - } - this->base_reference() += blocks; - } - - void increment() { - if (++bitOffset_ == bitsPerBlock()) { - advanceToNextBlock(); - } - } - - void decrement() { - if (bitOffset_-- == 0) { - bitOffset_ = bitsPerBlock() - 1; - --this->base_reference(); - } - } - - bool equal(const BitIterator& other) const { - return (bitOffset_ == other.bitOffset_ && - this->base_reference() == other.base_reference()); - } - - ssize_t distance_to(const BitIterator& other) const { - return - (other.base_reference() - this->base_reference()) * bitsPerBlock() + - (other.bitOffset_ - bitOffset_); - } - - ssize_t bitOffset_; -}; - -/** - * Helper function, so you can write - * auto bi = makeBitIterator(container.begin()); - */ -template -BitIterator makeBitIterator(const BaseIter& iter) { - return BitIterator(iter); -} - - -/** - * Find first bit set in a range of bit iterators. - * 4.5x faster than the obvious std::find(begin, end, true); - */ -template -BitIterator findFirstSet(BitIterator begin, - BitIterator end) { - // shortcut to avoid ugly static_cast<> - static const typename BaseIter::value_type one = 1; - - while (begin.base() != end.base()) { - typename BaseIter::value_type v = *begin.base(); - // mask out the bits that don't matter (< begin.bitOffset) - v &= ~((one << begin.bitOffset()) - 1); - size_t firstSet = findFirstSet(v); - if (firstSet) { - --firstSet; // now it's 0-based - assert(firstSet >= begin.bitOffset()); - begin.advanceInBlock(firstSet - begin.bitOffset()); - return begin; - } - begin.advanceToNextBlock(); - } - - // now begin points to the same block as end - if (end.bitOffset() != 0) { // assume end is dereferenceable - typename BaseIter::value_type v = *begin.base(); - // mask out the bits that don't matter (< begin.bitOffset) - v &= ~((one << begin.bitOffset()) - 1); - // mask out the bits that don't matter (>= end.bitOffset) - v &= (one << end.bitOffset()) - 1; - size_t firstSet = findFirstSet(v); - if (firstSet) { - --firstSet; // now it's 0-based - assert(firstSet >= begin.bitOffset()); - begin.advanceInBlock(firstSet - begin.bitOffset()); - return begin; - } - } - - return end; -} - - -namespace detail { - -template struct Unaligned; - -template -struct Unaligned< - T, - typename std::enable_if::value>::type> { - T value; -} __attribute__((packed)); - -} // namespace detail - -/** - * Read an unaligned value of type T and return it. - */ -template -inline T loadUnaligned(const void* p) { - static_assert(alignof(detail::Unaligned) == 1, "Invalid alignment"); - return static_cast*>(p)->value; -} - -/** - * Write an unaligned value of type T. - */ -template -inline void storeUnaligned(void* p, T value) { - static_assert(alignof(detail::Unaligned) == 1, "Invalid alignment"); - static_cast*>(p)->value = value; -} - -} // namespace folly - -#endif /* FOLLY_BITS_H_ */ - +#include // @shim