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