1 | //===-- sanitizer_bitvector.h -----------------------------------*- C++ -*-===// |
2 | // |
3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
4 | // See https://llvm.org/LICENSE.txt for license information. |
5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
6 | // |
7 | //===----------------------------------------------------------------------===// |
8 | // |
9 | // Specializer BitVector implementation. |
10 | // |
11 | //===----------------------------------------------------------------------===// |
12 | |
13 | #ifndef SANITIZER_BITVECTOR_H |
14 | #define SANITIZER_BITVECTOR_H |
15 | |
16 | #include "sanitizer_common.h" |
17 | |
18 | namespace __sanitizer { |
19 | |
20 | // Fixed size bit vector based on a single basic integer. |
21 | template <class basic_int_t = uptr> |
22 | class BasicBitVector { |
23 | public: |
24 | enum SizeEnum : uptr { kSize = sizeof(basic_int_t) * 8 }; |
25 | |
26 | uptr size() const { return kSize; } |
27 | // No CTOR. |
28 | void clear() { bits_ = 0; } |
29 | void setAll() { bits_ = ~(basic_int_t)0; } |
30 | bool empty() const { return bits_ == 0; } |
31 | |
32 | // Returns true if the bit has changed from 0 to 1. |
33 | bool setBit(uptr idx) { |
34 | basic_int_t old = bits_; |
35 | bits_ |= mask(idx); |
36 | return bits_ != old; |
37 | } |
38 | |
39 | // Returns true if the bit has changed from 1 to 0. |
40 | bool clearBit(uptr idx) { |
41 | basic_int_t old = bits_; |
42 | bits_ &= ~mask(idx); |
43 | return bits_ != old; |
44 | } |
45 | |
46 | bool getBit(uptr idx) const { return (bits_ & mask(idx)) != 0; } |
47 | |
48 | uptr getAndClearFirstOne() { |
49 | CHECK(!empty()); |
50 | uptr idx = LeastSignificantSetBitIndex(bits_); |
51 | clearBit(idx); |
52 | return idx; |
53 | } |
54 | |
55 | // Do "this |= v" and return whether new bits have been added. |
56 | bool setUnion(const BasicBitVector &v) { |
57 | basic_int_t old = bits_; |
58 | bits_ |= v.bits_; |
59 | return bits_ != old; |
60 | } |
61 | |
62 | // Do "this &= v" and return whether any bits have been removed. |
63 | bool setIntersection(const BasicBitVector &v) { |
64 | basic_int_t old = bits_; |
65 | bits_ &= v.bits_; |
66 | return bits_ != old; |
67 | } |
68 | |
69 | // Do "this &= ~v" and return whether any bits have been removed. |
70 | bool setDifference(const BasicBitVector &v) { |
71 | basic_int_t old = bits_; |
72 | bits_ &= ~v.bits_; |
73 | return bits_ != old; |
74 | } |
75 | |
76 | void copyFrom(const BasicBitVector &v) { bits_ = v.bits_; } |
77 | |
78 | // Returns true if 'this' intersects with 'v'. |
79 | bool intersectsWith(const BasicBitVector &v) const { |
80 | return (bits_ & v.bits_) != 0; |
81 | } |
82 | |
83 | // for (BasicBitVector<>::Iterator it(bv); it.hasNext();) { |
84 | // uptr idx = it.next(); |
85 | // use(idx); |
86 | // } |
87 | class Iterator { |
88 | public: |
89 | Iterator() { } |
90 | explicit Iterator(const BasicBitVector &bv) : bv_(bv) {} |
91 | bool hasNext() const { return !bv_.empty(); } |
92 | uptr next() { return bv_.getAndClearFirstOne(); } |
93 | void clear() { bv_.clear(); } |
94 | private: |
95 | BasicBitVector bv_; |
96 | }; |
97 | |
98 | private: |
99 | basic_int_t mask(uptr idx) const { |
100 | CHECK_LT(idx, size()); |
101 | return (basic_int_t)1UL << idx; |
102 | } |
103 | basic_int_t bits_; |
104 | }; |
105 | |
106 | // Fixed size bit vector of (kLevel1Size*BV::kSize**2) bits. |
107 | // The implementation is optimized for better performance on |
108 | // sparse bit vectors, i.e. the those with few set bits. |
109 | template <uptr kLevel1Size = 1, class BV = BasicBitVector<> > |
110 | class TwoLevelBitVector { |
111 | // This is essentially a 2-level bit vector. |
112 | // Set bit in the first level BV indicates that there are set bits |
113 | // in the corresponding BV of the second level. |
114 | // This structure allows O(kLevel1Size) time for clear() and empty(), |
115 | // as well fast handling of sparse BVs. |
116 | public: |
117 | enum SizeEnum : uptr { kSize = BV::kSize * BV::kSize * kLevel1Size }; |
118 | // No CTOR. |
119 | |
120 | uptr size() const { return kSize; } |
121 | |
122 | void clear() { |
123 | for (uptr i = 0; i < kLevel1Size; i++) |
124 | l1_[i].clear(); |
125 | } |
126 | |
127 | void setAll() { |
128 | for (uptr i0 = 0; i0 < kLevel1Size; i0++) { |
129 | l1_[i0].setAll(); |
130 | for (uptr i1 = 0; i1 < BV::kSize; i1++) |
131 | l2_[i0][i1].setAll(); |
132 | } |
133 | } |
134 | |
135 | bool empty() const { |
136 | for (uptr i = 0; i < kLevel1Size; i++) |
137 | if (!l1_[i].empty()) |
138 | return false; |
139 | return true; |
140 | } |
141 | |
142 | // Returns true if the bit has changed from 0 to 1. |
143 | bool setBit(uptr idx) { |
144 | check(idx); |
145 | uptr i0 = idx0(idx); |
146 | uptr i1 = idx1(idx); |
147 | uptr i2 = idx2(idx); |
148 | if (!l1_[i0].getBit(i1)) { |
149 | l1_[i0].setBit(i1); |
150 | l2_[i0][i1].clear(); |
151 | } |
152 | bool res = l2_[i0][i1].setBit(i2); |
153 | // Printf("%s: %zd => %zd %zd %zd; %d\n", __func__, |
154 | // idx, i0, i1, i2, res); |
155 | return res; |
156 | } |
157 | |
158 | bool clearBit(uptr idx) { |
159 | check(idx); |
160 | uptr i0 = idx0(idx); |
161 | uptr i1 = idx1(idx); |
162 | uptr i2 = idx2(idx); |
163 | bool res = false; |
164 | if (l1_[i0].getBit(i1)) { |
165 | res = l2_[i0][i1].clearBit(i2); |
166 | if (l2_[i0][i1].empty()) |
167 | l1_[i0].clearBit(i1); |
168 | } |
169 | return res; |
170 | } |
171 | |
172 | bool getBit(uptr idx) const { |
173 | check(idx); |
174 | uptr i0 = idx0(idx); |
175 | uptr i1 = idx1(idx); |
176 | uptr i2 = idx2(idx); |
177 | // Printf("%s: %zd => %zd %zd %zd\n", __func__, idx, i0, i1, i2); |
178 | return l1_[i0].getBit(i1) && l2_[i0][i1].getBit(i2); |
179 | } |
180 | |
181 | uptr getAndClearFirstOne() { |
182 | for (uptr i0 = 0; i0 < kLevel1Size; i0++) { |
183 | if (l1_[i0].empty()) continue; |
184 | uptr i1 = l1_[i0].getAndClearFirstOne(); |
185 | uptr i2 = l2_[i0][i1].getAndClearFirstOne(); |
186 | if (!l2_[i0][i1].empty()) |
187 | l1_[i0].setBit(i1); |
188 | uptr res = i0 * BV::kSize * BV::kSize + i1 * BV::kSize + i2; |
189 | // Printf("getAndClearFirstOne: %zd %zd %zd => %zd\n", i0, i1, i2, res); |
190 | return res; |
191 | } |
192 | CHECK(0); |
193 | return 0; |
194 | } |
195 | |
196 | // Do "this |= v" and return whether new bits have been added. |
197 | bool setUnion(const TwoLevelBitVector &v) { |
198 | bool res = false; |
199 | for (uptr i0 = 0; i0 < kLevel1Size; i0++) { |
200 | BV t = v.l1_[i0]; |
201 | while (!t.empty()) { |
202 | uptr i1 = t.getAndClearFirstOne(); |
203 | if (l1_[i0].setBit(i1)) |
204 | l2_[i0][i1].clear(); |
205 | if (l2_[i0][i1].setUnion(v.l2_[i0][i1])) |
206 | res = true; |
207 | } |
208 | } |
209 | return res; |
210 | } |
211 | |
212 | // Do "this &= v" and return whether any bits have been removed. |
213 | bool setIntersection(const TwoLevelBitVector &v) { |
214 | bool res = false; |
215 | for (uptr i0 = 0; i0 < kLevel1Size; i0++) { |
216 | if (l1_[i0].setIntersection(v.l1_[i0])) |
217 | res = true; |
218 | if (!l1_[i0].empty()) { |
219 | BV t = l1_[i0]; |
220 | while (!t.empty()) { |
221 | uptr i1 = t.getAndClearFirstOne(); |
222 | if (l2_[i0][i1].setIntersection(v.l2_[i0][i1])) |
223 | res = true; |
224 | if (l2_[i0][i1].empty()) |
225 | l1_[i0].clearBit(i1); |
226 | } |
227 | } |
228 | } |
229 | return res; |
230 | } |
231 | |
232 | // Do "this &= ~v" and return whether any bits have been removed. |
233 | bool setDifference(const TwoLevelBitVector &v) { |
234 | bool res = false; |
235 | for (uptr i0 = 0; i0 < kLevel1Size; i0++) { |
236 | BV t = l1_[i0]; |
237 | t.setIntersection(v.l1_[i0]); |
238 | while (!t.empty()) { |
239 | uptr i1 = t.getAndClearFirstOne(); |
240 | if (l2_[i0][i1].setDifference(v.l2_[i0][i1])) |
241 | res = true; |
242 | if (l2_[i0][i1].empty()) |
243 | l1_[i0].clearBit(i1); |
244 | } |
245 | } |
246 | return res; |
247 | } |
248 | |
249 | void copyFrom(const TwoLevelBitVector &v) { |
250 | clear(); |
251 | setUnion(v); |
252 | } |
253 | |
254 | // Returns true if 'this' intersects with 'v'. |
255 | bool intersectsWith(const TwoLevelBitVector &v) const { |
256 | for (uptr i0 = 0; i0 < kLevel1Size; i0++) { |
257 | BV t = l1_[i0]; |
258 | t.setIntersection(v.l1_[i0]); |
259 | while (!t.empty()) { |
260 | uptr i1 = t.getAndClearFirstOne(); |
261 | if (!v.l1_[i0].getBit(i1)) continue; |
262 | if (l2_[i0][i1].intersectsWith(v.l2_[i0][i1])) |
263 | return true; |
264 | } |
265 | } |
266 | return false; |
267 | } |
268 | |
269 | // for (TwoLevelBitVector<>::Iterator it(bv); it.hasNext();) { |
270 | // uptr idx = it.next(); |
271 | // use(idx); |
272 | // } |
273 | class Iterator { |
274 | public: |
275 | Iterator() { } |
276 | explicit Iterator(const TwoLevelBitVector &bv) : bv_(bv), i0_(0), i1_(0) { |
277 | it1_.clear(); |
278 | it2_.clear(); |
279 | } |
280 | |
281 | bool hasNext() const { |
282 | if (it1_.hasNext()) return true; |
283 | for (uptr i = i0_; i < kLevel1Size; i++) |
284 | if (!bv_.l1_[i].empty()) return true; |
285 | return false; |
286 | } |
287 | |
288 | uptr next() { |
289 | // Printf("++++: %zd %zd; %d %d; size %zd\n", i0_, i1_, it1_.hasNext(), |
290 | // it2_.hasNext(), kSize); |
291 | if (!it1_.hasNext() && !it2_.hasNext()) { |
292 | for (; i0_ < kLevel1Size; i0_++) { |
293 | if (bv_.l1_[i0_].empty()) continue; |
294 | it1_ = typename BV::Iterator(bv_.l1_[i0_]); |
295 | // Printf("+i0: %zd %zd; %d %d; size %zd\n", i0_, i1_, it1_.hasNext(), |
296 | // it2_.hasNext(), kSize); |
297 | break; |
298 | } |
299 | } |
300 | if (!it2_.hasNext()) { |
301 | CHECK(it1_.hasNext()); |
302 | i1_ = it1_.next(); |
303 | it2_ = typename BV::Iterator(bv_.l2_[i0_][i1_]); |
304 | // Printf("++i1: %zd %zd; %d %d; size %zd\n", i0_, i1_, it1_.hasNext(), |
305 | // it2_.hasNext(), kSize); |
306 | } |
307 | CHECK(it2_.hasNext()); |
308 | uptr i2 = it2_.next(); |
309 | uptr res = i0_ * BV::kSize * BV::kSize + i1_ * BV::kSize + i2; |
310 | // Printf("+ret: %zd %zd; %d %d; size %zd; res: %zd\n", i0_, i1_, |
311 | // it1_.hasNext(), it2_.hasNext(), kSize, res); |
312 | if (!it1_.hasNext() && !it2_.hasNext()) |
313 | i0_++; |
314 | return res; |
315 | } |
316 | |
317 | private: |
318 | const TwoLevelBitVector &bv_; |
319 | uptr i0_, i1_; |
320 | typename BV::Iterator it1_, it2_; |
321 | }; |
322 | |
323 | private: |
324 | void check(uptr idx) const { CHECK_LT(idx, size()); } |
325 | |
326 | uptr idx0(uptr idx) const { |
327 | uptr res = idx / (BV::kSize * BV::kSize); |
328 | CHECK_LT(res, kLevel1Size); |
329 | return res; |
330 | } |
331 | |
332 | uptr idx1(uptr idx) const { |
333 | uptr res = (idx / BV::kSize) % BV::kSize; |
334 | CHECK_LT(res, BV::kSize); |
335 | return res; |
336 | } |
337 | |
338 | uptr idx2(uptr idx) const { |
339 | uptr res = idx % BV::kSize; |
340 | CHECK_LT(res, BV::kSize); |
341 | return res; |
342 | } |
343 | |
344 | BV l1_[kLevel1Size]; |
345 | BV l2_[kLevel1Size][BV::kSize]; |
346 | }; |
347 | |
348 | } // namespace __sanitizer |
349 | |
350 | #endif // SANITIZER_BITVECTOR_H |
351 | |