1 /* 2 * Copyright (C) 2018 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 #ifndef ART_LIBARTBASE_BASE_BIT_TABLE_H_ 18 #define ART_LIBARTBASE_BASE_BIT_TABLE_H_ 19 20 #include <array> 21 #include <initializer_list> 22 #include <numeric> 23 #include <string.h> 24 #include <type_traits> 25 #include <unordered_map> 26 27 #include "base/bit_memory_region.h" 28 #include "base/casts.h" 29 #include "base/iteration_range.h" 30 #include "base/memory_region.h" 31 #include "base/scoped_arena_containers.h" 32 #include "base/stl_util.h" 33 34 namespace art { 35 36 // Generic purpose table of uint32_t values, which are tightly packed at bit level. 37 // It has its own header with the number of rows and the bit-widths of all columns. 38 // The values are accessible by (row, column). The value -1 is stored efficiently. 39 template<uint32_t kNumColumns> 40 class BitTableBase { 41 public: 42 static constexpr uint32_t kNoValue = std::numeric_limits<uint32_t>::max(); // == -1. 43 static constexpr uint32_t kValueBias = kNoValue; // Bias so that -1 is encoded as 0. 44 45 BitTableBase() {} 46 explicit BitTableBase(BitMemoryReader& reader) { 47 Decode(reader); 48 } 49 50 ALWAYS_INLINE void Decode(BitMemoryReader& reader) { 51 // Decode row count and column sizes from the table header. 52 num_rows_ = reader.ReadVarint(); 53 if (num_rows_ != 0) { 54 column_offset_[0] = 0; 55 for (uint32_t i = 0; i < kNumColumns; i++) { 56 size_t column_end = column_offset_[i] + reader.ReadVarint(); 57 column_offset_[i + 1] = dchecked_integral_cast<uint16_t>(column_end); 58 } 59 } 60 61 // Record the region which contains the table data and skip past it. 62 table_data_ = reader.ReadRegion(num_rows_ * NumRowBits()); 63 } 64 65 ALWAYS_INLINE uint32_t Get(uint32_t row, uint32_t column = 0) const { 66 DCHECK(table_data_.IsValid()) << "Table has not been loaded"; 67 DCHECK_LT(row, num_rows_); 68 DCHECK_LT(column, kNumColumns); 69 size_t offset = row * NumRowBits() + column_offset_[column]; 70 return table_data_.LoadBits(offset, NumColumnBits(column)) + kValueBias; 71 } 72 73 ALWAYS_INLINE BitMemoryRegion GetBitMemoryRegion(uint32_t row, uint32_t column = 0) const { 74 DCHECK(table_data_.IsValid()) << "Table has not been loaded"; 75 DCHECK_LT(row, num_rows_); 76 DCHECK_LT(column, kNumColumns); 77 size_t offset = row * NumRowBits() + column_offset_[column]; 78 return table_data_.Subregion(offset, NumColumnBits(column)); 79 } 80 81 size_t NumRows() const { return num_rows_; } 82 83 uint32_t NumRowBits() const { return column_offset_[kNumColumns]; } 84 85 constexpr size_t NumColumns() const { return kNumColumns; } 86 87 uint32_t NumColumnBits(uint32_t column) const { 88 return column_offset_[column + 1] - column_offset_[column]; 89 } 90 91 size_t DataBitSize() const { return table_data_.size_in_bits(); } 92 93 bool Equals(const BitTableBase& other) const { 94 return num_rows_ == other.num_rows_ && 95 std::equal(column_offset_, column_offset_ + kNumColumns, other.column_offset_) && 96 BitMemoryRegion::Compare(table_data_, other.table_data_) == 0; 97 } 98 99 protected: 100 BitMemoryRegion table_data_; 101 size_t num_rows_ = 0; 102 uint16_t column_offset_[kNumColumns + 1] = {}; 103 }; 104 105 // Helper class which can be used to create BitTable accessors with named getters. 106 template<uint32_t NumColumns> 107 class BitTableAccessor { 108 public: 109 static constexpr uint32_t kNumColumns = NumColumns; 110 static constexpr uint32_t kNoValue = BitTableBase<kNumColumns>::kNoValue; 111 112 BitTableAccessor(const BitTableBase<kNumColumns>* table, uint32_t row) 113 : table_(table), row_(row) { 114 DCHECK(table_ != nullptr); 115 } 116 117 ALWAYS_INLINE uint32_t Row() const { return row_; } 118 119 ALWAYS_INLINE bool IsValid() const { return row_ < table_->NumRows(); } 120 121 ALWAYS_INLINE bool Equals(const BitTableAccessor& other) { 122 return this->table_ == other.table_ && this->row_ == other.row_; 123 } 124 125 // Helper macro to create constructors and per-table utilities in derived class. 126 #define BIT_TABLE_HEADER(NAME) \ 127 using BitTableAccessor<kNumColumns>::BitTableAccessor; /* inherit constructors */ \ 128 template<int COLUMN, int UNUSED /*needed to compile*/> struct ColumnName; \ 129 static constexpr const char* kTableName = #NAME; \ 130 131 // Helper macro to create named column accessors in derived class. 132 #define BIT_TABLE_COLUMN(COLUMN, NAME) \ 133 static constexpr uint32_t k##NAME = COLUMN; \ 134 ALWAYS_INLINE uint32_t Get##NAME() const { return table_->Get(row_, COLUMN); } \ 135 ALWAYS_INLINE bool Has##NAME() const { return Get##NAME() != kNoValue; } \ 136 template<int UNUSED> struct ColumnName<COLUMN, UNUSED> { \ 137 static constexpr const char* Value = #NAME; \ 138 }; \ 139 140 protected: 141 const BitTableBase<kNumColumns>* table_ = nullptr; 142 uint32_t row_ = -1; 143 }; 144 145 // Template meta-programming helper. 146 template<typename Accessor, size_t... Columns> 147 static const char* const* GetBitTableColumnNamesImpl(std::index_sequence<Columns...>) { 148 static const char* names[] = { Accessor::template ColumnName<Columns, 0>::Value... }; 149 return names; 150 } 151 152 // Wrapper which makes it easier to use named accessors for the individual rows. 153 template<typename Accessor> 154 class BitTable : public BitTableBase<Accessor::kNumColumns> { 155 public: 156 class const_iterator : public std::iterator<std::random_access_iterator_tag, 157 /* value_type */ Accessor, 158 /* difference_type */ int32_t, 159 /* pointer */ void, 160 /* reference */ void> { 161 public: 162 using difference_type = int32_t; 163 const_iterator() {} 164 const_iterator(const BitTable* table, uint32_t row) : table_(table), row_(row) {} 165 const_iterator operator+(difference_type n) { return const_iterator(table_, row_ + n); } 166 const_iterator operator-(difference_type n) { return const_iterator(table_, row_ - n); } 167 difference_type operator-(const const_iterator& other) { return row_ - other.row_; } 168 void operator+=(difference_type rows) { row_ += rows; } 169 void operator-=(difference_type rows) { row_ -= rows; } 170 const_iterator operator++() { return const_iterator(table_, ++row_); } 171 const_iterator operator--() { return const_iterator(table_, --row_); } 172 const_iterator operator++(int) { return const_iterator(table_, row_++); } 173 const_iterator operator--(int) { return const_iterator(table_, row_--); } 174 bool operator==(const_iterator i) const { DCHECK(table_ == i.table_); return row_ == i.row_; } 175 bool operator!=(const_iterator i) const { DCHECK(table_ == i.table_); return row_ != i.row_; } 176 bool operator<=(const_iterator i) const { DCHECK(table_ == i.table_); return row_ <= i.row_; } 177 bool operator>=(const_iterator i) const { DCHECK(table_ == i.table_); return row_ >= i.row_; } 178 bool operator<(const_iterator i) const { DCHECK(table_ == i.table_); return row_ < i.row_; } 179 bool operator>(const_iterator i) const { DCHECK(table_ == i.table_); return row_ > i.row_; } 180 Accessor operator*() { 181 DCHECK_LT(row_, table_->NumRows()); 182 return Accessor(table_, row_); 183 } 184 Accessor operator->() { 185 DCHECK_LT(row_, table_->NumRows()); 186 return Accessor(table_, row_); 187 } 188 Accessor operator[](size_t index) { 189 DCHECK_LT(row_ + index, table_->NumRows()); 190 return Accessor(table_, row_ + index); 191 } 192 private: 193 const BitTable* table_ = nullptr; 194 uint32_t row_ = 0; 195 }; 196 197 using BitTableBase<Accessor::kNumColumns>::BitTableBase; // Constructors. 198 199 ALWAYS_INLINE const_iterator begin() const { return const_iterator(this, 0); } 200 ALWAYS_INLINE const_iterator end() const { return const_iterator(this, this->NumRows()); } 201 202 ALWAYS_INLINE Accessor GetRow(uint32_t row) const { 203 return Accessor(this, row); 204 } 205 206 ALWAYS_INLINE Accessor GetInvalidRow() const { 207 return Accessor(this, static_cast<uint32_t>(-1)); 208 } 209 210 const char* GetName() const { 211 return Accessor::kTableName; 212 } 213 214 const char* const* GetColumnNames() const { 215 return GetBitTableColumnNamesImpl<Accessor>(std::make_index_sequence<Accessor::kNumColumns>()); 216 } 217 }; 218 219 template<typename Accessor> 220 typename BitTable<Accessor>::const_iterator operator+( 221 typename BitTable<Accessor>::const_iterator::difference_type n, 222 typename BitTable<Accessor>::const_iterator a) { 223 return a + n; 224 } 225 226 template<typename Accessor> 227 class BitTableRange : public IterationRange<typename BitTable<Accessor>::const_iterator> { 228 public: 229 typedef typename BitTable<Accessor>::const_iterator const_iterator; 230 231 using IterationRange<const_iterator>::IterationRange; 232 BitTableRange() : IterationRange<const_iterator>(const_iterator(), const_iterator()) { } 233 234 bool empty() const { return this->begin() == this->end(); } 235 size_t size() const { return this->end() - this->begin(); } 236 237 Accessor operator[](size_t index) const { 238 const_iterator it = this->begin() + index; 239 DCHECK(it < this->end()); 240 return *it; 241 } 242 243 Accessor back() const { 244 DCHECK(!empty()); 245 return *(this->end() - 1); 246 } 247 248 void pop_back() { 249 DCHECK(!empty()); 250 --this->last_; 251 } 252 }; 253 254 // Helper class for encoding BitTable. It can optionally de-duplicate the inputs. 255 template<uint32_t kNumColumns> 256 class BitTableBuilderBase { 257 public: 258 static constexpr uint32_t kNoValue = BitTableBase<kNumColumns>::kNoValue; 259 static constexpr uint32_t kValueBias = BitTableBase<kNumColumns>::kValueBias; 260 261 class Entry { 262 public: 263 Entry() { 264 // The definition of kNoValue here is for host and target debug builds which complain about 265 // missing a symbol definition for BitTableBase<N>::kNovValue when optimization is off. 266 static constexpr uint32_t kNoValue = BitTableBase<kNumColumns>::kNoValue; 267 std::fill_n(data_, kNumColumns, kNoValue); 268 } 269 270 Entry(std::initializer_list<uint32_t> values) { 271 DCHECK_EQ(values.size(), kNumColumns); 272 std::copy(values.begin(), values.end(), data_); 273 } 274 275 uint32_t& operator[](size_t column) { 276 DCHECK_LT(column, kNumColumns); 277 return data_[column]; 278 } 279 280 uint32_t operator[](size_t column) const { 281 DCHECK_LT(column, kNumColumns); 282 return data_[column]; 283 } 284 285 private: 286 uint32_t data_[kNumColumns]; 287 }; 288 289 explicit BitTableBuilderBase(ScopedArenaAllocator* allocator) 290 : rows_(allocator->Adapter(kArenaAllocBitTableBuilder)), 291 dedup_(8, allocator->Adapter(kArenaAllocBitTableBuilder)) { 292 } 293 294 Entry& operator[](size_t row) { return rows_[row]; } 295 const Entry& operator[](size_t row) const { return rows_[row]; } 296 const Entry& back() const { return rows_.back(); } 297 size_t size() const { return rows_.size(); } 298 299 // Append given value to the vector without de-duplication. 300 // This will not add the element to the dedup map to avoid its associated costs. 301 void Add(Entry value) { 302 rows_.push_back(value); 303 } 304 305 // Append given list of values and return the index of the first value. 306 // If the exact same set of values was already added, return the old index. 307 uint32_t Dedup(Entry* values, size_t count = 1) { 308 FNVHash<MemoryRegion> hasher; 309 uint32_t hash = hasher(MemoryRegion(values, sizeof(Entry) * count)); 310 311 // Check if we have already added identical set of values. 312 auto range = dedup_.equal_range(hash); 313 for (auto it = range.first; it != range.second; ++it) { 314 uint32_t index = it->second; 315 if (count <= size() - index && 316 std::equal(values, 317 values + count, 318 rows_.begin() + index, 319 [](const Entry& lhs, const Entry& rhs) { 320 return memcmp(&lhs, &rhs, sizeof(Entry)) == 0; 321 })) { 322 return index; 323 } 324 } 325 326 // Add the set of values and add the index to the dedup map. 327 uint32_t index = size(); 328 rows_.insert(rows_.end(), values, values + count); 329 dedup_.emplace(hash, index); 330 return index; 331 } 332 333 uint32_t Dedup(Entry value) { 334 return Dedup(&value, /* count */ 1); 335 } 336 337 // Calculate the column bit widths based on the current data. 338 void Measure(/*out*/ std::array<uint32_t, kNumColumns>* column_bits) const { 339 uint32_t max_column_value[kNumColumns]; 340 std::fill_n(max_column_value, kNumColumns, 0); 341 for (uint32_t r = 0; r < size(); r++) { 342 for (uint32_t c = 0; c < kNumColumns; c++) { 343 max_column_value[c] |= rows_[r][c] - kValueBias; 344 } 345 } 346 for (uint32_t c = 0; c < kNumColumns; c++) { 347 (*column_bits)[c] = MinimumBitsToStore(max_column_value[c]); 348 } 349 } 350 351 // Encode the stored data into a BitTable. 352 template<typename Vector> 353 void Encode(BitMemoryWriter<Vector>& out) const { 354 size_t initial_bit_offset = out.NumberOfWrittenBits(); 355 356 std::array<uint32_t, kNumColumns> column_bits; 357 Measure(&column_bits); 358 out.WriteVarint(size()); 359 if (size() != 0) { 360 // Write table header. 361 for (uint32_t c = 0; c < kNumColumns; c++) { 362 out.WriteVarint(column_bits[c]); 363 } 364 365 // Write table data. 366 for (uint32_t r = 0; r < size(); r++) { 367 for (uint32_t c = 0; c < kNumColumns; c++) { 368 out.WriteBits(rows_[r][c] - kValueBias, column_bits[c]); 369 } 370 } 371 } 372 373 // Verify the written data. 374 if (kIsDebugBuild) { 375 BitTableBase<kNumColumns> table; 376 BitMemoryReader reader(out.GetWrittenRegion().Subregion(initial_bit_offset)); 377 table.Decode(reader); 378 DCHECK_EQ(size(), table.NumRows()); 379 for (uint32_t c = 0; c < kNumColumns; c++) { 380 DCHECK_EQ(column_bits[c], table.NumColumnBits(c)); 381 } 382 for (uint32_t r = 0; r < size(); r++) { 383 for (uint32_t c = 0; c < kNumColumns; c++) { 384 DCHECK_EQ(rows_[r][c], table.Get(r, c)) << " (" << r << ", " << c << ")"; 385 } 386 } 387 } 388 } 389 390 protected: 391 ScopedArenaDeque<Entry> rows_; 392 ScopedArenaUnorderedMultimap<uint32_t, uint32_t> dedup_; // Hash -> row index. 393 }; 394 395 template<typename Accessor> 396 class BitTableBuilder : public BitTableBuilderBase<Accessor::kNumColumns> { 397 public: 398 using BitTableBuilderBase<Accessor::kNumColumns>::BitTableBuilderBase; // Constructors. 399 }; 400 401 // Helper class for encoding single-column BitTable of bitmaps (allows more than 32 bits). 402 class BitmapTableBuilder { 403 public: 404 explicit BitmapTableBuilder(ScopedArenaAllocator* const allocator) 405 : allocator_(allocator), 406 rows_(allocator->Adapter(kArenaAllocBitTableBuilder)), 407 dedup_(8, allocator_->Adapter(kArenaAllocBitTableBuilder)) { 408 } 409 410 MemoryRegion operator[](size_t row) { return rows_[row]; } 411 const MemoryRegion operator[](size_t row) const { return rows_[row]; } 412 size_t size() const { return rows_.size(); } 413 414 // Add the given bitmap to the table and return its index. 415 // If the bitmap was already added it will be deduplicated. 416 // The last bit must be set and any padding bits in the last byte must be zero. 417 uint32_t Dedup(const void* bitmap, size_t num_bits) { 418 MemoryRegion region(const_cast<void*>(bitmap), BitsToBytesRoundUp(num_bits)); 419 DCHECK(num_bits == 0 || BitMemoryRegion(region).LoadBit(num_bits - 1) == 1); 420 DCHECK_EQ(BitMemoryRegion(region).LoadBits(num_bits, region.size_in_bits() - num_bits), 0u); 421 FNVHash<MemoryRegion> hasher; 422 uint32_t hash = hasher(region); 423 424 // Check if we have already added identical bitmap. 425 auto range = dedup_.equal_range(hash); 426 for (auto it = range.first; it != range.second; ++it) { 427 if (MemoryRegion::ContentEquals()(region, rows_[it->second])) { 428 return it->second; 429 } 430 } 431 432 // Add the bitmap and add the index to the dedup map. 433 uint32_t index = size(); 434 void* copy = allocator_->Alloc(region.size(), kArenaAllocBitTableBuilder); 435 memcpy(copy, region.pointer(), region.size()); 436 rows_.push_back(MemoryRegion(copy, region.size())); 437 dedup_.emplace(hash, index); 438 max_num_bits_ = std::max(max_num_bits_, num_bits); 439 return index; 440 } 441 442 // Encode the stored data into a BitTable. 443 template<typename Vector> 444 void Encode(BitMemoryWriter<Vector>& out) const { 445 size_t initial_bit_offset = out.NumberOfWrittenBits(); 446 447 out.WriteVarint(size()); 448 if (size() != 0) { 449 out.WriteVarint(max_num_bits_); 450 451 // Write table data. 452 for (MemoryRegion row : rows_) { 453 BitMemoryRegion src(row); 454 BitMemoryRegion dst = out.Allocate(max_num_bits_); 455 dst.StoreBits(/* bit_offset */ 0, src, std::min(max_num_bits_, src.size_in_bits())); 456 } 457 } 458 459 // Verify the written data. 460 if (kIsDebugBuild) { 461 BitTableBase<1> table; 462 BitMemoryReader reader(out.GetWrittenRegion().Subregion(initial_bit_offset)); 463 table.Decode(reader); 464 DCHECK_EQ(size(), table.NumRows()); 465 DCHECK_EQ(max_num_bits_, table.NumColumnBits(0)); 466 for (uint32_t r = 0; r < size(); r++) { 467 BitMemoryRegion expected(rows_[r]); 468 BitMemoryRegion seen = table.GetBitMemoryRegion(r); 469 size_t num_bits = std::max(expected.size_in_bits(), seen.size_in_bits()); 470 for (size_t b = 0; b < num_bits; b++) { 471 bool e = b < expected.size_in_bits() && expected.LoadBit(b); 472 bool s = b < seen.size_in_bits() && seen.LoadBit(b); 473 DCHECK_EQ(e, s) << " (" << r << ")[" << b << "]"; 474 } 475 } 476 } 477 } 478 479 private: 480 ScopedArenaAllocator* const allocator_; 481 ScopedArenaDeque<MemoryRegion> rows_; 482 ScopedArenaUnorderedMultimap<uint32_t, uint32_t> dedup_; // Hash -> row index. 483 size_t max_num_bits_ = 0u; 484 }; 485 486 } // namespace art 487 488 #endif // ART_LIBARTBASE_BASE_BIT_TABLE_H_ 489