• 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_MEMORY_REGION_H
17 #define LIBPANDABASE_UTILS_BIT_MEMORY_REGION_H
18 
19 #include "globals.h"
20 #include "mem/mem.h"
21 #include "utils/bit_utils.h"
22 #include "utils/span.h"
23 #include "utils/type_helpers.h"
24 
25 namespace panda {
26 
27 template <typename Base = uint8_t>
28 class BitMemoryRegion {
29 public:
30     using ValueType = std::conditional_t<std::is_const_v<Base>, const uint8_t, uint8_t>;
31 
32     BitMemoryRegion() = default;
BitMemoryRegion(Base * data,size_t size)33     BitMemoryRegion(Base *data, size_t size) : BitMemoryRegion(data, 0, size) {}
BitMemoryRegion(Base * data,size_t start,size_t size)34     BitMemoryRegion(Base *data, size_t start, size_t size)
35         : data_(reinterpret_cast<ValueType *>(reinterpret_cast<uintptr_t>(
36               AlignDown(reinterpret_cast<uintptr_t>(data) + (start >> BITS_PER_BYTE_LOG2), alignof(uint64_t))))),
37           start_(start + BITS_PER_BYTE * (reinterpret_cast<ValueType *>(data) - data_)),
38           size_(size)
39     {
40     }
41 
42     template <typename T, typename = typename T::value_type>
BitMemoryRegion(T & data)43     explicit BitMemoryRegion(T &data)
44         : BitMemoryRegion(reinterpret_cast<ValueType *>(data.data()), 0,
45                           data.size() * sizeof(typename T::value_type) * BITS_PER_BYTE)
46     {
47     }
48 
49     class Iterator : public std::iterator<std::forward_iterator_tag, uint32_t, ptrdiff_t, void, uint32_t> {
50     public:
51         static constexpr uint32_t INVAILD_OFFSET = std::numeric_limits<uint32_t>::max();
52 
Iterator(const BitMemoryRegion & region,uint32_t offset)53         Iterator(const BitMemoryRegion &region, uint32_t offset) : region_(region), bit_(offset)
54         {
55             if (bit_ != region_.Size() && !region_.ReadBit(bit_)) {
56                 Next(1);
57             }
58         }
59 
60         Iterator &operator++()
61         {
62             Next(1);
63             return *this;
64         }
65 
66         bool operator==(const Iterator &rhs) const
67         {
68             return bit_ == rhs.bit_;
69         }
70 
71         bool operator!=(const Iterator &rhs) const
72         {
73             return !(*this == rhs);
74         }
75 
76         Iterator operator+(int32_t n) const
77         {
78             ASSERT(bit_ + n < region_.Size());
79             Iterator it(*this);
80             it.Next(n);
81             return it;
82         }
83         Iterator operator-(int32_t n) const
84         {
85             ASSERT(helpers::ToUnsigned(n) <= bit_);
86             Iterator it(*this);
87             it.Next(-n);
88             return it;
89         }
90 
91         uint32_t operator*()
92         {
93             return bit_;
94         }
95 
Next(uint32_t val)96         void Next(uint32_t val)
97         {
98             ASSERT(val != 0);
99             int step = (val > 0) ? 1 : -1;
100             for (; val != 0; val--) {
101                 for (bit_ += step; bit_ > 0 && bit_ != region_.Size() && !region_.ReadBit(bit_); bit_ += step) {
102                 }
103                 if (bit_ == 0 && !region_.ReadBit(bit_)) {
104                     bit_ = region_.Size();
105                 }
106             }
107         }
108 
109         ~Iterator() = default;
110 
111         DEFAULT_COPY_SEMANTIC(Iterator);
112         DEFAULT_NOEXCEPT_MOVE_SEMANTIC(Iterator);
113 
114     private:
115         const BitMemoryRegion &region_;
116         uint32_t bit_ {INVAILD_OFFSET};
117     };
118 
begin()119     Iterator begin() const
120     {
121         return Iterator(*this, 0);
122     }
123 
end()124     Iterator end() const
125     {
126         return Iterator(*this, Size());
127     }
128 
Read(size_t offset)129     bool Read(size_t offset)
130     {
131         ASSERT(offset < size_);
132         size_t index = (start_ + offset) / BITS_PER_BYTE;
133         size_t shift = (start_ + offset) % BITS_PER_BYTE;
134         // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
135         return (data_[index] & (1U << shift)) != 0;
136     }
137 
Write(bool value,size_t offset)138     void Write(bool value, size_t offset)
139     {
140         ASSERT(offset < size_);
141         size_t index = (start_ + offset) / BITS_PER_BYTE;
142         size_t shift = (start_ + offset) % BITS_PER_BYTE;
143         // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
144         data_[index] &= ~(1U << shift);
145         // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
146         data_[index] |= ((value ? 1U : 0U) << shift);
147     }
148 
149     template <typename T = size_t>
150     NO_ADDRESS_SANITIZE  // Suppress asan since we can read extra bytes
151         T
Read(size_t offset,size_t length)152         Read(size_t offset, size_t length) const
153     {
154         static_assert(std::is_integral_v<T>, "T must be integral");
155         static_assert(std::is_unsigned_v<T>, "T must be unsigned");
156 
157         if (length == 0) {
158             return 0;
159         }
160 
161         ASSERT(offset + length <= size_);
162         ASSERT(offset < size_);
163 
164         const T *data = reinterpret_cast<const T *>(data_);
165         size_t width = std::numeric_limits<std::make_unsigned_t<T>>::digits;
166         size_t index = (start_ + offset) / width;
167         size_t shift = (start_ + offset) % width;
168         // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
169         T value = data[index] >> shift;
170         // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
171         T extra = data[index + (shift + (length - 1)) / width];
172         T clear = (std::numeric_limits<T>::max() << 1U) << (length - 1);
173         return (value | (extra << ((width - shift) & (width - 1)))) & ~clear;
174     }
175 
176     template <typename T = size_t>
ReadAll()177     NO_ADDRESS_SANITIZE T ReadAll() const
178     {
179         ASSERT(sizeof(T) * BITS_PER_BYTE >= Size());
180         return Read(0, Size());
181     }
182 
ReadBit(size_t offset)183     bool ReadBit(size_t offset) const
184     {
185         ASSERT(offset < size_);
186         offset += start_;
187         size_t index = offset / BITS_PER_BYTE;
188         // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
189         return (data_[index] & (1U << (offset & (BITS_PER_BYTE - 1)))) != 0;
190     }
191 
192     template <typename T = size_t>
Pop(size_t length)193     T Pop(size_t length)
194     {
195         T res = Read(0, length);
196         start_ += length;
197         return res;
198     }
199 
200     NO_ADDRESS_SANITIZE
Write(uint32_t value,size_t offset,size_t length)201     void Write(uint32_t value, size_t offset, size_t length)
202     {
203         if (length == 0) {
204             return;
205         }
206 
207         ASSERT(offset + length <= size_);
208         ASSERT(offset < size_);
209 
210         uint32_t mask = (std::numeric_limits<uint32_t>::max()) >> ((std::numeric_limits<uint32_t>::digits) - length);
211         size_t index = (start_ + offset) / BITS_PER_BYTE;
212         size_t shift = (start_ + offset) % BITS_PER_BYTE;
213         // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
214         data_[index] &= ~(mask << shift);
215         // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
216         data_[index] |= (value << shift);
217         size_t end_bits = BITS_PER_BYTE - shift;
218         for (int i = 1; end_bits < length; i++, end_bits += BITS_PER_BYTE) {
219             // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
220             data_[index + i] &= ~(mask >> end_bits);
221             // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
222             data_[index + i] |= (value >> end_bits);
223         }
224     }
225 
Subregion(size_t offset,size_t length)226     BitMemoryRegion Subregion(size_t offset, size_t length)
227     {
228         ASSERT(offset <= size_);
229         ASSERT(offset + length <= size_);
230         return BitMemoryRegion(data_, start_ + offset, length);
231     }
232 
Subregion(size_t offset,size_t length)233     BitMemoryRegion Subregion(size_t offset, size_t length) const
234     {
235         ASSERT(offset <= size_);
236         ASSERT(offset + length <= size_);
237         return BitMemoryRegion(data_, start_ + offset, length);
238     }
239 
Size()240     size_t Size() const
241     {
242         return size_;
243     }
244 
Popcount(size_t first,size_t length)245     size_t Popcount(size_t first, size_t length) const
246     {
247         ASSERT(first < Size());
248         ASSERT((first + length) <= Size());
249         size_t res = 0;
250         size_t i = 0;
251         for (; (i + BITS_PER_UINT32) < length; i += BITS_PER_UINT32) {
252             res += panda::Popcount(Read(first + i, BITS_PER_UINT32));
253         }
254         return res + panda::Popcount(Read(first + i, length - i));
255     }
256 
Popcount()257     size_t Popcount() const
258     {
259         return Popcount(0, Size());
260     }
261 
262     void Dump(std::ostream &os) const;
263 
264     virtual ~BitMemoryRegion() = default;
265 
266     DEFAULT_COPY_SEMANTIC(BitMemoryRegion);
267     DEFAULT_NOEXCEPT_MOVE_SEMANTIC(BitMemoryRegion);
268 
269 protected:
Advance(size_t val)270     void Advance(size_t val)
271     {
272         ASSERT(val <= size_);
273         start_ += val;
274         size_ -= val;
275     }
276 
277 private:
278     ValueType *data_ {nullptr};
279     size_t start_ {0};
280     size_t size_ {0};
281 };
282 
283 }  // namespace panda
284 
285 #endif  // LIBPANDABASE_UTILS_BIT_MEMORY_REGION_H
286