2 * Copyright 2012 Facebook, Inc.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 #ifndef FOLLY_EXPERIMENTAL_BITS_H_
18 #define FOLLY_EXPERIMENTAL_BITS_H_
21 #include <type_traits>
24 #include "folly/Bits.h"
25 #include "folly/Range.h"
29 // As a general rule, bit operations work on unsigned values only;
30 // right-shift is arithmetic for signed values, and that can lead to
34 * Population count (number of bits set), using __builtin_popcount or
35 * __builtin_popcountll, depending on size.
38 inline typename std::enable_if<
39 (std::is_integral<T>::value &&
40 std::is_unsigned<T>::value &&
41 sizeof(T) <= sizeof(unsigned int)),
44 return __builtin_popcount(x);
48 inline typename std::enable_if<
49 (std::is_integral<T>::value &&
50 std::is_unsigned<T>::value &&
51 sizeof(T) > sizeof(unsigned int) &&
52 sizeof(T) <= sizeof(unsigned long long)),
55 return __builtin_popcountll(x);
61 * Helper class to make Bits<T> (below) work with both aligned values
62 * (T, where T is an unsigned integral type) or unaligned values
63 * (Unaligned<T>, where T is an unsigned integral type)
65 template <class T, class Enable=void> struct BitsTraits;
67 // Partial specialization for Unaligned<T>, where T is unsigned integral
69 struct BitsTraits<Unaligned<T>, typename std::enable_if<
70 (std::is_integral<T>::value && std::is_unsigned<T>::value)>::type> {
71 typedef T UnderlyingType;
72 static T load(const Unaligned<T>& x) { return x.value; }
73 static void store(Unaligned<T>& x, T v) { x.value = v; }
76 // Partial specialization for T, where T is unsigned integral
78 struct BitsTraits<T, typename std::enable_if<
79 (std::is_integral<T>::value && std::is_unsigned<T>::value)>::type> {
80 typedef T UnderlyingType;
81 static T load(const T& x) { return x; }
82 static void store(T& x, T v) { x = v; }
87 * Wrapper class with static methods for various bit-level operations,
88 * treating an array of T as an array of bits (in little-endian order).
89 * (T is either an unsigned integral type or Unaligned<X>, where X is
90 * an unsigned integral type)
92 template <class T, class Traits=detail::BitsTraits<T>>
94 typedef typename Traits::UnderlyingType UnderlyingType;
96 static_assert(sizeof(T) == sizeof(UnderlyingType), "Size mismatch");
99 * Number of bits in a block.
101 static constexpr size_t bitsPerBlock =
102 std::numeric_limits<UnderlyingType>::digits;
105 * Byte index of the given bit.
107 static constexpr size_t blockIndex(size_t bit) {
108 return bit / bitsPerBlock;
112 * Offset in block of the given bit.
114 static constexpr size_t bitOffset(size_t bit) {
115 return bit % bitsPerBlock;
119 * Number of blocks used by the given number of bits.
121 static constexpr size_t blockCount(size_t nbits) {
122 return nbits / bitsPerBlock + (nbits % bitsPerBlock != 0);
128 static void set(T* p, size_t bit);
131 * Clear the given bit.
133 static void clear(T* p, size_t bit);
136 * Test the given bit.
138 static bool test(const T* p, size_t bit);
141 * Set count contiguous bits starting at bitStart to the values
142 * from the least significant count bits of value; little endian.
143 * (value & 1 becomes the bit at bitStart, etc)
144 * Precondition: count <= sizeof(T) * 8
146 static void set(T* p, size_t bitStart, size_t count, UnderlyingType value);
149 * Get count contiguous bits starting at bitStart.
150 * Precondition: count <= sizeof(T) * 8
152 static UnderlyingType get(const T* p, size_t bitStart, size_t count);
155 * Count the number of bits set in a range of blocks.
157 static size_t count(const T* begin, const T* end);
160 // Same as set, assumes all bits are in the same block.
161 // (bitStart < sizeof(T) * 8, bitStart + count <= sizeof(T) * 8)
162 static void innerSet(T* p, size_t bitStart, size_t count,
163 UnderlyingType value);
165 // Same as get, assumes all bits are in the same block.
166 // (bitStart < sizeof(T) * 8, bitStart + count <= sizeof(T) * 8)
167 static UnderlyingType innerGet(const T* p, size_t bitStart, size_t count);
169 static constexpr UnderlyingType one = UnderlyingType(1);
172 template <class T, class Traits>
173 inline void Bits<T, Traits>::set(T* p, size_t bit) {
174 T& block = p[blockIndex(bit)];
175 Traits::store(block, Traits::load(block) | (one << bitOffset(bit)));
178 template <class T, class Traits>
179 inline void Bits<T, Traits>::clear(T* p, size_t bit) {
180 T& block = p[blockIndex(bit)];
181 Traits::store(block, Traits::load(block) & ~(one << bitOffset(bit)));
184 template <class T, class Traits>
185 inline bool Bits<T, Traits>::test(const T* p, size_t bit) {
186 return Traits::load(p[blockIndex(bit)]) & (one << bitOffset(bit));
189 template <class T, class Traits>
190 inline void Bits<T, Traits>::set(T* p, size_t bitStart, size_t count,
191 UnderlyingType value) {
192 assert(count <= sizeof(UnderlyingType) * 8);
193 assert(count == sizeof(UnderlyingType) ||
194 (value & ~((one << count) - 1)) == 0);
195 size_t idx = blockIndex(bitStart);
196 size_t offset = bitOffset(bitStart);
197 if (offset + count <= bitsPerBlock) {
198 innerSet(p + idx, offset, count, value);
200 size_t countInThisBlock = bitsPerBlock - offset;
201 size_t countInNextBlock = count - countInThisBlock;
202 innerSet(p + idx, offset, countInThisBlock,
203 value & ((one << countInThisBlock) - 1));
204 innerSet(p + idx + 1, 0, countInNextBlock, value >> countInThisBlock);
208 template <class T, class Traits>
209 inline auto Bits<T, Traits>::get(const T* p, size_t bitStart, size_t count)
211 assert(count <= sizeof(UnderlyingType) * 8);
212 size_t idx = blockIndex(bitStart);
213 size_t offset = bitOffset(bitStart);
214 if (offset + count <= bitsPerBlock) {
215 return innerGet(p + idx, offset, count);
217 size_t countInThisBlock = bitsPerBlock - offset;
218 size_t countInNextBlock = count - countInThisBlock;
219 UnderlyingType thisBlockValue = innerGet(p + idx, offset, countInThisBlock);
220 UnderlyingType nextBlockValue = innerGet(p + idx + 1, 0, countInNextBlock);
221 return (nextBlockValue << countInThisBlock) | thisBlockValue;
225 template <class T, class Traits>
226 inline void Bits<T, Traits>::innerSet(T* p, size_t offset, size_t count,
227 UnderlyingType value) {
228 // Mask out bits and set new value
229 UnderlyingType v = Traits::load(*p);
230 v &= ~(((one << count) - 1) << offset);
231 v |= (value << offset);
232 Traits::store(*p, v);
235 template <class T, class Traits>
236 inline auto Bits<T, Traits>::innerGet(const T* p, size_t offset, size_t count)
238 return (Traits::load(*p) >> offset) & ((one << count) - 1);
241 template <class T, class Traits>
242 inline size_t Bits<T, Traits>::count(const T* begin, const T* end) {
244 for (; begin != end; ++begin) {
245 n += popcount(Traits::load(*begin));
252 #endif /* FOLLY_EXPERIMENTAL_BITS_H_ */