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