Remove POD helper classes for BitTableBuilder.

Instead of declaring the classes explicitly and then casting,
create generic BitTableBuilder::Entry class for that purpose.
This removes the need to keep the POD helper classes in sync.

Test: test-art-host-gtest-stack_map_test
Test: test-art-host-gtest-bit_table_test
Change-Id: I4c632313bafd3a4bc823648436a5310b6f2a1d13
diff --git a/compiler/optimizing/stack_map_stream.cc b/compiler/optimizing/stack_map_stream.cc
index cd11549..ca58514 100644
--- a/compiler/optimizing/stack_map_stream.cc
+++ b/compiler/optimizing/stack_map_stream.cc
@@ -31,11 +31,12 @@
 constexpr static bool kVerifyStackMaps = kIsDebugBuild;
 
 uint32_t StackMapStream::GetStackMapNativePcOffset(size_t i) {
-  return StackMap::UnpackNativePc(stack_maps_[i].packed_native_pc, instruction_set_);
+  return StackMap::UnpackNativePc(stack_maps_[i][StackMap::kPackedNativePc], instruction_set_);
 }
 
 void StackMapStream::SetStackMapNativePcOffset(size_t i, uint32_t native_pc_offset) {
-  stack_maps_[i].packed_native_pc = StackMap::PackNativePc(native_pc_offset, instruction_set_);
+  stack_maps_[i][StackMap::kPackedNativePc] =
+      StackMap::PackNativePc(native_pc_offset, instruction_set_);
 }
 
 void StackMapStream::BeginStackMapEntry(uint32_t dex_pc,
@@ -55,20 +56,17 @@
     DCHECK_EQ(num_dex_registers_, num_dex_registers) << "Inconsistent register count";
   }
 
-  current_stack_map_ = StackMapEntry {
-    .kind = static_cast<uint32_t>(kind),
-    .packed_native_pc = StackMap::PackNativePc(native_pc_offset, instruction_set_),
-    .dex_pc = dex_pc,
-    .register_mask_index = kNoValue,
-    .stack_mask_index = kNoValue,
-    .inline_info_index = kNoValue,
-    .dex_register_mask_index = kNoValue,
-    .dex_register_map_index = kNoValue,
-  };
+  current_stack_map_ = BitTableBuilder<StackMap::kCount>::Entry();
+  current_stack_map_[StackMap::kKind] = static_cast<uint32_t>(kind);
+  current_stack_map_[StackMap::kPackedNativePc] =
+      StackMap::PackNativePc(native_pc_offset, instruction_set_);
+  current_stack_map_[StackMap::kDexPc] = dex_pc;
   if (register_mask != 0) {
     uint32_t shift = LeastSignificantBit(register_mask);
-    RegisterMaskEntry entry = { register_mask >> shift, shift };
-    current_stack_map_.register_mask_index = register_masks_.Dedup(&entry);
+    BitTableBuilder<RegisterMask::kCount>::Entry entry;
+    entry[RegisterMask::kValue] = register_mask >> shift;
+    entry[RegisterMask::kShift] = shift;
+    current_stack_map_[StackMap::kRegisterMaskIndex] = register_masks_.Dedup(&entry);
   }
   // The compiler assumes the bit vector will be read during PrepareForFillIn(),
   // and it might modify the data before that. Therefore, just store the pointer.
@@ -114,8 +112,8 @@
 
   // Generate index into the InlineInfo table.
   if (!current_inline_infos_.empty()) {
-    current_inline_infos_.back().is_last = InlineInfo::kLast;
-    current_stack_map_.inline_info_index =
+    current_inline_infos_.back()[InlineInfo::kIsLast] = InlineInfo::kLast;
+    current_stack_map_[StackMap::kInlineInfoIndex] =
         inline_infos_.Dedup(current_inline_infos_.data(), current_inline_infos_.size());
   }
 
@@ -130,13 +128,13 @@
 }
 
 void StackMapStream::AddInvoke(InvokeType invoke_type, uint32_t dex_method_index) {
-  uint32_t packed_native_pc = current_stack_map_.packed_native_pc;
+  uint32_t packed_native_pc = current_stack_map_[StackMap::kPackedNativePc];
   size_t invoke_info_index = invoke_infos_.size();
-  invoke_infos_.Add(InvokeInfoEntry {
-    .packed_native_pc = packed_native_pc,
-    .invoke_type = invoke_type,
-    .method_info_index = method_infos_.Dedup(&dex_method_index),
-  });
+  BitTableBuilder<InvokeInfo::kCount>::Entry entry;
+  entry[InvokeInfo::kPackedNativePc] = packed_native_pc;
+  entry[InvokeInfo::kInvokeType] = invoke_type;
+  entry[InvokeInfo::kMethodInfoIndex] = method_infos_.Dedup({dex_method_index});
+  invoke_infos_.Add(entry);
 
   if (kVerifyStackMaps) {
     dchecks_.emplace_back([=](const CodeInfo& code_info) {
@@ -144,7 +142,7 @@
       CHECK_EQ(invoke_info.GetNativePcOffset(instruction_set_),
                StackMap::UnpackNativePc(packed_native_pc, instruction_set_));
       CHECK_EQ(invoke_info.GetInvokeType(), invoke_type);
-      CHECK_EQ(method_infos_[invoke_info.GetMethodInfoIndex()], dex_method_index);
+      CHECK_EQ(method_infos_[invoke_info.GetMethodInfoIndex()][0], dex_method_index);
     });
   }
 }
@@ -159,24 +157,20 @@
 
   expected_num_dex_registers_ += num_dex_registers;
 
-  InlineInfoEntry entry = {
-    .is_last = InlineInfo::kMore,
-    .dex_pc = dex_pc,
-    .method_info_index = kNoValue,
-    .art_method_hi = kNoValue,
-    .art_method_lo = kNoValue,
-    .num_dex_registers = static_cast<uint32_t>(expected_num_dex_registers_),
-  };
+  BitTableBuilder<InlineInfo::kCount>::Entry entry;
+  entry[InlineInfo::kIsLast] = InlineInfo::kMore;
+  entry[InlineInfo::kDexPc] = dex_pc;
+  entry[InlineInfo::kNumberOfDexRegisters] = static_cast<uint32_t>(expected_num_dex_registers_);
   if (EncodeArtMethodInInlineInfo(method)) {
-    entry.art_method_hi = High32Bits(reinterpret_cast<uintptr_t>(method));
-    entry.art_method_lo = Low32Bits(reinterpret_cast<uintptr_t>(method));
+    entry[InlineInfo::kArtMethodHi] = High32Bits(reinterpret_cast<uintptr_t>(method));
+    entry[InlineInfo::kArtMethodLo] = Low32Bits(reinterpret_cast<uintptr_t>(method));
   } else {
     if (dex_pc != static_cast<uint32_t>(-1) && kIsDebugBuild) {
       ScopedObjectAccess soa(Thread::Current());
       DCHECK(IsSameDexFile(*outer_dex_file, *method->GetDexFile()));
     }
     uint32_t dex_method_index = method->GetDexMethodIndexUnchecked();
-    entry.method_info_index = method_infos_.Dedup(&dex_method_index);
+    entry[InlineInfo::kMethodInfoIndex] = method_infos_.Dedup({dex_method_index});
   }
   current_inline_infos_.push_back(entry);
 
@@ -192,7 +186,7 @@
       if (encode_art_method) {
         CHECK_EQ(inline_info.GetArtMethod(), method);
       } else {
-        CHECK_EQ(method_infos_[inline_info.GetMethodInfoIndex()],
+        CHECK_EQ(method_infos_[inline_info.GetMethodInfoIndex()][0],
                  method->GetDexMethodIndexUnchecked());
       }
     });
@@ -225,13 +219,13 @@
     // Distance is difference between this index and the index of last modification.
     uint32_t distance = stack_maps_.size() - dex_register_timestamp_[i];
     if (previous_dex_registers_[i] != reg || distance > kMaxDexRegisterMapSearchDistance) {
-      DexRegisterEntry entry = DexRegisterEntry{
-        .kind = static_cast<uint32_t>(reg.GetKind()),
-        .packed_value = DexRegisterInfo::PackValue(reg.GetKind(), reg.GetValue()),
-      };
+      BitTableBuilder<DexRegisterInfo::kCount>::Entry entry;
+      entry[DexRegisterInfo::kKind] = static_cast<uint32_t>(reg.GetKind());
+      entry[DexRegisterInfo::kPackedValue] =
+          DexRegisterInfo::PackValue(reg.GetKind(), reg.GetValue());
       uint32_t index = reg.IsLive() ? dex_register_catalog_.Dedup(&entry) : kNoValue;
       temp_dex_register_mask_.SetBit(i);
-      temp_dex_register_map_.push_back(index);
+      temp_dex_register_map_.push_back({index});
       previous_dex_registers_[i] = reg;
       dex_register_timestamp_[i] = stack_maps_.size();
     }
@@ -239,12 +233,12 @@
 
   // Set the mask and map for the current StackMap (which includes inlined registers).
   if (temp_dex_register_mask_.GetNumberOfBits() != 0) {
-    current_stack_map_.dex_register_mask_index =
+    current_stack_map_[StackMap::kDexRegisterMaskIndex] =
         dex_register_masks_.Dedup(temp_dex_register_mask_.GetRawStorage(),
                                   temp_dex_register_mask_.GetNumberOfBits());
   }
   if (!current_dex_registers_.empty()) {
-    current_stack_map_.dex_register_map_index =
+    current_stack_map_[StackMap::kDexRegisterMapIndex] =
         dex_register_maps_.Dedup(temp_dex_register_map_.data(),
                                  temp_dex_register_map_.size());
   }
@@ -275,7 +269,7 @@
   {
     MethodInfo info(region.begin(), method_infos_.size());
     for (size_t i = 0; i < method_infos_.size(); ++i) {
-      info.SetMethodIndex(i, method_infos_[i]);
+      info.SetMethodIndex(i, method_infos_[i][0]);
     }
   }
   if (kVerifyStackMaps) {
@@ -284,23 +278,19 @@
     const size_t count = info.NumMethodIndices();
     DCHECK_EQ(count, method_infos_.size());
     for (size_t i = 0; i < count; ++i) {
-      DCHECK_EQ(info.GetMethodIndex(i), method_infos_[i]);
+      DCHECK_EQ(info.GetMethodIndex(i), method_infos_[i][0]);
     }
   }
 }
 
 size_t StackMapStream::PrepareForFillIn() {
-  static_assert(sizeof(StackMapEntry) == StackMap::kCount * sizeof(uint32_t), "Layout");
-  static_assert(sizeof(InvokeInfoEntry) == InvokeInfo::kCount * sizeof(uint32_t), "Layout");
-  static_assert(sizeof(InlineInfoEntry) == InlineInfo::kCount * sizeof(uint32_t), "Layout");
-  static_assert(sizeof(DexRegisterEntry) == DexRegisterInfo::kCount * sizeof(uint32_t), "Layout");
   DCHECK_EQ(out_.size(), 0u);
 
   // Read the stack masks now. The compiler might have updated them.
   for (size_t i = 0; i < lazy_stack_masks_.size(); i++) {
     BitVector* stack_mask = lazy_stack_masks_[i];
     if (stack_mask != nullptr && stack_mask->GetNumberOfBits() != 0) {
-      stack_maps_[i].stack_mask_index =
+      stack_maps_[i][StackMap::kStackMaskIndex] =
         stack_masks_.Dedup(stack_mask->GetRawStorage(), stack_mask->GetNumberOfBits());
     }
   }
diff --git a/compiler/optimizing/stack_map_stream.h b/compiler/optimizing/stack_map_stream.h
index 0686847..c0a41af 100644
--- a/compiler/optimizing/stack_map_stream.h
+++ b/compiler/optimizing/stack_map_stream.h
@@ -52,6 +52,7 @@
         lazy_stack_masks_(allocator->Adapter(kArenaAllocStackMapStream)),
         in_stack_map_(false),
         in_inline_info_(false),
+        current_stack_map_(),
         current_inline_infos_(allocator->Adapter(kArenaAllocStackMapStream)),
         current_dex_registers_(allocator->Adapter(kArenaAllocStackMapStream)),
         previous_dex_registers_(allocator->Adapter(kArenaAllocStackMapStream)),
@@ -97,70 +98,29 @@
  private:
   static constexpr uint32_t kNoValue = -1;
 
-  // The fields must be uint32_t and mirror the StackMap accessor in stack_map.h!
-  struct StackMapEntry {
-    uint32_t kind;
-    uint32_t packed_native_pc;
-    uint32_t dex_pc;
-    uint32_t register_mask_index;
-    uint32_t stack_mask_index;
-    uint32_t inline_info_index;
-    uint32_t dex_register_mask_index;
-    uint32_t dex_register_map_index;
-  };
-
-  // The fields must be uint32_t and mirror the InlineInfo accessor in stack_map.h!
-  struct InlineInfoEntry {
-    uint32_t is_last;
-    uint32_t dex_pc;
-    uint32_t method_info_index;
-    uint32_t art_method_hi;
-    uint32_t art_method_lo;
-    uint32_t num_dex_registers;
-  };
-
-  // The fields must be uint32_t and mirror the InvokeInfo accessor in stack_map.h!
-  struct InvokeInfoEntry {
-    uint32_t packed_native_pc;
-    uint32_t invoke_type;
-    uint32_t method_info_index;
-  };
-
-  // The fields must be uint32_t and mirror the DexRegisterInfo accessor in stack_map.h!
-  struct DexRegisterEntry {
-    uint32_t kind;
-    uint32_t packed_value;
-  };
-
-  // The fields must be uint32_t and mirror the RegisterMask accessor in stack_map.h!
-  struct RegisterMaskEntry {
-    uint32_t value;
-    uint32_t shift;
-  };
-
   void CreateDexRegisterMap();
 
   const InstructionSet instruction_set_;
-  BitTableBuilder<StackMapEntry> stack_maps_;
-  BitTableBuilder<RegisterMaskEntry> register_masks_;
+  BitTableBuilder<StackMap::kCount> stack_maps_;
+  BitTableBuilder<RegisterMask::kCount> register_masks_;
   BitmapTableBuilder stack_masks_;
-  BitTableBuilder<InvokeInfoEntry> invoke_infos_;
-  BitTableBuilder<InlineInfoEntry> inline_infos_;
+  BitTableBuilder<InvokeInfo::kCount> invoke_infos_;
+  BitTableBuilder<InlineInfo::kCount> inline_infos_;
   BitmapTableBuilder dex_register_masks_;
-  BitTableBuilder<uint32_t> dex_register_maps_;
-  BitTableBuilder<DexRegisterEntry> dex_register_catalog_;
+  BitTableBuilder<MaskInfo::kCount> dex_register_maps_;
+  BitTableBuilder<DexRegisterInfo::kCount> dex_register_catalog_;
   uint32_t num_dex_registers_ = 0;  // TODO: Make this const and get the value in constructor.
   ScopedArenaVector<uint8_t> out_;
 
-  BitTableBuilder<uint32_t> method_infos_;
+  BitTableBuilder<1> method_infos_;
 
   ScopedArenaVector<BitVector*> lazy_stack_masks_;
 
   // Variables which track the current state between Begin/End calls;
   bool in_stack_map_;
   bool in_inline_info_;
-  StackMapEntry current_stack_map_;
-  ScopedArenaVector<InlineInfoEntry> current_inline_infos_;
+  BitTableBuilder<StackMap::kCount>::Entry current_stack_map_;
+  ScopedArenaVector<BitTableBuilder<InlineInfo::kCount>::Entry> current_inline_infos_;
   ScopedArenaVector<DexRegisterLocation> current_dex_registers_;
   ScopedArenaVector<DexRegisterLocation> previous_dex_registers_;
   ScopedArenaVector<uint32_t> dex_register_timestamp_;  // Stack map index of last change.
@@ -169,7 +129,7 @@
   // Temporary variables used in CreateDexRegisterMap.
   // They are here so that we can reuse the reserved memory.
   ArenaBitVector temp_dex_register_mask_;
-  ScopedArenaVector<uint32_t> temp_dex_register_map_;
+  ScopedArenaVector<BitTableBuilder<DexRegisterMapInfo::kCount>::Entry> temp_dex_register_map_;
 
   // A set of lambda functions to be executed at the end to verify
   // the encoded data. It is generally only used in debug builds.
diff --git a/libartbase/base/bit_table.h b/libartbase/base/bit_table.h
index 6a714e6..2cc1a31 100644
--- a/libartbase/base/bit_table.h
+++ b/libartbase/base/bit_table.h
@@ -18,6 +18,7 @@
 #define ART_LIBARTBASE_BASE_BIT_TABLE_H_
 
 #include <array>
+#include <initializer_list>
 #include <numeric>
 #include <string.h>
 #include <type_traits>
@@ -184,33 +185,54 @@
 }
 
 // Helper class for encoding BitTable. It can optionally de-duplicate the inputs.
-// Type 'T' must be POD type consisting of uint32_t fields (one for each column).
-template<typename T>
+template<uint32_t kNumColumns>
 class BitTableBuilder {
  public:
-  static_assert(std::is_pod<T>::value, "Type 'T' must be POD");
-  static constexpr size_t kNumColumns = sizeof(T) / sizeof(uint32_t);
+  class Entry {
+   public:
+    Entry() {
+      std::fill_n(data_, kNumColumns, BitTable<kNumColumns>::Accessor::kNoValue);
+    }
+
+    Entry(std::initializer_list<uint32_t> values) {
+      DCHECK_EQ(values.size(), kNumColumns);
+      std::copy(values.begin(), values.end(), data_);
+    }
+
+    uint32_t& operator[](size_t column) {
+      DCHECK_LT(column, kNumColumns);
+      return data_[column];
+    }
+
+    uint32_t operator[](size_t column) const {
+      DCHECK_LT(column, kNumColumns);
+      return data_[column];
+    }
+
+   private:
+    uint32_t data_[kNumColumns];
+  };
 
   explicit BitTableBuilder(ScopedArenaAllocator* allocator)
       : rows_(allocator->Adapter(kArenaAllocBitTableBuilder)),
         dedup_(8, allocator->Adapter(kArenaAllocBitTableBuilder)) {
   }
 
-  T& operator[](size_t row) { return rows_[row]; }
-  const T& operator[](size_t row) const { return rows_[row]; }
+  Entry& operator[](size_t row) { return rows_[row]; }
+  const Entry& operator[](size_t row) const { return rows_[row]; }
   size_t size() const { return rows_.size(); }
 
   // 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(T value) {
+  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(T* values, size_t count = 1) {
+  uint32_t Dedup(Entry* values, size_t count = 1) {
     FNVHash<MemoryRegion> hasher;
-    uint32_t hash = hasher(MemoryRegion(values, sizeof(T) * count));
+    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);
@@ -220,8 +242,8 @@
           std::equal(values,
                      values + count,
                      rows_.begin() + index,
-                     [](const T& lhs, const T& rhs) {
-                       return memcmp(&lhs, &rhs, sizeof(T)) == 0;
+                     [](const Entry& lhs, const Entry& rhs) {
+                       return memcmp(&lhs, &rhs, sizeof(Entry)) == 0;
                      })) {
         return index;
       }
@@ -234,11 +256,8 @@
     return index;
   }
 
-  ALWAYS_INLINE uint32_t Get(uint32_t row, uint32_t column) const {
-    DCHECK_LT(row, size());
-    DCHECK_LT(column, kNumColumns);
-    const uint32_t* data = reinterpret_cast<const uint32_t*>(&rows_[row]);
-    return data[column];
+  uint32_t Dedup(Entry value) {
+    return Dedup(&value, /* count */ 1);
   }
 
   // Calculate the column bit widths based on the current data.
@@ -247,7 +266,7 @@
     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] |= Get(r, c) - BitTable<kNumColumns>::kValueBias;
+        max_column_value[c] |= rows_[r][c] - BitTable<kNumColumns>::kValueBias;
       }
     }
     for (uint32_t c = 0; c < kNumColumns; c++) {
@@ -276,7 +295,7 @@
       BitMemoryRegion region(MemoryRegion(out->data(), out->size()));
       for (uint32_t r = 0; r < size(); r++) {
         for (uint32_t c = 0; c < kNumColumns; c++) {
-          region.StoreBitsAndAdvance(bit_offset, Get(r, c) - bias, column_bits[c]);
+          region.StoreBitsAndAdvance(bit_offset, rows_[r][c] - bias, column_bits[c]);
         }
       }
     }
@@ -292,14 +311,14 @@
       }
       for (uint32_t r = 0; r < size(); r++) {
         for (uint32_t c = 0; c < kNumColumns; c++) {
-          DCHECK_EQ(Get(r, c), table.Get(r, c)) << " (" << r << ", " << c << ")";
+          DCHECK_EQ(rows_[r][c], table.Get(r, c)) << " (" << r << ", " << c << ")";
         }
       }
     }
   }
 
  protected:
-  ScopedArenaDeque<T> rows_;
+  ScopedArenaDeque<Entry> rows_;
   ScopedArenaUnorderedMultimap<uint32_t, uint32_t> dedup_;  // Hash -> row index.
 };
 
diff --git a/libartbase/base/bit_table_test.cc b/libartbase/base/bit_table_test.cc
index 8abf0da..969940f 100644
--- a/libartbase/base/bit_table_test.cc
+++ b/libartbase/base/bit_table_test.cc
@@ -50,7 +50,7 @@
 
   std::vector<uint8_t> buffer;
   size_t encode_bit_offset = 0;
-  BitTableBuilder<uint32_t> builder(&allocator);
+  BitTableBuilder<1> builder(&allocator);
   builder.Encode(&buffer, &encode_bit_offset);
 
   size_t decode_bit_offset = 0;
@@ -67,11 +67,11 @@
   constexpr uint32_t kNoValue = -1;
   std::vector<uint8_t> buffer;
   size_t encode_bit_offset = 0;
-  BitTableBuilder<uint32_t> builder(&allocator);
-  builder.Add(42u);
-  builder.Add(kNoValue);
-  builder.Add(1000u);
-  builder.Add(kNoValue);
+  BitTableBuilder<1> builder(&allocator);
+  builder.Add({42u});
+  builder.Add({kNoValue});
+  builder.Add({1000u});
+  builder.Add({kNoValue});
   builder.Encode(&buffer, &encode_bit_offset);
 
   size_t decode_bit_offset = 0;
@@ -93,8 +93,8 @@
   for (size_t start_bit_offset = 0; start_bit_offset <= 32; start_bit_offset++) {
     std::vector<uint8_t> buffer;
     size_t encode_bit_offset = start_bit_offset;
-    BitTableBuilder<uint32_t> builder(&allocator);
-    builder.Add(42u);
+    BitTableBuilder<1> builder(&allocator);
+    builder.Add({42u});
     builder.Encode(&buffer, &encode_bit_offset);
 
     size_t decode_bit_offset = start_bit_offset;
@@ -113,15 +113,9 @@
   constexpr uint32_t kNoValue = -1;
   std::vector<uint8_t> buffer;
   size_t encode_bit_offset = 0;
-  struct RowData {
-    uint32_t a;
-    uint32_t b;
-    uint32_t c;
-    uint32_t d;
-  };
-  BitTableBuilder<RowData> builder(&allocator);
-  builder.Add(RowData{42u, kNoValue, 0u, static_cast<uint32_t>(-2)});
-  builder.Add(RowData{62u, kNoValue, 63u, static_cast<uint32_t>(-3)});
+  BitTableBuilder<4> builder(&allocator);
+  builder.Add({42u, kNoValue, 0u, static_cast<uint32_t>(-2)});
+  builder.Add({62u, kNoValue, 63u, static_cast<uint32_t>(-3)});
   builder.Encode(&buffer, &encode_bit_offset);
 
   size_t decode_bit_offset = 0;
@@ -147,13 +141,9 @@
   ArenaStack arena_stack(&pool);
   ScopedArenaAllocator allocator(&arena_stack);
 
-  struct RowData {
-    uint32_t a;
-    uint32_t b;
-  };
-  BitTableBuilder<RowData> builder(&allocator);
-  RowData value0{1, 2};
-  RowData value1{3, 4};
+  BitTableBuilder<2> builder(&allocator);
+  BitTableBuilder<2>::Entry value0{1, 2};
+  BitTableBuilder<2>::Entry value1{3, 4};
   EXPECT_EQ(0u, builder.Dedup(&value0));
   EXPECT_EQ(1u, builder.Dedup(&value1));
   EXPECT_EQ(0u, builder.Dedup(&value0));
@@ -197,16 +187,12 @@
   ScopedArenaAllocator allocator(&arena_stack);
   FNVHash<MemoryRegion> hasher;
 
-  struct RowData {
-    uint32_t a;
-    uint32_t b;
-  };
-  RowData value0{56948505, 0};
-  RowData value1{67108869, 0};
+  BitTableBuilder<2>::Entry value0{56948505, 0};
+  BitTableBuilder<2>::Entry value1{67108869, 0};
 
-  BitTableBuilder<RowData> builder(&allocator);
-  EXPECT_EQ(hasher(MemoryRegion(&value0, sizeof(RowData))),
-            hasher(MemoryRegion(&value1, sizeof(RowData))));
+  BitTableBuilder<2> builder(&allocator);
+  EXPECT_EQ(hasher(MemoryRegion(&value0, sizeof(value0))),
+            hasher(MemoryRegion(&value1, sizeof(value1))));
   EXPECT_EQ(0u, builder.Dedup(&value0));
   EXPECT_EQ(1u, builder.Dedup(&value1));
   EXPECT_EQ(0u, builder.Dedup(&value0));
@@ -214,12 +200,12 @@
   EXPECT_EQ(2u, builder.size());
 
   BitmapTableBuilder builder2(&allocator);
-  EXPECT_EQ(hasher(MemoryRegion(&value0, BitsToBytesRoundUp(MinimumBitsToStore(value0.a)))),
-            hasher(MemoryRegion(&value1, BitsToBytesRoundUp(MinimumBitsToStore(value1.a)))));
-  EXPECT_EQ(0u, builder2.Dedup(&value0.a, MinimumBitsToStore(value0.a)));
-  EXPECT_EQ(1u, builder2.Dedup(&value1.a, MinimumBitsToStore(value1.a)));
-  EXPECT_EQ(0u, builder2.Dedup(&value0.a, MinimumBitsToStore(value0.a)));
-  EXPECT_EQ(1u, builder2.Dedup(&value1.a, MinimumBitsToStore(value1.a)));
+  EXPECT_EQ(hasher(MemoryRegion(&value0, BitsToBytesRoundUp(MinimumBitsToStore(value0[0])))),
+            hasher(MemoryRegion(&value1, BitsToBytesRoundUp(MinimumBitsToStore(value1[0])))));
+  EXPECT_EQ(0u, builder2.Dedup(&value0[0], MinimumBitsToStore(value0[0])));
+  EXPECT_EQ(1u, builder2.Dedup(&value1[0], MinimumBitsToStore(value1[0])));
+  EXPECT_EQ(0u, builder2.Dedup(&value0[0], MinimumBitsToStore(value0[0])));
+  EXPECT_EQ(1u, builder2.Dedup(&value1[0], MinimumBitsToStore(value1[0])));
   EXPECT_EQ(2u, builder2.size());
 }