forked from facebook/hermes
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathBitArray.h
More file actions
171 lines (155 loc) · 6.2 KB
/
BitArray.h
File metadata and controls
171 lines (155 loc) · 6.2 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#ifndef HERMES_ADT_BITARRAY_H
#define HERMES_ADT_BITARRAY_H
#include "llvh/Support/MathExtras.h"
#include <array>
#include <bitset>
namespace hermes {
/// This serves as a replacement for std::bitset that provides fast search and
/// customisable alignment. The storage is fixed size and inline, unlike
/// std::vector<bool> or llvm::BitVector.
/// \tparam N the number of bits to be stored.
/// \tparam A the desired alignment of this structure in bytes.
template <size_t N, size_t A = sizeof(uintptr_t)>
class BitArray {
private:
static_assert(
A && A % sizeof(uintptr_t) == 0,
"Bit set alignment must be a non-zero multiple of word size!");
static constexpr size_t kBitsPerWord = 8 * sizeof(uintptr_t);
static constexpr size_t kNumWords =
llvh::alignTo<kBitsPerWord>(N) / kBitsPerWord;
static constexpr size_t kPaddedWords =
llvh::alignTo<A / sizeof(uintptr_t)>(kNumWords);
std::array<uintptr_t, kPaddedWords> allBits_;
/// Find the first bit with value \p V at or after \p idx.
template <bool V>
size_t findNextBitImpl(size_t idx) const {
assert(
idx <= N &&
"precondition: idx is less than or equal to number of bits");
// If there are bits after N but in the same word, we do not need to
// generate this special case, since we will do a check on idx at the end.
if (N % kBitsPerWord == 0 && idx == N) {
return N;
}
size_t wordIdx = idx / kBitsPerWord;
size_t offset = idx % kBitsPerWord;
// Start looking from the given idx. Invert the word if we are looking for a
// 0 so it's the same as looking for a 1.
uintptr_t currentWord = V ? allBits_[wordIdx] : ~allBits_[wordIdx];
// Set all bits before idx in the word to zero so we skip past them.
currentWord &= (std::numeric_limits<uintptr_t>::max() << offset);
// In the case where we have padding words, they are filled with 1's, so we
// do not need the bounds check on wordIdx when searching for a 1.
constexpr bool noBoundsCheck = kNumWords < kPaddedWords && V;
// Loops through word-sized values in the bit array. Note that at the end of
// this loop, wordIdx points to the element right after currentWord. Writing
// it in this way helps with handling the first and last element edge cases.
++wordIdx;
while (!currentWord && (noBoundsCheck || wordIdx < kNumWords)) {
currentWord = V ? allBits_[wordIdx] : ~allBits_[wordIdx];
++wordIdx;
}
idx = (wordIdx - 1) * kBitsPerWord + llvh::countTrailingZeros(currentWord);
// In the case where N is not a multiple of the word size, this ensures
// that the returned value is equal to N. Otherwise, this check is
// optimised away at compile time.
if (N % kBitsPerWord) {
idx = std::min(idx, N);
}
return idx;
}
/// Find the first bit with value \p V strictly before \p idx.
/// \return an index before \p idx representing a set bit or N if no set bits
/// are found.
template <bool V>
size_t findPrevBitImpl(size_t idx) const {
assert(
idx <= N &&
"precondition: idx is less than or equal to number of bits");
if (!idx) {
return N;
}
// Start the search at idx-1
idx--;
size_t wordIdx = idx / kBitsPerWord;
size_t offset = idx % kBitsPerWord;
// Start looking from the given idx. Invert the word if we are looking for a
// 0 so it's the same as looking for a 1.
uintptr_t currentWord = V ? allBits_[wordIdx] : ~allBits_[wordIdx];
// Set all bits after idx in the word to zero so we skip past them.
currentWord &= llvh::maskTrailingOnes<uintptr_t>(offset + 1);
// Loops through word-sized values in the bit array. Note that at the end of
// this loop, wordIdx points to the element right before currentWord.
// Writing it in this way helps with handling the first and last element
// edge cases.
while (wordIdx-- > 0 && !currentWord) {
currentWord = V ? allBits_[wordIdx] : ~allBits_[wordIdx];
}
// Index is:
// (Index of currentWord) * (Bits per word) + (Index of last 1 in word)
// NOTE: In the case where no bits are found, we expect currentWord to be 0.
// As a result, idx will underflow and evaluate to UINT_MAX.
idx = (wordIdx + 1) * kBitsPerWord +
(kBitsPerWord - llvh::countLeadingZeros(currentWord) - 1);
return std::min(idx, N);
}
public:
BitArray() {
std::fill_n(allBits_.begin(), kNumWords, 0);
// Fill the padding bits with 1's so we can efficiently search for 1's in
// the data.
std::fill_n(
allBits_.begin() + kNumWords,
kPaddedWords - kNumWords,
std::numeric_limits<uintptr_t>::max());
}
/// Set all bits to 1.
inline void set() {
std::fill_n(
allBits_.begin(), kNumWords, std::numeric_limits<uintptr_t>::max());
}
inline void set(size_t idx, bool val) {
const uintptr_t mask = 1ULL << (idx % kBitsPerWord);
const size_t wordIdx = idx / kBitsPerWord;
if (val)
allBits_[wordIdx] |= mask;
else
allBits_[wordIdx] &= ~mask;
}
/// Set all bits to 0.
inline void reset() {
std::fill_n(allBits_.begin(), kNumWords, 0);
}
/// Return a bool representing the value at \p idx.
inline bool at(size_t idx) const {
assert(idx < N && "Index must be within the bitset");
const uintptr_t mask = 1ULL << (idx % kBitsPerWord);
const auto wordIdx = idx / kBitsPerWord;
return allBits_[wordIdx] & mask;
}
/// Find the index of the first set bit at or after \p idx.
size_t findNextSetBitFrom(size_t idx) const {
return findNextBitImpl<true>(idx);
}
/// Find the index of the first unset bit at or after \p idx.
size_t findNextZeroBitFrom(size_t idx) const {
return findNextBitImpl<false>(idx);
}
/// Find the index of the first set bit at or before \p idx.
size_t findPrevSetBitFrom(size_t idx) const {
return findPrevBitImpl<true>(idx);
}
/// Find the index of the first unset bit at or before \p idx.
size_t findPrevZeroBitFrom(size_t idx) const {
return findPrevBitImpl<false>(idx);
}
};
} // namespace hermes
#endif // HERMES_ADT_BITARRAY_H