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 ®ion, 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 ®ion_; 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