// Generic purpose table of uint32_t values, which are tightly packed at bit level. // It has its own header with the number of rows and the bit-widths of all columns. // The values are accessible by (row, column). The value -1 is stored efficiently. template<uint32_t kNumColumns> class BitTableBase { public: static constexpr uint32_t kNoValue = std::numeric_limits<uint32_t>::max(); // == -1. static constexpr uint32_t kValueBias = kNoValue; // Bias so that -1 is encoded as 0.
ALWAYS_INLINE void Decode(BitMemoryReader& reader) { // Decode row count and column sizes from the table header.
std::array<uint32_t, 1+kNumColumns> header = reader.ReadInterleavedVarints<1+kNumColumns>();
num_rows_ = header[0];
row_bits_ = 0;
// Column offset is cumulative and stored as uint16 for performance. // Note that the last column is allowed to end past uint16 offset, // which we use in some corner cases (for very large masks). for (uint32_t i = 0; i < kNumColumns; i++) {
column_offset_[i] = dchecked_integral_cast<uint16_t>(row_bits_);
row_bits_ += header[i + 1]; // row end is stored as uint32_t.
}
// Record the region which contains the table data and skip past it.
table_data_ = reader.ReadRegion(num_rows_ * RowBits());
}
template <uint32_t kColumn>
ALWAYS_INLINE uint32_t Get(uint32_t row) const {
static_assert(kColumn < kNumColumns);
DCHECK(table_data_.IsValid()) << "Table has not been loaded";
DCHECK_LT(row, num_rows_); auto [column_offset, column_length] = ColumnRange<kColumn>();
size_t offset = row * RowBits() + column_offset; return table_data_.LoadBits(offset, column_length) + kValueBias;
}
template <uint32_t kColumn>
ALWAYS_INLINE BitMemoryRegion GetBitMemoryRegion(uint32_t row) const {
static_assert(kColumn < kNumColumns);
DCHECK(table_data_.IsValid()) << "Table has not been loaded";
DCHECK_LT(row, num_rows_); auto [column_offset, column_length] = ColumnRange<kColumn>();
size_t offset = row * RowBits() + column_offset; return table_data_.Subregion(offset, column_length);
}
// Helper class which can be used to create BitTable accessors with named getters. template<uint32_t NumColumns> class BitTableAccessor { public: static constexpr uint32_t kNumColumns = NumColumns; static constexpr uint32_t kNoValue = BitTableBase<kNumColumns>::kNoValue;
template<typename Accessor> typename BitTable<Accessor>::const_iterator operator+( typename BitTable<Accessor>::const_iterator::difference_type n, typename BitTable<Accessor>::const_iterator a) { return a + n;
}
template<typename Accessor> class BitTableRange : public IterationRange<typename BitTable<Accessor>::const_iterator> { public: using const_iterator = typename BitTable<Accessor>::const_iterator;
using IterationRange<const_iterator>::IterationRange;
BitTableRange() : IterationRange<const_iterator>(const_iterator(), const_iterator()) { }
// Helper class for encoding BitTable. It can optionally de-duplicate the inputs. template<uint32_t kNumColumns> class BitTableBuilderBase { public: static constexpr uint32_t kNoValue = BitTableBase<kNumColumns>::kNoValue; static constexpr uint32_t kValueBias = BitTableBase<kNumColumns>::kValueBias;
class Entry { public:
Entry() { // The definition of kLocalNoValue here is for host and target debug builds which // complain about missing a symbol definition for BitTableBase<N>::kNovValue when // optimization is off. static constexpr uint32_t kLocalNoValue = BitTableBase<kNumColumns>::kNoValue;
std::fill_n(data_, kNumColumns, kLocalNoValue);
}
// Append given value to the vector without de-duplication. // This will not add the element to the dedup map to avoid its associated costs. void Add(Entry value) {
rows_.push_back(value);
}
// Append given list of values and return the index of the first value. // If the exact same set of values was already added, return the old index.
uint32_t Dedup(Entry* values, size_t count = 1) {
FNVHash<MemoryRegion> hasher;
uint32_t hash = hasher(MemoryRegion(values, sizeof(Entry) * count));
// Check if we have already added identical set of values. auto range = dedup_.equal_range(hash); for (auto it = range.first; it != range.second; ++it) {
uint32_t index = it->second; if (count <= size() - index &&
std::equal(values,
values + count,
rows_.begin() + index,
[](const Entry& lhs, const Entry& rhs) { return memcmp(&lhs, &rhs, sizeof(Entry)) == 0;
})) { return index;
}
}
// Add the set of values and add the index to the dedup map.
uint32_t index = size();
rows_.insert(rows_.end(), values, values + count);
dedup_.emplace(hash, index); return index;
}
// Calculate the column bit widths based on the current data. void Measure(/*out*/ uint32_t* column_bits) const {
uint32_t max_column_value[kNumColumns];
std::fill_n(max_column_value, kNumColumns, 0); for (uint32_t r = 0; r < size(); r++) { for (uint32_t c = 0; c < kNumColumns; c++) {
max_column_value[c] |= rows_[r][c] - kValueBias;
}
} for (uint32_t c = 0; c < kNumColumns; c++) {
column_bits[c] = MinimumBitsToStore(max_column_value[c]);
}
}
// Encode the stored data into a BitTable. template<typename Vector> void Encode(BitMemoryWriter<Vector>& out) const {
size_t initial_bit_offset = out.NumberOfWrittenBits();
// Write table data. for (uint32_t r = 0; r < size(); r++) { for (uint32_t c = 0; c < kNumColumns; c++) {
out.WriteBits(rows_[r][c] - kValueBias, column_bits[c]);
}
}
// Verify the written data. if (kIsDebugBuild) {
BitTableBase<kNumColumns> table;
BitMemoryReader reader(out.GetWrittenRegion().Subregion(initial_bit_offset));
table.Decode(reader);
DCHECK_EQ(size(), table.NumRows());
table.ForEachColumnIndex([&](auto c_const) {
constexpr uint32_t c = c_const;
DCHECK_EQ(column_bits[c], table.template NumColumnBits<c>());
}); for (uint32_t r = 0; r < size(); r++) {
table.ForEachColumnIndex([&](auto c_const) {
constexpr uint32_t c = c_const;
DCHECK_EQ(rows_[r][c], table.template Get<c>(r)) << " (" << r << ", " << c << ")";
});
}
}
}
template<typename Accessor> class BitTableBuilder : public BitTableBuilderBase<Accessor::kNumColumns> { public: using BitTableBuilderBase<Accessor::kNumColumns>::BitTableBuilderBase; // Constructors.
};
// Helper class for encoding single-column BitTable of bitmaps (allows more than 32 bits). class BitmapTableBuilder { public: explicit BitmapTableBuilder(ScopedArenaAllocator* const allocator)
: allocator_(allocator),
rows_(allocator->Adapter(kArenaAllocBitTableBuilder)),
dedup_(8, allocator_->Adapter(kArenaAllocBitTableBuilder)) {
rows_.reserve(8);
}
// Add the given bitmap to the table and return its index. // If the bitmap was already added it will be deduplicated. // The last bit must be set and any padding bits in the last byte must be zero.
uint32_t Dedup(constvoid* bitmap, size_t num_bits) {
MemoryRegion region(const_cast<void*>(bitmap), BitsToBytesRoundUp(num_bits));
DCHECK(num_bits == 0 || BitMemoryRegion(region).LoadBit(num_bits - 1) == 1);
DCHECK_EQ(BitMemoryRegion(region).LoadBits(num_bits, region.size_in_bits() - num_bits), 0u);
FNVHash<MemoryRegion> hasher;
uint32_t hash = hasher(region);
// Check if we have already added identical bitmap. auto range = dedup_.equal_range(hash); for (auto it = range.first; it != range.second; ++it) { if (region == rows_[it->second]) { return it->second;
}
}
// Add the bitmap and add the index to the dedup map.
uint32_t index = size(); void* copy = allocator_->Alloc(region.size(), kArenaAllocBitTableBuilder);
memcpy(copy, region.pointer(), region.size());
rows_.push_back(MemoryRegion(copy, region.size()));
dedup_.emplace(hash, index);
max_num_bits_ = std::max(max_num_bits_, num_bits); return index;
}
// Encode the stored data into a BitTable. template<typename Vector> void Encode(BitMemoryWriter<Vector>& out) const {
size_t initial_bit_offset = out.NumberOfWrittenBits();
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.