• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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