1 /**
2 * Copyright (c) 2021-2022 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #ifndef LIBPANDABASE_UTILS_BIT_VECTOR_H
17 #define LIBPANDABASE_UTILS_BIT_VECTOR_H
18
19 #include "globals.h"
20 #include "mem/arena_allocator.h"
21 #include "utils/bit_utils.h"
22 #include "utils/small_vector.h"
23 #include "utils/span.h"
24 #include "utils/type_helpers.h"
25
26 #include <vector>
27
28 namespace panda {
29 template <bool IsConst>
30 class BitVectorIterator;
31 } // namespace panda
32
33 namespace std { // NOLINT(cert-dcl58-cpp)
34 template <bool IsConst>
35 inline void fill(panda::BitVectorIterator<IsConst> first, panda::BitVectorIterator<IsConst> last, bool value);
36 } // namespace std
37
38 namespace panda {
39
40 class BitReference final {
41 using WordType = uint32_t;
42
43 public:
BitReference(WordType * data,WordType mask)44 BitReference(WordType *data, WordType mask) : data_(data), mask_(mask)
45 {
46 ASSERT(Popcount(mask) == 1);
47 }
48 BitReference(const BitReference &) = default;
49 BitReference(BitReference &&) = default;
50
51 ~BitReference() = default;
52
53 BitReference &operator=(bool v)
54 {
55 if (v) {
56 *data_ |= mask_;
57 } else {
58 *data_ &= ~mask_;
59 }
60 return *this;
61 }
62 BitReference &operator=(const BitReference &other)
63 {
64 if (&other != this) {
65 *this = bool(other);
66 }
67
68 return *this;
69 }
70 BitReference &operator=(BitReference &&other) noexcept
71 {
72 *this = bool(other);
73 return *this;
74 }
75 // NOLINTNEXTLINE(google-explicit-constructor)
76 operator bool() const
77 {
78 return (*data_ & mask_) != 0;
79 }
80 bool operator==(const BitReference &other) const
81 {
82 return bool(*this) == bool(other);
83 }
84 bool operator<(const BitReference &other) const
85 {
86 return !bool(*this) && bool(other);
87 }
88
89 private:
90 WordType *data_;
91 WordType mask_;
92 };
93
94 template <bool IsConst>
95 class BitVectorIterator : public std::iterator<std::random_access_iterator_tag, bool> {
96 using WordType = std::conditional_t<IsConst, const uint32_t, uint32_t>;
97 using Reference = std::conditional_t<IsConst, bool, BitReference>;
98 using Pointer = std::conditional_t<IsConst, const bool *, BitReference *>;
99
100 static constexpr size_t WORD_BITS = BITS_PER_BYTE * sizeof(WordType);
101
102 public:
BitVectorIterator(WordType * data,int offset)103 BitVectorIterator(WordType *data, int offset) : data_(data), offset_(offset) {}
104 ~BitVectorIterator() = default;
105
106 DEFAULT_COPY_SEMANTIC(BitVectorIterator);
107 DEFAULT_NOEXCEPT_MOVE_SEMANTIC(BitVectorIterator);
108
109 template <bool _IsConst>
110 bool operator==(const BitVectorIterator<_IsConst> &other) const
111 {
112 return data_ == other.data_ && offset_ == other.offset_;
113 }
114 template <bool _IsConst>
115 bool operator<(const BitVectorIterator<_IsConst> &other) const
116 {
117 return data_ < other.data_ || (data_ == other.data_ && offset_ < other.offset_);
118 }
119 template <bool _IsConst>
120 bool operator!=(const BitVectorIterator<_IsConst> &other) const
121 {
122 return !(*this == other);
123 }
124 template <bool _IsConst>
125 bool operator>(const BitVectorIterator<_IsConst> &other) const
126 {
127 return other < *this;
128 }
129 template <bool _IsConst>
130 bool operator<=(const BitVectorIterator<_IsConst> &other) const
131 {
132 return !(other < *this);
133 }
134 template <bool _IsConst>
135 bool operator>=(const BitVectorIterator<_IsConst> &other) const
136 {
137 return !(*this < other);
138 }
139
140 Reference operator*() const
141 {
142 if constexpr (IsConst) { // NOLINT(readability-braces-around-statements)
143 return (*data_ & (1U << helpers::ToUnsigned(offset_))) != 0;
144 } else { // NOLINT(readability-misleading-indentation)
145 ASSERT(offset_ >= 0);
146 return BitReference(data_, 1U << helpers::ToUnsigned(offset_));
147 }
148 }
149 BitVectorIterator &operator++()
150 {
151 BumpUp();
152 return *this;
153 }
154 BitVectorIterator operator++(int) // NOLINT(cert-dcl21-cpp)
155 {
156 BitVectorIterator tmp = *this;
157 BumpUp();
158 return tmp;
159 }
160 BitVectorIterator &operator--()
161 {
162 BumpDown();
163 return *this;
164 }
165 BitVectorIterator operator--(int) // NOLINT(cert-dcl21-cpp)
166 {
167 BitVectorIterator tmp = *this;
168 BumpDown();
169 return tmp;
170 }
171 BitVectorIterator &operator+=(difference_type v)
172 {
173 Increase(v);
174 return *this;
175 }
176 BitVectorIterator &operator-=(difference_type v)
177 {
178 Increase(-v);
179 return *this;
180 }
181 BitVectorIterator operator+(difference_type v) const
182 {
183 auto tmp = *this;
184 return tmp += v;
185 }
186 BitVectorIterator operator-(difference_type v) const
187 {
188 auto tmp = *this;
189 return tmp -= v;
190 }
191 difference_type operator-(BitVectorIterator other) const
192 {
193 return (data_ - other.data_) * WORD_BITS + (offset_ - other.offset_);
194 }
195
196 Reference operator[](difference_type v) const
197 {
198 return *(*this + v);
199 }
200
201 private:
BumpUp()202 void BumpUp()
203 {
204 if (offset_++ == (WORD_BITS - 1)) {
205 offset_ = 0;
206 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
207 ++data_;
208 }
209 }
BumpDown()210 void BumpDown()
211 {
212 if (offset_-- == 0) {
213 offset_ = WORD_BITS - 1;
214 --data_;
215 }
216 }
Increase(ptrdiff_t n)217 void Increase(ptrdiff_t n)
218 {
219 difference_type diff = offset_ + n;
220 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
221 data_ += diff / helpers::ToSigned(WORD_BITS);
222 diff = diff % helpers::ToSigned(WORD_BITS);
223 if (diff < 0) {
224 diff += helpers::ToSigned(WORD_BITS);
225 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
226 --data_;
227 }
228 offset_ = diff;
229 }
230
231 private:
232 WordType *data_;
233 int offset_;
234
235 friend class BitVectorIterator<!IsConst>;
236 template <bool _IsConst>
237 // NOLINTNEXTLINE(readability-redundant-declaration)
238 friend void std::fill(panda::BitVectorIterator<_IsConst> first, panda::BitVectorIterator<_IsConst> last,
239 bool value);
240 };
241
242 /**
243 * BitVector implements std::vector<bool> interface with some extensions:
244 * - ability to get storage data
245 * - iterate over set/zero indices
246 * - set/clear bits with automatic resizing (SetBit/ClearBit)
247 * - ability to operate over existing storage, without expanding (`FixedSize` template parameter)
248 * - several auxiliary methods: Popcount, GetHighestBitSet, etc
249 * @tparam FixedSize whether storage can be expanded, if true - storage has fixed size and cannot be expanded
250 * @tparam Allocator allocator to be used for underlying std::vector
251 */
252 template <bool FixedSize, typename Allocator = StdAllocatorStub>
253 class BitVectorBase final {
254 using WordType = uint32_t;
255 using VectorType = std::conditional_t<FixedSize, Span<WordType>,
256 std::vector<WordType, typename Allocator::template AdapterType<WordType>>>;
257
258 static constexpr size_t WORD_BITS = BITS_PER_BYTE * sizeof(WordType);
259
260 public:
BitVectorBase()261 BitVectorBase() : storage_(Allocator::GetInstance()->Adapter()) {}
262
BitVectorBase(Allocator * allocator)263 explicit BitVectorBase(Allocator *allocator) : storage_(allocator->Adapter())
264 {
265 static_assert(!FixedSize);
266 }
267
BitVectorBase(size_t size,Allocator * allocator)268 BitVectorBase(size_t size, Allocator *allocator)
269 : size_(size), storage_(GetWordIndex(size) + 1, allocator->Adapter())
270 {
271 static_assert(!FixedSize);
272 }
273
BitVectorBase(void * data,size_t bits)274 BitVectorBase(void *data, size_t bits)
275 : size_(bits),
276 storage_(reinterpret_cast<WordType *>(data), GetWordIndex(bits) + (((bits % WORD_BITS) != 0) ? 1 : 0))
277 {
278 static_assert(FixedSize);
279 }
280
BitVectorBase(Span<WordType> span)281 explicit BitVectorBase(Span<WordType> span) : size_(span.size() * WORD_BITS), storage_(span)
282 {
283 static_assert(FixedSize);
284 }
285
286 ~BitVectorBase() = default;
287
288 DEFAULT_COPY_SEMANTIC(BitVectorBase);
289 DEFAULT_MOVE_SEMANTIC(BitVectorBase);
290
291 using value_type = bool;
292 using container_value_type = WordType;
293 using reference = BitReference;
294 using const_reference = bool;
295 using pointer = BitReference *;
296 using const_pointer = const BitReference *;
297 using iterator = BitVectorIterator<false>;
298 using const_iterator = BitVectorIterator<true>;
299 using reverse_iterator = std::reverse_iterator<BitVectorIterator<false>>;
300 using reverse_const_iterator = std::reverse_iterator<BitVectorIterator<true>>;
301 using difference_type = ptrdiff_t;
302
303 template <bool BitValue>
304 class BitIndexIterator;
305
begin()306 const_iterator begin() const
307 {
308 return const_iterator(storage_.data(), 0);
309 }
begin()310 iterator begin()
311 {
312 return iterator(storage_.data(), 0);
313 }
cbegin()314 const_iterator cbegin() const
315 {
316 return const_iterator(storage_.data(), 0);
317 }
end()318 const_iterator end() const
319 {
320 return const_iterator(storage_.data() + GetWordIndex(size_), size_ % WORD_BITS);
321 }
end()322 iterator end()
323 {
324 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
325 return iterator(storage_.data() + GetWordIndex(size_), size_ % WORD_BITS);
326 }
cend()327 const_iterator cend() const
328 {
329 return const_iterator(storage_.data() + GetWordIndex(size_), size_ % WORD_BITS);
330 }
331
332 reference operator[](size_t n)
333 {
334 return *iterator(&storage_[n / WORD_BITS], n % WORD_BITS);
335 }
336
337 const_reference operator[](size_t n) const
338 {
339 return *const_iterator(&storage_[n / WORD_BITS], n % WORD_BITS);
340 }
341
size()342 size_t size() const
343 {
344 return size_;
345 }
346
empty()347 bool empty() const
348 {
349 return size() == 0;
350 }
351
capacity()352 size_t capacity() const
353 {
354 return storage_.capacity() * WORD_BITS;
355 }
356
resize(size_t bits)357 void resize(size_t bits)
358 {
359 static_assert(!FixedSize);
360 auto initial_size = size();
361 if (bits > initial_size) {
362 EnsureSpace(bits);
363 std::fill(begin() + initial_size, begin() + bits, false);
364 }
365 size_ = bits;
366 }
367
clear()368 void clear()
369 {
370 static_assert(!FixedSize);
371 std::fill(begin(), end(), false);
372 size_ = 0;
373 }
374
push_back(bool v)375 void push_back(bool v)
376 {
377 static_assert(!FixedSize);
378 EnsureSpace(size() + 1);
379 (*this)[size()] = v;
380 size_++;
381 }
382
data()383 void *data()
384 {
385 return storage_.data();
386 }
data()387 const void *data() const
388 {
389 return storage_.data();
390 }
391
GetDataSpan()392 Span<uint8_t> GetDataSpan()
393 {
394 auto sp = Span<WordType>(storage_.data(), storage_.size());
395 return sp.SubSpan<uint8_t>(0, storage_.size() * sizeof(WordType));
396 }
397
GetContainerDataSpan()398 Span<uint32_t> GetContainerDataSpan()
399 {
400 return Span<WordType>(storage_.data(), storage_.size());
401 }
402
403 /// Set bit. If size of vector is less than bit, vector will be resized.
SetBit(size_t index)404 void SetBit(size_t index)
405 {
406 EnsureSpace(index + 1);
407 GetWord(index) |= GetBitMask(index);
408 if (index >= size()) {
409 size_ = index + 1;
410 }
411 }
412
413 /// Clear bit. If size of vector is less than bit, vector will be resized.
ClearBit(size_t index)414 void ClearBit(size_t index)
415 {
416 EnsureSpace(index + 1);
417 GetWord(index) &= ~GetBitMask(index);
418 if (index >= size()) {
419 size_ = index + 1;
420 }
421 }
422
GetBit(size_t index)423 bool GetBit(size_t index) const
424 {
425 return (GetWord(index) & GetBitMask(index)) != 0;
426 }
427
PopCount()428 size_t PopCount() const
429 {
430 return PopCount(size());
431 }
432
PopCount(size_t last_index)433 size_t PopCount(size_t last_index) const
434 {
435 if (empty()) {
436 return 0;
437 }
438 size_t res = 0;
439 size_t words = GetWordIndex(last_index);
440 size_t offset = last_index % WORD_BITS;
441 for (size_t i = 0; i < words; i++) {
442 res += Popcount(storage_[i]);
443 }
444 if (offset != 0) {
445 res += Popcount(storage_[words] & ((1U << offset) - 1));
446 }
447 return res;
448 }
449
Reset()450 void Reset()
451 {
452 std::fill(storage_.begin(), storage_.end(), 0);
453 }
454
GetHighestBitSet()455 int GetHighestBitSet() const
456 {
457 for (int i = storage_.size() - 1; i >= 0; i--) {
458 if (storage_[i] != 0) {
459 return (BITS_PER_UINT32 - 1) - Clz(storage_[i]) + (i * WORD_BITS);
460 }
461 }
462 return -1;
463 }
464
GetSizeInBytes()465 size_t GetSizeInBytes() const
466 {
467 return BitsToBytesRoundUp(size());
468 }
469
GetContainerSize()470 size_t GetContainerSize() const
471 {
472 return storage_.size();
473 }
474
GetContainerSizeInBytes()475 size_t GetContainerSizeInBytes() const
476 {
477 return storage_.size() * sizeof(WordType);
478 }
479
GetFixed()480 BitVectorBase<true> GetFixed()
481 {
482 return BitVectorBase<true>(reinterpret_cast<WordType *>(data()), size());
483 }
484
485 template <bool _FixedSize, typename _Allocator>
486 bool operator==(const BitVectorBase<_FixedSize, _Allocator> &other) const
487 {
488 return size_ == other.size() && (std::memcmp(data(), other.data(), BitsToBytesRoundUp(size_)) == 0);
489 }
490
491 template <bool _FixedSize, typename _Allocator>
492 bool operator!=(const BitVectorBase<_FixedSize, _Allocator> &other) const
493 {
494 return !(*this == other);
495 }
496
497 template <bool BitValue>
498 class BitsIndicesRange final {
499 public:
BitsIndicesRange(const BitVectorBase & vector)500 explicit BitsIndicesRange(const BitVectorBase &vector) : vector_(vector) {}
501 ~BitsIndicesRange() = default;
502 NO_COPY_SEMANTIC(BitsIndicesRange);
503 NO_MOVE_SEMANTIC(BitsIndicesRange);
504
begin()505 auto begin()
506 {
507 return BitIndexIterator<BitValue>(vector_, 0);
508 }
end()509 auto end()
510 {
511 return BitIndexIterator<BitValue>(vector_, BitIndexIterator<BitValue>::INVAILD_OFFSET);
512 }
513
514 private:
515 const BitVectorBase &vector_;
516 };
GetSetBitsIndices()517 auto GetSetBitsIndices() const
518 {
519 return BitsIndicesRange<true>(*this);
520 }
GetZeroBitsIndices()521 auto GetZeroBitsIndices() const
522 {
523 return BitsIndicesRange<false>(*this);
524 }
525
526 private:
GetWord(size_t index)527 uint32_t &GetWord(size_t index)
528 {
529 return storage_[GetWordIndex(index)];
530 }
531
GetWord(size_t index)532 uint32_t GetWord(size_t index) const
533 {
534 return storage_[GetWordIndex(index)];
535 }
536
GetWordIndex(size_t index)537 static constexpr size_t GetWordIndex(size_t index)
538 {
539 return index / (WORD_BITS);
540 }
541
GetBitMask(size_t index)542 static constexpr uint32_t GetBitMask(size_t index)
543 {
544 return 1U << (index & (WORD_BITS - 1));
545 }
546
EnsureSpace(size_t bits)547 void EnsureSpace(size_t bits)
548 {
549 static constexpr size_t GROW_MULTIPLIER = 2;
550 size_t words = RoundUp(bits, WORD_BITS) / WORD_BITS;
551 if (words > storage_.size()) {
552 if constexpr (FixedSize) { // NOLINT(readability-braces-around-statements)
553 UNREACHABLE();
554 } else { // NOLINT(readability-misleading-indentation)
555 storage_.resize(std::max(storage_.size() * GROW_MULTIPLIER, words));
556 }
557 }
558 }
559
560 private:
561 size_t size_ {0};
562 VectorType storage_;
563 };
564
565 template <bool IsFixed, typename Allocator>
566 template <bool BitValue>
567 class BitVectorBase<IsFixed, Allocator>::BitIndexIterator {
568 using WordType = uint32_t;
569
570 public:
571 static constexpr int32_t INVAILD_OFFSET = std::numeric_limits<uint32_t>::max();
572
BitIndexIterator(const BitVectorBase & data,int32_t offset)573 BitIndexIterator(const BitVectorBase &data, int32_t offset) : data_(data), offset_(offset)
574 {
575 if (data_.empty()) {
576 offset_ = INVAILD_OFFSET;
577 } else if (offset_ != INVAILD_OFFSET && data_.GetBit(offset_) != BitValue) {
578 Next(1);
579 }
580 }
581
582 ~BitIndexIterator() = default;
583
584 NO_COPY_SEMANTIC(BitIndexIterator);
585 NO_MOVE_SEMANTIC(BitIndexIterator);
586
587 bool operator==(const BitIndexIterator &rhs) const
588 {
589 return data_.data() == rhs.data_.data() && offset_ == rhs.offset_;
590 }
591
592 bool operator!=(const BitIndexIterator &rhs) const
593 {
594 return !(*this == rhs);
595 }
596
597 BitIndexIterator &operator++()
598 {
599 Next(1);
600 return *this;
601 }
602
603 uint32_t operator*()
604 {
605 return offset_;
606 }
607
608 private:
Next(uint32_t val)609 void Next(uint32_t val)
610 {
611 ASSERT(val != 0);
612 int step = (val > 0) ? 1 : -1;
613 for (; val != 0; val--) {
614 if ((offset_ + 1) == helpers::ToSigned(data_.size())) {
615 offset_ = INVAILD_OFFSET;
616 return;
617 }
618 for (offset_ += step; data_.GetBit(offset_) != BitValue; offset_ += step) {
619 if ((offset_ + 1) == helpers::ToSigned(data_.size())) {
620 offset_ = INVAILD_OFFSET;
621 return;
622 }
623 }
624 }
625 }
626
627 private:
628 const BitVectorBase &data_;
629 int32_t offset_;
630 };
631
632 namespace internal {
633 template <bool IsConst>
FillBitVector(panda::BitVectorIterator<IsConst> first,panda::BitVectorIterator<IsConst> last,bool value)634 inline void FillBitVector(panda::BitVectorIterator<IsConst> first, panda::BitVectorIterator<IsConst> last, bool value)
635 {
636 for (; first != last; ++first) {
637 *first = value;
638 }
639 }
640 } // namespace internal
641
642 template <typename Allocator = StdAllocatorStub>
643 using BitVector = BitVectorBase<false, Allocator>;
644 using BitVectorSpan = BitVectorBase<true>;
645 using ArenaBitVector = BitVectorBase<false, ArenaAllocator>;
646 using ArenaBitVectorSpan = BitVectorBase<true, ArenaAllocator>;
647
648 } // namespace panda
649
650 namespace std { // NOLINT(cert-dcl58-cpp)
651
652 template <bool IsConst>
fill(panda::BitVectorIterator<IsConst> first,panda::BitVectorIterator<IsConst> last,bool value)653 inline void fill(panda::BitVectorIterator<IsConst> first, panda::BitVectorIterator<IsConst> last, bool value)
654 {
655 if (first.data_ != last.data_) {
656 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
657 std::fill(first.data_ + 1, last.data_, value ? ~0LLU : 0);
658 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
659 panda::internal::FillBitVector(first, panda::BitVectorIterator<IsConst>(first.data_ + 1, 0), value);
660 panda::internal::FillBitVector(panda::BitVectorIterator<IsConst>(last.data_, 0), last, value);
661 } else {
662 panda::internal::FillBitVector(first, last, value);
663 }
664 }
665
666 } // namespace std
667
668 #endif // LIBPANDABASE_UTILS_BIT_VECTOR_H
669