| 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 | |