TrackEvent: Add support for interned state

This patch adds a way for client library users to define their own track event
arguments which will be automatically deduplicated (interned) when writing into
the trace. For example, strings such as event categories and names that are
reused throughout the trace are good candidates for interning.

This patch also rewrites TrackEvent internals to use the new interning
mechanism.

Bug: 132678367
Change-Id: I29a50947a1f7641c3a3ed74b7cc670151ea935e8
diff --git a/src/protozero/scattered_heap_buffer.cc b/src/protozero/scattered_heap_buffer.cc
index 682a059..389c36e 100644
--- a/src/protozero/scattered_heap_buffer.cc
+++ b/src/protozero/scattered_heap_buffer.cc
@@ -20,20 +20,31 @@
 
 namespace protozero {
 
+ScatteredHeapBuffer::Slice::Slice()
+    : buffer_(nullptr), size_(0u), unused_bytes_(0u) {}
+
 ScatteredHeapBuffer::Slice::Slice(size_t size)
     : buffer_(std::unique_ptr<uint8_t[]>(new uint8_t[size])),
       size_(size),
       unused_bytes_(size) {
   PERFETTO_DCHECK(size);
-#if PERFETTO_DCHECK_IS_ON()
-  memset(start(), 0xff, size_);
-#endif  // PERFETTO_DCHECK_IS_ON()
+  Clear();
 }
 
 ScatteredHeapBuffer::Slice::Slice(Slice&& slice) noexcept = default;
 
 ScatteredHeapBuffer::Slice::~Slice() = default;
 
+ScatteredHeapBuffer::Slice& ScatteredHeapBuffer::Slice::operator=(Slice&&) =
+    default;
+
+void ScatteredHeapBuffer::Slice::Clear() {
+  unused_bytes_ = size_;
+#if PERFETTO_DCHECK_IS_ON()
+  memset(start(), 0xff, size_);
+#endif  // PERFETTO_DCHECK_IS_ON()
+}
+
 ScatteredHeapBuffer::ScatteredHeapBuffer(size_t initial_slice_size_bytes,
                                          size_t maximum_slice_size_bytes)
     : next_slice_size_(initial_slice_size_bytes),
@@ -48,7 +59,12 @@
   PERFETTO_CHECK(writer_);
   AdjustUsedSizeOfCurrentSlice();
 
-  slices_.emplace_back(next_slice_size_);
+  if (cached_slice_.start()) {
+    slices_.push_back(std::move(cached_slice_));
+    PERFETTO_DCHECK(!cached_slice_.start());
+  } else {
+    slices_.emplace_back(next_slice_size_);
+  }
   next_slice_size_ = std::min(maximum_slice_size_, next_slice_size_ * 2);
   return slices_.back().GetTotalRange();
 }
@@ -63,6 +79,14 @@
   return buffer;
 }
 
+std::vector<protozero::ContiguousMemoryRange> ScatteredHeapBuffer::GetRanges() {
+  AdjustUsedSizeOfCurrentSlice();
+  std::vector<protozero::ContiguousMemoryRange> ranges;
+  for (const auto& slice : slices_)
+    ranges.push_back(slice.GetUsedRange());
+  return ranges;
+}
+
 void ScatteredHeapBuffer::AdjustUsedSizeOfCurrentSlice() {
   if (!slices_.empty())
     slices_.back().set_unused_bytes(writer_->bytes_available());
@@ -76,4 +100,12 @@
   return total_size;
 }
 
+void ScatteredHeapBuffer::Reset() {
+  if (slices_.empty())
+    return;
+  cached_slice_ = std::move(slices_.front());
+  cached_slice_.Clear();
+  slices_.clear();
+}
+
 }  // namespace protozero