Bits.h 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327
  1. /*
  2. * Copyright 2012-present Facebook, Inc.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. #pragma once
  17. #include <cstddef>
  18. #include <limits>
  19. #include <type_traits>
  20. #include <glog/logging.h>
  21. #include <folly/Portability.h>
  22. #include <folly/Range.h>
  23. #include <folly/lang/Bits.h>
  24. namespace folly {
  25. template <class T>
  26. struct UnalignedNoASan : public Unaligned<T> {};
  27. // As a general rule, bit operations work on unsigned values only;
  28. // right-shift is arithmetic for signed values, and that can lead to
  29. // unpleasant bugs.
  30. namespace detail {
  31. /**
  32. * Helper class to make Bits<T> (below) work with both aligned values
  33. * (T, where T is an unsigned integral type) or unaligned values
  34. * (Unaligned<T>, where T is an unsigned integral type)
  35. */
  36. template <class T, class Enable = void>
  37. struct BitsTraits;
  38. // Partial specialization for Unaligned<T>, where T is unsigned integral
  39. // loadRMW is the same as load, but it indicates that it loads for a
  40. // read-modify-write operation (we write back the bits we won't change);
  41. // silence the GCC warning in that case.
  42. template <class T>
  43. struct BitsTraits<
  44. Unaligned<T>,
  45. typename std::enable_if<(std::is_integral<T>::value)>::type> {
  46. typedef T UnderlyingType;
  47. static T load(const Unaligned<T>& x) {
  48. return x.value;
  49. }
  50. static void store(Unaligned<T>& x, T v) {
  51. x.value = v;
  52. }
  53. static T loadRMW(const Unaligned<T>& x) {
  54. FOLLY_PUSH_WARNING
  55. FOLLY_GNU_DISABLE_WARNING("-Wuninitialized")
  56. FOLLY_GCC_DISABLE_WARNING("-Wmaybe-uninitialized")
  57. return x.value;
  58. FOLLY_POP_WARNING
  59. }
  60. };
  61. // Special version that allows one to disable address sanitizer on demand.
  62. template <class T>
  63. struct BitsTraits<
  64. UnalignedNoASan<T>,
  65. typename std::enable_if<(std::is_integral<T>::value)>::type> {
  66. typedef T UnderlyingType;
  67. static T FOLLY_DISABLE_ADDRESS_SANITIZER load(const UnalignedNoASan<T>& x) {
  68. return x.value;
  69. }
  70. static void FOLLY_DISABLE_ADDRESS_SANITIZER
  71. store(UnalignedNoASan<T>& x, T v) {
  72. x.value = v;
  73. }
  74. static T FOLLY_DISABLE_ADDRESS_SANITIZER
  75. loadRMW(const UnalignedNoASan<T>& x) {
  76. FOLLY_PUSH_WARNING
  77. FOLLY_GNU_DISABLE_WARNING("-Wuninitialized")
  78. FOLLY_GCC_DISABLE_WARNING("-Wmaybe-uninitialized")
  79. return x.value;
  80. FOLLY_POP_WARNING
  81. }
  82. };
  83. // Partial specialization for T, where T is unsigned integral
  84. template <class T>
  85. struct BitsTraits<
  86. T,
  87. typename std::enable_if<(std::is_integral<T>::value)>::type> {
  88. typedef T UnderlyingType;
  89. static T load(const T& x) {
  90. return x;
  91. }
  92. static void store(T& x, T v) {
  93. x = v;
  94. }
  95. static T loadRMW(const T& x) {
  96. FOLLY_PUSH_WARNING
  97. FOLLY_GNU_DISABLE_WARNING("-Wuninitialized")
  98. FOLLY_GCC_DISABLE_WARNING("-Wmaybe-uninitialized")
  99. return x;
  100. FOLLY_POP_WARNING
  101. }
  102. };
  103. } // namespace detail
  104. /**
  105. * Wrapper class with static methods for various bit-level operations,
  106. * treating an array of T as an array of bits (in little-endian order).
  107. * (T is either an unsigned integral type or Unaligned<X>, where X is
  108. * an unsigned integral type)
  109. */
  110. template <class T, class Traits = detail::BitsTraits<T>>
  111. struct Bits {
  112. typedef typename Traits::UnderlyingType UnderlyingType;
  113. typedef T type;
  114. static_assert(sizeof(T) == sizeof(UnderlyingType), "Size mismatch");
  115. /**
  116. * Number of bits in a block.
  117. */
  118. static constexpr size_t bitsPerBlock = std::numeric_limits<
  119. typename std::make_unsigned<UnderlyingType>::type>::digits;
  120. /**
  121. * Byte index of the given bit.
  122. */
  123. static constexpr size_t blockIndex(size_t bit) {
  124. return bit / bitsPerBlock;
  125. }
  126. /**
  127. * Offset in block of the given bit.
  128. */
  129. static constexpr size_t bitOffset(size_t bit) {
  130. return bit % bitsPerBlock;
  131. }
  132. /**
  133. * Number of blocks used by the given number of bits.
  134. */
  135. static constexpr size_t blockCount(size_t nbits) {
  136. return nbits / bitsPerBlock + (nbits % bitsPerBlock != 0);
  137. }
  138. /**
  139. * Set the given bit.
  140. */
  141. static void set(T* p, size_t bit);
  142. /**
  143. * Clear the given bit.
  144. */
  145. static void clear(T* p, size_t bit);
  146. /**
  147. * Test the given bit.
  148. */
  149. static bool test(const T* p, size_t bit);
  150. /**
  151. * Set count contiguous bits starting at bitStart to the values
  152. * from the least significant count bits of value; little endian.
  153. * (value & 1 becomes the bit at bitStart, etc)
  154. * Precondition: count <= sizeof(T) * 8
  155. * Precondition: value can fit in 'count' bits
  156. */
  157. static void set(T* p, size_t bitStart, size_t count, UnderlyingType value);
  158. /**
  159. * Get count contiguous bits starting at bitStart.
  160. * Precondition: count <= sizeof(T) * 8
  161. */
  162. static UnderlyingType get(const T* p, size_t bitStart, size_t count);
  163. /**
  164. * Count the number of bits set in a range of blocks.
  165. */
  166. static size_t count(const T* begin, const T* end);
  167. private:
  168. // Same as set, assumes all bits are in the same block.
  169. // (bitStart < sizeof(T) * 8, bitStart + count <= sizeof(T) * 8)
  170. static void
  171. innerSet(T* p, size_t bitStart, size_t count, UnderlyingType value);
  172. // Same as get, assumes all bits are in the same block.
  173. // (bitStart < sizeof(T) * 8, bitStart + count <= sizeof(T) * 8)
  174. static UnderlyingType innerGet(const T* p, size_t bitStart, size_t count);
  175. static constexpr UnderlyingType zero = UnderlyingType(0);
  176. static constexpr UnderlyingType one = UnderlyingType(1);
  177. using UnsignedType = typename std::make_unsigned<UnderlyingType>::type;
  178. static constexpr UnderlyingType ones(size_t count) {
  179. return (count < bitsPerBlock)
  180. ? static_cast<UnderlyingType>((UnsignedType{1} << count) - 1)
  181. : ~zero;
  182. }
  183. };
  184. // gcc 4.8 needs more -Wmaybe-uninitialized tickling, as it propagates the
  185. // taint upstream from loadRMW
  186. FOLLY_PUSH_WARNING
  187. FOLLY_GNU_DISABLE_WARNING("-Wuninitialized")
  188. FOLLY_GCC_DISABLE_WARNING("-Wmaybe-uninitialized")
  189. template <class T, class Traits>
  190. inline void Bits<T, Traits>::set(T* p, size_t bit) {
  191. T& block = p[blockIndex(bit)];
  192. Traits::store(block, Traits::loadRMW(block) | (one << bitOffset(bit)));
  193. }
  194. template <class T, class Traits>
  195. inline void Bits<T, Traits>::clear(T* p, size_t bit) {
  196. T& block = p[blockIndex(bit)];
  197. Traits::store(block, Traits::loadRMW(block) & ~(one << bitOffset(bit)));
  198. }
  199. template <class T, class Traits>
  200. inline void Bits<T, Traits>::set(
  201. T* p,
  202. size_t bitStart,
  203. size_t count,
  204. UnderlyingType value) {
  205. DCHECK_LE(count, sizeof(UnderlyingType) * 8);
  206. size_t cut = bitsPerBlock - count;
  207. if (cut != 8 * sizeof(UnderlyingType)) {
  208. using U = typename std::make_unsigned<UnderlyingType>::type;
  209. DCHECK_EQ(value, UnderlyingType(U(value) << cut) >> cut);
  210. }
  211. size_t idx = blockIndex(bitStart);
  212. size_t offset = bitOffset(bitStart);
  213. if (std::is_signed<UnderlyingType>::value) {
  214. value &= ones(count);
  215. }
  216. if (offset + count <= bitsPerBlock) {
  217. innerSet(p + idx, offset, count, value);
  218. } else {
  219. size_t countInThisBlock = bitsPerBlock - offset;
  220. size_t countInNextBlock = count - countInThisBlock;
  221. UnderlyingType thisBlock = UnderlyingType(value & ones(countInThisBlock));
  222. UnderlyingType nextBlock = UnderlyingType(value >> countInThisBlock);
  223. if (std::is_signed<UnderlyingType>::value) {
  224. nextBlock &= ones(countInNextBlock);
  225. }
  226. innerSet(p + idx, offset, countInThisBlock, thisBlock);
  227. innerSet(p + idx + 1, 0, countInNextBlock, nextBlock);
  228. }
  229. }
  230. template <class T, class Traits>
  231. inline void Bits<T, Traits>::innerSet(
  232. T* p,
  233. size_t offset,
  234. size_t count,
  235. UnderlyingType value) {
  236. // Mask out bits and set new value
  237. UnderlyingType v = Traits::loadRMW(*p);
  238. v &= ~(ones(count) << offset);
  239. v |= (value << offset);
  240. Traits::store(*p, v);
  241. }
  242. FOLLY_POP_WARNING
  243. template <class T, class Traits>
  244. inline bool Bits<T, Traits>::test(const T* p, size_t bit) {
  245. return Traits::load(p[blockIndex(bit)]) & (one << bitOffset(bit));
  246. }
  247. template <class T, class Traits>
  248. inline auto Bits<T, Traits>::get(const T* p, size_t bitStart, size_t count)
  249. -> UnderlyingType {
  250. if (count == 0) {
  251. return UnderlyingType{};
  252. }
  253. DCHECK_LE(count, sizeof(UnderlyingType) * 8);
  254. size_t idx = blockIndex(bitStart);
  255. size_t offset = bitOffset(bitStart);
  256. UnderlyingType ret;
  257. if (offset + count <= bitsPerBlock) {
  258. ret = innerGet(p + idx, offset, count);
  259. } else {
  260. size_t countInThisBlock = bitsPerBlock - offset;
  261. size_t countInNextBlock = count - countInThisBlock;
  262. UnderlyingType thisBlockValue = innerGet(p + idx, offset, countInThisBlock);
  263. UnderlyingType nextBlockValue = innerGet(p + idx + 1, 0, countInNextBlock);
  264. ret = (nextBlockValue << countInThisBlock) | thisBlockValue;
  265. }
  266. if (std::is_signed<UnderlyingType>::value) {
  267. size_t emptyBits = bitsPerBlock - count;
  268. ret <<= emptyBits;
  269. ret >>= emptyBits;
  270. }
  271. return ret;
  272. }
  273. template <class T, class Traits>
  274. inline auto Bits<T, Traits>::innerGet(const T* p, size_t offset, size_t count)
  275. -> UnderlyingType {
  276. return (Traits::load(*p) >> offset) & ones(count);
  277. }
  278. template <class T, class Traits>
  279. inline size_t Bits<T, Traits>::count(const T* begin, const T* end) {
  280. size_t n = 0;
  281. for (; begin != end; ++begin) {
  282. n += popcount(Traits::load(*begin));
  283. }
  284. return n;
  285. }
  286. } // namespace folly