Performance improvement for mapping table creation.

Avoid the raw mapping tables altogether.

Change-Id: I6d1c786325d369e899a75f15701edbafdd14363f
diff --git a/compiler/dex/quick/codegen_util.cc b/compiler/dex/quick/codegen_util.cc
index 92b24e1..a682d58 100644
--- a/compiler/dex/quick/codegen_util.cc
+++ b/compiler/dex/quick/codegen_util.cc
@@ -24,6 +24,28 @@
 
 namespace art {
 
+namespace {
+
+/* Dump a mapping table */
+template <typename It>
+void DumpMappingTable(const char* table_name, const char* descriptor, const char* name,
+                      const Signature& signature, uint32_t size, It first) {
+  if (size != 0) {
+    std::string line(StringPrintf("\n  %s %s%s_%s_table[%zu] = {", table_name,
+                     descriptor, name, signature.ToString().c_str(), size));
+    std::replace(line.begin(), line.end(), ';', '_');
+    LOG(INFO) << line;
+    for (uint32_t i = 0; i != size; ++i) {
+      line = StringPrintf("    {0x%05x, 0x%04x},", first.NativePcOffset(), first.DexPc());
+      ++first;
+      LOG(INFO) << line;
+    }
+    LOG(INFO) <<"  };\n\n";
+  }
+}
+
+}  // anonymous namespace
+
 bool Mir2Lir::IsInexpensiveConstant(RegLocation rl_src) {
   bool res = false;
   if (rl_src.is_const) {
@@ -251,23 +273,6 @@
   }
 }
 
-/* Dump a mapping table */
-void Mir2Lir::DumpMappingTable(const char* table_name, const char* descriptor,
-                               const char* name, const Signature& signature,
-                               const std::vector<uint32_t>& v) {
-  if (v.size() > 0) {
-    std::string line(StringPrintf("\n  %s %s%s_%s_table[%zu] = {", table_name,
-                     descriptor, name, signature.ToString().c_str(), v.size()));
-    std::replace(line.begin(), line.end(), ';', '_');
-    LOG(INFO) << line;
-    for (uint32_t i = 0; i < v.size(); i+=2) {
-      line = StringPrintf("    {0x%05x, 0x%04x},", v[i], v[i+1]);
-      LOG(INFO) << line;
-    }
-    LOG(INFO) <<"  };\n\n";
-  }
-}
-
 /* Dump instructions and constant pool contents */
 void Mir2Lir::CodegenDump() {
   LOG(INFO) << "Dumping LIR insns for "
@@ -302,8 +307,13 @@
   const char* descriptor(cu_->dex_file->GetMethodDeclaringClassDescriptor(method_id));
 
   // Dump mapping tables
-  DumpMappingTable("PC2Dex_MappingTable", descriptor, name, signature, pc2dex_mapping_table_);
-  DumpMappingTable("Dex2PC_MappingTable", descriptor, name, signature, dex2pc_mapping_table_);
+  if (!encoded_mapping_table_.empty()) {
+    MappingTable table(&encoded_mapping_table_[0]);
+    DumpMappingTable("PC2Dex_MappingTable", descriptor, name, signature,
+                     table.PcToDexSize(), table.PcToDexBegin());
+    DumpMappingTable("Dex2PC_MappingTable", descriptor, name, signature,
+                     table.DexToPcSize(), table.DexToPcBegin());
+  }
 }
 
 /*
@@ -522,34 +532,34 @@
 
 // Make sure we have a code address for every declared catch entry
 bool Mir2Lir::VerifyCatchEntries() {
+  MappingTable table(&encoded_mapping_table_[0]);
+  std::vector<uint32_t> dex_pcs;
+  dex_pcs.reserve(table.DexToPcSize());
+  for (auto it = table.DexToPcBegin(), end = table.DexToPcEnd(); it != end; ++it) {
+    dex_pcs.push_back(it.DexPc());
+  }
+  // Sort dex_pcs, so that we can quickly check it against the ordered mir_graph_->catches_.
+  std::sort(dex_pcs.begin(), dex_pcs.end());
+
   bool success = true;
-  for (std::set<uint32_t>::const_iterator it = mir_graph_->catches_.begin();
-       it != mir_graph_->catches_.end(); ++it) {
-    uint32_t dex_pc = *it;
-    bool found = false;
-    for (size_t i = 0; i < dex2pc_mapping_table_.size(); i += 2) {
-      if (dex_pc == dex2pc_mapping_table_[i+1]) {
-        found = true;
-        break;
-      }
+  auto it = dex_pcs.begin(), end = dex_pcs.end();
+  for (uint32_t dex_pc : mir_graph_->catches_) {
+    while (it != end && *it < dex_pc) {
+      LOG(INFO) << "Unexpected catch entry @ dex pc 0x" << std::hex << *it;
+      ++it;
+      success = false;
     }
-    if (!found) {
+    if (it == end || *it > dex_pc) {
       LOG(INFO) << "Missing native PC for catch entry @ 0x" << std::hex << dex_pc;
       success = false;
-    }
-  }
-  // Now, try in the other direction
-  for (size_t i = 0; i < dex2pc_mapping_table_.size(); i += 2) {
-    uint32_t dex_pc = dex2pc_mapping_table_[i+1];
-    if (mir_graph_->catches_.find(dex_pc) == mir_graph_->catches_.end()) {
-      LOG(INFO) << "Unexpected catch entry @ dex pc 0x" << std::hex << dex_pc;
-      success = false;
+    } else {
+      ++it;
     }
   }
   if (!success) {
     LOG(INFO) << "Bad dex2pcMapping table in " << PrettyMethod(cu_->method_idx, *cu_->dex_file);
     LOG(INFO) << "Entries @ decode: " << mir_graph_->catches_.size() << ", Entries in table: "
-              << dex2pc_mapping_table_.size()/2;
+              << table.DexToPcSize();
   }
   return success;
 }
@@ -573,8 +583,6 @@
                                            static_cast<int32_t>(pc2dex_dalvik_offset));
       pc2dex_offset = tgt_lir->offset;
       pc2dex_dalvik_offset = tgt_lir->dalvik_offset;
-      pc2dex_mapping_table_.push_back(tgt_lir->offset);
-      pc2dex_mapping_table_.push_back(tgt_lir->dalvik_offset);
     }
     if (!tgt_lir->flags.is_nop && (tgt_lir->opcode == kPseudoExportedPC)) {
       dex2pc_entries += 1;
@@ -584,63 +592,67 @@
                                            static_cast<int32_t>(dex2pc_dalvik_offset));
       dex2pc_offset = tgt_lir->offset;
       dex2pc_dalvik_offset = tgt_lir->dalvik_offset;
-      dex2pc_mapping_table_.push_back(tgt_lir->offset);
-      dex2pc_mapping_table_.push_back(tgt_lir->dalvik_offset);
     }
   }
-  if (kIsDebugBuild) {
-    CHECK(VerifyCatchEntries());
-  }
-  DCHECK_EQ(pc2dex_mapping_table_.size(), 2u * pc2dex_entries);
-  DCHECK_EQ(dex2pc_mapping_table_.size(), 2u * dex2pc_entries);
 
   uint32_t total_entries = pc2dex_entries + dex2pc_entries;
   uint32_t hdr_data_size = UnsignedLeb128Size(total_entries) + UnsignedLeb128Size(pc2dex_entries);
   uint32_t data_size = hdr_data_size + pc2dex_data_size + dex2pc_data_size;
-  encoded_mapping_table_.Reserve(data_size);
-  encoded_mapping_table_.PushBackUnsigned(total_entries);
-  encoded_mapping_table_.PushBackUnsigned(pc2dex_entries);
+  encoded_mapping_table_.resize(data_size);
+  uint8_t* write_pos = &encoded_mapping_table_[0];
+  write_pos = EncodeUnsignedLeb128(write_pos, total_entries);
+  write_pos = EncodeUnsignedLeb128(write_pos, pc2dex_entries);
+  DCHECK_EQ(static_cast<size_t>(write_pos - &encoded_mapping_table_[0]), hdr_data_size);
+  uint8_t* write_pos2 = write_pos + pc2dex_data_size;
 
-  dex2pc_offset = 0u;
-  dex2pc_dalvik_offset = 0u;
   pc2dex_offset = 0u;
   pc2dex_dalvik_offset = 0u;
-  for (uint32_t i = 0; i != pc2dex_entries; ++i) {
-    encoded_mapping_table_.PushBackUnsigned(pc2dex_mapping_table_[2 * i] - pc2dex_offset);
-    encoded_mapping_table_.PushBackSigned(static_cast<int32_t>(pc2dex_mapping_table_[2 * i + 1]) -
-                                          static_cast<int32_t>(pc2dex_dalvik_offset));
-    pc2dex_offset = pc2dex_mapping_table_[2 * i];
-    pc2dex_dalvik_offset = pc2dex_mapping_table_[2 * i + 1];
+  dex2pc_offset = 0u;
+  dex2pc_dalvik_offset = 0u;
+  for (LIR* tgt_lir = first_lir_insn_; tgt_lir != NULL; tgt_lir = NEXT_LIR(tgt_lir)) {
+    if (!tgt_lir->flags.is_nop && (tgt_lir->opcode == kPseudoSafepointPC)) {
+      DCHECK(pc2dex_offset <= tgt_lir->offset);
+      write_pos = EncodeUnsignedLeb128(write_pos, tgt_lir->offset - pc2dex_offset);
+      write_pos = EncodeSignedLeb128(write_pos, static_cast<int32_t>(tgt_lir->dalvik_offset) -
+                                     static_cast<int32_t>(pc2dex_dalvik_offset));
+      pc2dex_offset = tgt_lir->offset;
+      pc2dex_dalvik_offset = tgt_lir->dalvik_offset;
+    }
+    if (!tgt_lir->flags.is_nop && (tgt_lir->opcode == kPseudoExportedPC)) {
+      DCHECK(dex2pc_offset <= tgt_lir->offset);
+      write_pos2 = EncodeUnsignedLeb128(write_pos2, tgt_lir->offset - dex2pc_offset);
+      write_pos2 = EncodeSignedLeb128(write_pos2, static_cast<int32_t>(tgt_lir->dalvik_offset) -
+                                      static_cast<int32_t>(dex2pc_dalvik_offset));
+      dex2pc_offset = tgt_lir->offset;
+      dex2pc_dalvik_offset = tgt_lir->dalvik_offset;
+    }
   }
-  DCHECK(encoded_mapping_table_.GetData().size() == hdr_data_size + pc2dex_data_size);
-  for (uint32_t i = 0; i != dex2pc_entries; ++i) {
-    encoded_mapping_table_.PushBackUnsigned(dex2pc_mapping_table_[2 * i] - dex2pc_offset);
-    encoded_mapping_table_.PushBackSigned(static_cast<int32_t>(dex2pc_mapping_table_[2 * i + 1]) -
-                                          static_cast<int32_t>(dex2pc_dalvik_offset));
-    dex2pc_offset = dex2pc_mapping_table_[2 * i];
-    dex2pc_dalvik_offset = dex2pc_mapping_table_[2 * i + 1];
-  }
-  DCHECK(encoded_mapping_table_.GetData().size() == data_size);
+  DCHECK_EQ(static_cast<size_t>(write_pos - &encoded_mapping_table_[0]),
+            hdr_data_size + pc2dex_data_size);
+  DCHECK_EQ(static_cast<size_t>(write_pos2 - &encoded_mapping_table_[0]), data_size);
 
   if (kIsDebugBuild) {
+    CHECK(VerifyCatchEntries());
+
     // Verify the encoded table holds the expected data.
-    MappingTable table(&encoded_mapping_table_.GetData()[0]);
+    MappingTable table(&encoded_mapping_table_[0]);
     CHECK_EQ(table.TotalSize(), total_entries);
     CHECK_EQ(table.PcToDexSize(), pc2dex_entries);
-    CHECK_EQ(table.DexToPcSize(), dex2pc_mapping_table_.size() / 2);
     auto it = table.PcToDexBegin();
-    for (uint32_t i = 0; i < pc2dex_mapping_table_.size(); ++i, ++it) {
-      CHECK_EQ(pc2dex_mapping_table_.at(i), it.NativePcOffset());
-      ++i;
-      CHECK_EQ(pc2dex_mapping_table_.at(i), it.DexPc());
+    auto it2 = table.DexToPcBegin();
+    for (LIR* tgt_lir = first_lir_insn_; tgt_lir != NULL; tgt_lir = NEXT_LIR(tgt_lir)) {
+      if (!tgt_lir->flags.is_nop && (tgt_lir->opcode == kPseudoSafepointPC)) {
+        CHECK_EQ(tgt_lir->offset, it.NativePcOffset());
+        CHECK_EQ(tgt_lir->dalvik_offset, it.DexPc());
+        ++it;
+      }
+      if (!tgt_lir->flags.is_nop && (tgt_lir->opcode == kPseudoExportedPC)) {
+        CHECK_EQ(tgt_lir->offset, it2.NativePcOffset());
+        CHECK_EQ(tgt_lir->dalvik_offset, it2.DexPc());
+        ++it2;
+      }
     }
     CHECK(it == table.PcToDexEnd());
-    auto it2 = table.DexToPcBegin();
-    for (uint32_t i = 0; i < dex2pc_mapping_table_.size(); ++i, ++it2) {
-      CHECK_EQ(dex2pc_mapping_table_.at(i), it2.NativePcOffset());
-      ++i;
-      CHECK_EQ(dex2pc_mapping_table_.at(i), it2.DexPc());
-    }
     CHECK(it2 == table.DexToPcEnd());
   }
 }
@@ -724,10 +736,11 @@
 };
 
 void Mir2Lir::CreateNativeGcMap() {
-  const std::vector<uint32_t>& mapping_table = pc2dex_mapping_table_;
+  DCHECK(!encoded_mapping_table_.empty());
+  MappingTable mapping_table(&encoded_mapping_table_[0]);
   uint32_t max_native_offset = 0;
-  for (size_t i = 0; i < mapping_table.size(); i += 2) {
-    uint32_t native_offset = mapping_table[i + 0];
+  for (auto it = mapping_table.PcToDexBegin(), end = mapping_table.PcToDexEnd(); it != end; ++it) {
+    uint32_t native_offset = it.NativePcOffset();
     if (native_offset > max_native_offset) {
       max_native_offset = native_offset;
     }
@@ -737,12 +750,12 @@
   verifier::DexPcToReferenceMap dex_gc_map(&(*gc_map_raw)[4], gc_map_raw->size() - 4);
   // Compute native offset to references size.
   NativePcToReferenceMapBuilder native_gc_map_builder(&native_gc_map_,
-                                                      mapping_table.size() / 2, max_native_offset,
-                                                      dex_gc_map.RegWidth());
+                                                      mapping_table.PcToDexSize(),
+                                                      max_native_offset, dex_gc_map.RegWidth());
 
-  for (size_t i = 0; i < mapping_table.size(); i += 2) {
-    uint32_t native_offset = mapping_table[i + 0];
-    uint32_t dex_pc = mapping_table[i + 1];
+  for (auto it = mapping_table.PcToDexBegin(), end = mapping_table.PcToDexEnd(); it != end; ++it) {
+    uint32_t native_offset = it.NativePcOffset();
+    uint32_t dex_pc = it.DexPc();
     const uint8_t* references = dex_gc_map.FindBitMap(dex_pc, false);
     CHECK(references != NULL) << "Missing ref for dex pc 0x" << std::hex << dex_pc;
     native_gc_map_builder.AddEntry(native_offset, references);
@@ -1041,7 +1054,7 @@
   }
   CompiledMethod* result =
       new CompiledMethod(*cu_->compiler_driver, cu_->instruction_set, code_buffer_, frame_size_,
-                         core_spill_mask_, fp_spill_mask_, encoded_mapping_table_.GetData(),
+                         core_spill_mask_, fp_spill_mask_, encoded_mapping_table_,
                          vmap_encoder.GetData(), native_gc_map_);
   return result;
 }
diff --git a/compiler/dex/quick/mir_to_lir.h b/compiler/dex/quick/mir_to_lir.h
index 92e21ff..fae6c4c 100644
--- a/compiler/dex/quick/mir_to_lir.h
+++ b/compiler/dex/quick/mir_to_lir.h
@@ -341,9 +341,6 @@
     bool EvaluateBranch(Instruction::Code opcode, int src1, int src2);
     bool IsInexpensiveConstant(RegLocation rl_src);
     ConditionCode FlipComparisonOrder(ConditionCode before);
-    void DumpMappingTable(const char* table_name, const char* descriptor,
-                          const char* name, const Signature& signature,
-                          const std::vector<uint32_t>& v);
     void InstallLiteralPools();
     void InstallSwitchTables();
     void InstallFillArrayData();
@@ -792,17 +789,6 @@
     GrowableArray<RegisterInfo*> tempreg_info_;
     GrowableArray<RegisterInfo*> reginfo_map_;
     GrowableArray<void*> pointer_storage_;
-    /*
-     * Holds mapping from native PC to dex PC for safepoints where we may deoptimize.
-     * Native PC is on the return address of the safepointed operation.  Dex PC is for
-     * the instruction being executed at the safepoint.
-     */
-    std::vector<uint32_t> pc2dex_mapping_table_;
-    /*
-     * Holds mapping from Dex PC to native PC for catch entry points.  Native PC and Dex PC
-     * immediately preceed the instruction.
-     */
-    std::vector<uint32_t> dex2pc_mapping_table_;
     CodeOffset current_code_offset_;    // Working byte offset of machine instructons.
     CodeOffset data_offset_;            // starting offset of literal pool.
     size_t total_size_;                   // header + code size.
@@ -828,7 +814,7 @@
     int live_sreg_;
     CodeBuffer code_buffer_;
     // The encoding mapping table data (dex -> pc offset and pc offset -> dex) with a size prefix.
-    Leb128EncodingVector encoded_mapping_table_;
+    std::vector<uint8_t> encoded_mapping_table_;
     std::vector<uint32_t> core_vmap_table_;
     std::vector<uint32_t> fp_vmap_table_;
     std::vector<uint8_t> native_gc_map_;
diff --git a/compiler/leb128_encoder.h b/compiler/leb128_encoder.h
index fe38c2f..6766683 100644
--- a/compiler/leb128_encoder.h
+++ b/compiler/leb128_encoder.h
@@ -22,6 +22,31 @@
 
 namespace art {
 
+static inline uint8_t* EncodeUnsignedLeb128(uint8_t* dest, uint32_t value) {
+  uint8_t out = value & 0x7f;
+  value >>= 7;
+  while (value != 0) {
+    *dest++ = out | 0x80;
+    out = value & 0x7f;
+    value >>= 7;
+  }
+  *dest++ = out;
+  return dest;
+}
+
+static inline uint8_t* EncodeSignedLeb128(uint8_t* dest, int32_t value) {
+  uint32_t extra_bits = static_cast<uint32_t>(value ^ (value >> 31)) >> 6;
+  uint8_t out = value & 0x7f;
+  while (extra_bits != 0u) {
+    *dest++ = out | 0x80;
+    value >>= 7;
+    out = value & 0x7f;
+    extra_bits >>= 7;
+  }
+  *dest++ = out;
+  return dest;
+}
+
 // An encoder with an API similar to vector<uint32_t> where the data is captured in ULEB128 format.
 class Leb128EncodingVector {
  public:
diff --git a/compiler/leb128_encoder_test.cc b/compiler/leb128_encoder_test.cc
index 3162ca5..c63dfa2 100644
--- a/compiler/leb128_encoder_test.cc
+++ b/compiler/leb128_encoder_test.cc
@@ -92,11 +92,12 @@
     {(-1) << 31, {0x80, 0x80, 0x80, 0x80, 0x78}},
 };
 
-TEST_F(Leb128Test, UnsignedSingles) {
+TEST_F(Leb128Test, UnsignedSinglesVector) {
   // Test individual encodings.
   for (size_t i = 0; i < arraysize(uleb128_tests); ++i) {
     Leb128EncodingVector builder;
     builder.PushBackUnsigned(uleb128_tests[i].decoded);
+    EXPECT_EQ(UnsignedLeb128Size(uleb128_tests[i].decoded), builder.GetData().size());
     const uint8_t* data_ptr = &uleb128_tests[i].leb128_data[0];
     const uint8_t* encoded_data_ptr = &builder.GetData()[0];
     for (size_t j = 0; j < 5; ++j) {
@@ -110,7 +111,26 @@
   }
 }
 
-TEST_F(Leb128Test, UnsignedStream) {
+TEST_F(Leb128Test, UnsignedSingles) {
+  // Test individual encodings.
+  for (size_t i = 0; i < arraysize(uleb128_tests); ++i) {
+    uint8_t encoded_data[5];
+    uint8_t* end = EncodeUnsignedLeb128(encoded_data, uleb128_tests[i].decoded);
+    size_t data_size = static_cast<size_t>(end - encoded_data);
+    EXPECT_EQ(UnsignedLeb128Size(uleb128_tests[i].decoded), data_size);
+    const uint8_t* data_ptr = &uleb128_tests[i].leb128_data[0];
+    for (size_t j = 0; j < 5; ++j) {
+      if (j < data_size) {
+        EXPECT_EQ(data_ptr[j], encoded_data[j]) << " i = " << i << " j = " << j;
+      } else {
+        EXPECT_EQ(data_ptr[j], 0U) << " i = " << i << " j = " << j;
+      }
+    }
+    EXPECT_EQ(DecodeUnsignedLeb128(&data_ptr), uleb128_tests[i].decoded) << " i = " << i;
+  }
+}
+
+TEST_F(Leb128Test, UnsignedStreamVector) {
   // Encode a number of entries.
   Leb128EncodingVector builder;
   for (size_t i = 0; i < arraysize(uleb128_tests); ++i) {
@@ -119,20 +139,46 @@
   const uint8_t* encoded_data_ptr = &builder.GetData()[0];
   for (size_t i = 0; i < arraysize(uleb128_tests); ++i) {
     const uint8_t* data_ptr = &uleb128_tests[i].leb128_data[0];
-    for (size_t j = 0; j < 5; ++j) {
-      if (data_ptr[j] != 0) {
-        EXPECT_EQ(data_ptr[j], encoded_data_ptr[j]) << " i = " << i << " j = " << j;
-      }
+    for (size_t j = 0; j < UnsignedLeb128Size(uleb128_tests[i].decoded); ++j) {
+      EXPECT_EQ(data_ptr[j], encoded_data_ptr[j]) << " i = " << i << " j = " << j;
+    }
+    for (size_t j = UnsignedLeb128Size(uleb128_tests[i].decoded); j < 5; ++j) {
+      EXPECT_EQ(data_ptr[j], 0) << " i = " << i << " j = " << j;
     }
     EXPECT_EQ(DecodeUnsignedLeb128(&encoded_data_ptr), uleb128_tests[i].decoded) << " i = " << i;
   }
+  EXPECT_EQ(builder.GetData().size(),
+            static_cast<size_t>(encoded_data_ptr - &builder.GetData()[0]));
 }
 
-TEST_F(Leb128Test, SignedSingles) {
+TEST_F(Leb128Test, UnsignedStream) {
+  // Encode a number of entries.
+  uint8_t encoded_data[5 * arraysize(uleb128_tests)];
+  uint8_t* end = encoded_data;
+  for (size_t i = 0; i < arraysize(uleb128_tests); ++i) {
+    end = EncodeUnsignedLeb128(end, uleb128_tests[i].decoded);
+  }
+  size_t data_size = static_cast<size_t>(end - encoded_data);
+  const uint8_t* encoded_data_ptr = encoded_data;
+  for (size_t i = 0; i < arraysize(uleb128_tests); ++i) {
+    const uint8_t* data_ptr = &uleb128_tests[i].leb128_data[0];
+    for (size_t j = 0; j < UnsignedLeb128Size(uleb128_tests[i].decoded); ++j) {
+      EXPECT_EQ(data_ptr[j], encoded_data_ptr[j]) << " i = " << i << " j = " << j;
+    }
+    for (size_t j = UnsignedLeb128Size(uleb128_tests[i].decoded); j < 5; ++j) {
+      EXPECT_EQ(data_ptr[j], 0) << " i = " << i << " j = " << j;
+    }
+    EXPECT_EQ(DecodeUnsignedLeb128(&encoded_data_ptr), uleb128_tests[i].decoded) << " i = " << i;
+  }
+  EXPECT_EQ(data_size, static_cast<size_t>(encoded_data_ptr - encoded_data));
+}
+
+TEST_F(Leb128Test, SignedSinglesVector) {
   // Test individual encodings.
   for (size_t i = 0; i < arraysize(sleb128_tests); ++i) {
     Leb128EncodingVector builder;
     builder.PushBackSigned(sleb128_tests[i].decoded);
+    EXPECT_EQ(SignedLeb128Size(sleb128_tests[i].decoded), builder.GetData().size());
     const uint8_t* data_ptr = &sleb128_tests[i].leb128_data[0];
     const uint8_t* encoded_data_ptr = &builder.GetData()[0];
     for (size_t j = 0; j < 5; ++j) {
@@ -146,7 +192,26 @@
   }
 }
 
-TEST_F(Leb128Test, SignedStream) {
+TEST_F(Leb128Test, SignedSingles) {
+  // Test individual encodings.
+  for (size_t i = 0; i < arraysize(sleb128_tests); ++i) {
+    uint8_t encoded_data[5];
+    uint8_t* end = EncodeSignedLeb128(encoded_data, sleb128_tests[i].decoded);
+    size_t data_size = static_cast<size_t>(end - encoded_data);
+    EXPECT_EQ(SignedLeb128Size(sleb128_tests[i].decoded), data_size);
+    const uint8_t* data_ptr = &sleb128_tests[i].leb128_data[0];
+    for (size_t j = 0; j < 5; ++j) {
+      if (j < data_size) {
+        EXPECT_EQ(data_ptr[j], encoded_data[j]) << " i = " << i << " j = " << j;
+      } else {
+        EXPECT_EQ(data_ptr[j], 0U) << " i = " << i << " j = " << j;
+      }
+    }
+    EXPECT_EQ(DecodeSignedLeb128(&data_ptr), sleb128_tests[i].decoded) << " i = " << i;
+  }
+}
+
+TEST_F(Leb128Test, SignedStreamVector) {
   // Encode a number of entries.
   Leb128EncodingVector builder;
   for (size_t i = 0; i < arraysize(sleb128_tests); ++i) {
@@ -155,13 +220,38 @@
   const uint8_t* encoded_data_ptr = &builder.GetData()[0];
   for (size_t i = 0; i < arraysize(sleb128_tests); ++i) {
     const uint8_t* data_ptr = &sleb128_tests[i].leb128_data[0];
-    for (size_t j = 0; j < 5; ++j) {
-      if (data_ptr[j] != 0) {
-        EXPECT_EQ(data_ptr[j], encoded_data_ptr[j]) << " i = " << i << " j = " << j;
-      }
+    for (size_t j = 0; j < SignedLeb128Size(sleb128_tests[i].decoded); ++j) {
+      EXPECT_EQ(data_ptr[j], encoded_data_ptr[j]) << " i = " << i << " j = " << j;
+    }
+    for (size_t j = SignedLeb128Size(sleb128_tests[i].decoded); j < 5; ++j) {
+      EXPECT_EQ(data_ptr[j], 0) << " i = " << i << " j = " << j;
     }
     EXPECT_EQ(DecodeSignedLeb128(&encoded_data_ptr), sleb128_tests[i].decoded) << " i = " << i;
   }
+  EXPECT_EQ(builder.GetData().size(),
+            static_cast<size_t>(encoded_data_ptr - &builder.GetData()[0]));
+}
+
+TEST_F(Leb128Test, SignedStream) {
+  // Encode a number of entries.
+  uint8_t encoded_data[5 * arraysize(sleb128_tests)];
+  uint8_t* end = encoded_data;
+  for (size_t i = 0; i < arraysize(sleb128_tests); ++i) {
+    end = EncodeSignedLeb128(end, sleb128_tests[i].decoded);
+  }
+  size_t data_size = static_cast<size_t>(end - encoded_data);
+  const uint8_t* encoded_data_ptr = encoded_data;
+  for (size_t i = 0; i < arraysize(sleb128_tests); ++i) {
+    const uint8_t* data_ptr = &sleb128_tests[i].leb128_data[0];
+    for (size_t j = 0; j < SignedLeb128Size(sleb128_tests[i].decoded); ++j) {
+      EXPECT_EQ(data_ptr[j], encoded_data_ptr[j]) << " i = " << i << " j = " << j;
+    }
+    for (size_t j = SignedLeb128Size(sleb128_tests[i].decoded); j < 5; ++j) {
+      EXPECT_EQ(data_ptr[j], 0) << " i = " << i << " j = " << j;
+    }
+    EXPECT_EQ(DecodeSignedLeb128(&encoded_data_ptr), sleb128_tests[i].decoded) << " i = " << i;
+  }
+  EXPECT_EQ(data_size, static_cast<size_t>(encoded_data_ptr - encoded_data));
 }
 
 TEST_F(Leb128Test, Speed) {