Merge "protozero: avoid trivial heap expansions"
diff --git a/include/perfetto/protozero/proto_decoder.h b/include/perfetto/protozero/proto_decoder.h
index ce5bb56..c8e46dd 100644
--- a/include/perfetto/protozero/proto_decoder.h
+++ b/include/perfetto/protozero/proto_decoder.h
@@ -357,7 +357,10 @@
       : ProtoDecoder(buffer, length),
         fields_(storage),
         num_fields_(num_fields),
-        size_(std::min(num_fields, capacity)),
+        // The reason for "capacity -1" is to avoid hitting the expansion path
+        // in TypedProtoDecoderBase::ParseAllFields() when we are just setting
+        // fields < INITIAL_STACK_CAPACITY (which is the most common case).
+        size_(std::min(num_fields, capacity - 1)),
         capacity_(capacity) {
     // The reason why Field needs to be trivially de/constructible is to avoid
     // implicit initializers on all the ~1000 entries. We need it to initialize
@@ -368,6 +371,7 @@
                       std::is_trivial<Field>::value,
                   "Field must be a trivial aggregate type");
     memset(fields_, 0, sizeof(Field) * capacity_);
+    PERFETTO_DCHECK(capacity > 0);
   }
 
   void ParseAllFields();
@@ -398,7 +402,7 @@
   uint32_t num_fields_;
 
   // Number of active |fields_| entries. This is initially equal to
-  // min(num_fields_, INITIAL_STACK_CAPACITY) and after ExpandHeapStorage() it
+  // min(num_fields_, INITIAL_STACK_CAPACITY - 1) and after ExpandHeapStorage()
   // becomes == |num_fields_|. If the message has non-packed repeated fields, it
   // can grow further, up to |capacity_|.
   // |size_| is always <= |capacity_|. But |num_fields_| can be > |size_|.
diff --git a/src/protozero/proto_decoder.cc b/src/protozero/proto_decoder.cc
index c13be9a..a766a28 100644
--- a/src/protozero/proto_decoder.cc
+++ b/src/protozero/proto_decoder.cc
@@ -233,14 +233,17 @@
 
 void TypedProtoDecoderBase::ExpandHeapStorage() {
   // When we expand the heap we must ensure that we have at very last capacity
-  // to deal with all known fields plus at least one repeated field. We go +32
-  // to avoid trivial re-allocations when dealing with repeated fields of a
-  // message that has > INITIAL_STACK_CAPACITY fields.
-  const uint32_t min_capacity = num_fields_ + 32;  // Any number >= +1 will do.
+  // to deal with all known fields plus at least one repeated field. We go +2048
+  // here based on observations on a large 4GB android trace. This is to avoid
+  // trivial re-allocations when dealing with repeated fields of a message that
+  // has > INITIAL_STACK_CAPACITY fields.
+  const uint32_t min_capacity = num_fields_ + 2048;  // Any num >= +1 will do.
   const uint32_t new_capacity = std::max(capacity_ * 2, min_capacity);
   PERFETTO_CHECK(new_capacity > size_ && new_capacity > num_fields_);
   std::unique_ptr<Field[]> new_storage(new Field[new_capacity]);
 
+  static_assert(std::is_trivially_constructible<Field>::value,
+                "Field must be trivially constructible");
   static_assert(std::is_trivially_copyable<Field>::value,
                 "Field must be trivially copyable");