Home | History | Annotate | Download | only in base
      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