Pivot source files into top-level src,include and unify test target

See discussion in go/perfetto-build-files .
This is to disambiguate things like
  #include "base/logging.h"
when in the chrome tree.

Also this CL unifies the test targets into two monolithic targets:
perfetto_tests and perfetto_benchmarks. This is to avoid ending
up with confusing binary names in the chrome tree (e.g.,
ipc_unittests)

Bug: 68710794
Change-Id: I1768e15b661406052b2be060d7aab0f1e7443a98
diff --git a/src/protozero/BUILD.gn b/src/protozero/BUILD.gn
new file mode 100644
index 0000000..885d934
--- /dev/null
+++ b/src/protozero/BUILD.gn
@@ -0,0 +1,77 @@
+# Copyright (C) 2017 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import("../../gn/perfetto.gni")
+import("../../gn/proto_library.gni")
+import("protozero_library.gni")
+
+source_set("protozero") {
+  public_configs = [ "../../gn:default_config" ]
+  public_deps = [
+    "../../include/perfetto/protozero",
+  ]
+  deps = [
+    "../../gn:default_deps",
+    "../../gn:gtest_prod_config",
+    "../base",
+  ]
+  sources = [
+    "proto_utils.cc",
+    "protozero_message.cc",
+    "protozero_message_handle.cc",
+    "scattered_stream_writer.cc",
+  ]
+}
+
+executable("protozero_unittests") {
+  testonly = true
+  deps = [
+    ":protozero",
+    ":testing_messages_lite",
+    ":testing_messages_zero",
+    "../../gn:default_deps",
+    "../../gn:gtest_deps",
+    "../base",
+  ]
+  sources = [
+    "proto_utils_unittest.cc",
+    "protozero_message_unittest.cc",
+    "scattered_stream_writer_unittest.cc",
+    "test/fake_scattered_buffer.cc",
+    "test/fake_scattered_buffer.h",
+    "test/protozero_conformance_unittest.cc",
+  ]
+}
+
+# Generates both xxx.pbzero.h and xxx.pb.h (official proto).
+
+testing_proto_sources = [
+  "test/example_proto/library.proto",
+  "test/example_proto/library_internals/galaxies.proto",
+  "test/example_proto/test_messages.proto",
+  "test/example_proto/upper_import.proto",
+]
+
+protozero_library("testing_messages_zero") {
+  sources = testing_proto_sources
+  proto_in_dir = perfetto_root_path
+  proto_out_dir = "protos_zero"
+  generator_plugin_options = "wrapper_namespace=pbzero"
+}
+
+proto_library("testing_messages_lite") {
+  sources = testing_proto_sources
+  proto_in_dir = perfetto_root_path
+  proto_out_dir = "protos_lite"
+}
diff --git a/src/protozero/README.md b/src/protozero/README.md
new file mode 100644
index 0000000..113961d
--- /dev/null
+++ b/src/protozero/README.md
@@ -0,0 +1,12 @@
+ProtoZero
+---------
+
+ProtoZero is a zero-copy zero-malloc append-only protobuf library.
+It's designed to be fast and efficient at the cost of a reduced API
+surface for generated stubs. The main limitations consist of:
+- Append-only interface: no readbacks are possible from the stubs.
+- No runtime checks for duplicated or missing mandatory fields.
+- Mandatory ordering when writing of nested messages: once a nested message is
+  started it must be completed before adding any fields to its parent.
+
+See also: [Design doc](https://goo.gl/EKvEfa]).
diff --git a/src/protozero/proto_utils.cc b/src/protozero/proto_utils.cc
new file mode 100644
index 0000000..8d1254a
--- /dev/null
+++ b/src/protozero/proto_utils.cc
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "perfetto/protozero/proto_utils.h"
+
+#include <string.h>
+
+#include <limits>
+
+#include "perfetto/base/logging.h"
+
+#define PERFETTO_CHECK_PTR_LE(a, b)                \
+  PERFETTO_CHECK(reinterpret_cast<uintptr_t>(a) <= \
+                 reinterpret_cast<uintptr_t>(b))
+
+namespace protozero {
+namespace proto_utils {
+
+#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
+#define BYTE_SWAP_TO_LE32(x) (x)
+#define BYTE_SWAP_TO_LE64(x) (x)
+#else
+#error Unimplemented for big endian archs.
+#endif
+
+const uint8_t* ParseVarInt(const uint8_t* start,
+                           const uint8_t* end,
+                           uint64_t* value) {
+  const uint8_t* pos = start;
+  uint64_t shift = 0;
+  *value = 0;
+  do {
+    PERFETTO_CHECK_PTR_LE(pos, end - 1);
+    PERFETTO_DCHECK(shift < 64ull);
+    *value |= static_cast<uint64_t>(*pos & 0x7f) << shift;
+    shift += 7;
+  } while (*pos++ & 0x80);
+  return pos;
+}
+
+const uint8_t* ParseField(const uint8_t* start,
+                          const uint8_t* end,
+                          uint32_t* field_id,
+                          FieldType* field_type,
+                          uint64_t* field_intvalue) {
+  // The first byte of a proto field is structured as follows:
+  // The least 3 significant bits determine the field type.
+  // The most 5 significant bits determine the field id. If MSB == 1, the
+  // field id continues on the next bytes following the VarInt encoding.
+  const uint8_t kFieldTypeNumBits = 3;
+  const uint8_t kFieldTypeMask = (1 << kFieldTypeNumBits) - 1;  // 0000 0111;
+
+  const uint8_t* pos = start;
+  PERFETTO_CHECK_PTR_LE(pos, end - 1);
+  *field_type = static_cast<FieldType>(*pos & kFieldTypeMask);
+
+  uint64_t raw_field_id;
+  pos = ParseVarInt(pos, end, &raw_field_id);
+  raw_field_id >>= kFieldTypeNumBits;
+
+  PERFETTO_DCHECK(raw_field_id <= std::numeric_limits<uint32_t>::max());
+  *field_id = static_cast<uint32_t>(raw_field_id);
+
+  switch (*field_type) {
+    case kFieldTypeFixed64: {
+      PERFETTO_CHECK_PTR_LE(pos + sizeof(uint64_t), end);
+      memcpy(field_intvalue, pos, sizeof(uint64_t));
+      *field_intvalue = BYTE_SWAP_TO_LE64(*field_intvalue);
+      pos += sizeof(uint64_t);
+      break;
+    }
+    case kFieldTypeFixed32: {
+      PERFETTO_CHECK_PTR_LE(pos + sizeof(uint32_t), end);
+      uint32_t tmp;
+      memcpy(&tmp, pos, sizeof(uint32_t));
+      *field_intvalue = BYTE_SWAP_TO_LE32(tmp);
+      pos += sizeof(uint32_t);
+      break;
+    }
+    case kFieldTypeVarInt: {
+      pos = ParseVarInt(pos, end, field_intvalue);
+      break;
+    }
+    case kFieldTypeLengthDelimited: {
+      pos = ParseVarInt(pos, end, field_intvalue);
+      pos += *field_intvalue;
+      PERFETTO_CHECK_PTR_LE(pos, end);
+      break;
+    }
+  }
+  return pos;
+}
+
+}  // namespace proto_utils
+}  // namespace protozero
diff --git a/src/protozero/proto_utils_unittest.cc b/src/protozero/proto_utils_unittest.cc
new file mode 100644
index 0000000..2ad6677
--- /dev/null
+++ b/src/protozero/proto_utils_unittest.cc
@@ -0,0 +1,206 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "perfetto/protozero/proto_utils.h"
+
+#include <limits>
+
+#include "gtest/gtest.h"
+#include "perfetto/base/logging.h"
+#include "perfetto/base/utils.h"
+
+namespace protozero {
+namespace proto_utils {
+namespace {
+
+using ::perfetto::base::ArraySize;
+
+struct VarIntExpectation {
+  const char* encoded;
+  size_t encoded_size;
+  uint64_t int_value;
+};
+
+const VarIntExpectation kVarIntExpectations[] = {
+    {"\x00", 1, 0},
+    {"\x01", 1, 0x1},
+    {"\x7f", 1, 0x7F},
+    {"\xFF\x01", 2, 0xFF},
+    {"\xFF\x7F", 2, 0x3FFF},
+    {"\x80\x80\x01", 3, 0x4000},
+    {"\xFF\xFF\x7F", 3, 0x1FFFFF},
+    {"\x80\x80\x80\x01", 4, 0x200000},
+    {"\xFF\xFF\xFF\x7F", 4, 0xFFFFFFF},
+    {"\x80\x80\x80\x80\x01", 5, 0x10000000},
+    {"\xFF\xFF\xFF\xFF\x0F", 5, 0xFFFFFFFF},
+    {"\x80\x80\x80\x80\x10", 5, 0x100000000},
+    {"\xFF\xFF\xFF\xFF\x7F", 5, 0x7FFFFFFFF},
+    {"\x80\x80\x80\x80\x80\x01", 6, 0x800000000},
+    {"\xFF\xFF\xFF\xFF\xFF\x7F", 6, 0x3FFFFFFFFFF},
+    {"\x80\x80\x80\x80\x80\x80\x01", 7, 0x40000000000},
+    {"\xFF\xFF\xFF\xFF\xFF\xFF\x7F", 7, 0x1FFFFFFFFFFFF},
+    {"\x80\x80\x80\x80\x80\x80\x80\x01", 8, 0x2000000000000},
+    {"\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x7F", 8, 0xFFFFFFFFFFFFFF},
+    {"\x80\x80\x80\x80\x80\x80\x80\x80\x01", 9, 0x100000000000000},
+    {"\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x7F", 9, 0x7FFFFFFFFFFFFFFF},
+    {"\x80\x80\x80\x80\x80\x80\x80\x80\x80\x01", 10, 0x8000000000000000},
+    {"\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x01", 10, 0xFFFFFFFFFFFFFFFF},
+};
+
+TEST(ProtoUtilsTest, FieldPreambleEncoding) {
+  // According to C++ standard, right shift of negative value has
+  // implementation-defined resulting value.
+  if ((static_cast<int32_t>(0x80000000u) >> 31) != -1)
+    FAIL() << "Platform has unsupported negative number format or arithmetic";
+
+  EXPECT_EQ(0x08u, MakeTagVarInt(1));
+  EXPECT_EQ(0x09u, MakeTagFixed<uint64_t>(1));
+  EXPECT_EQ(0x0Au, MakeTagLengthDelimited(1));
+  EXPECT_EQ(0x0Du, MakeTagFixed<uint32_t>(1));
+
+  EXPECT_EQ(0x03F8u, MakeTagVarInt(0x7F));
+  EXPECT_EQ(0x03F9u, MakeTagFixed<int64_t>(0x7F));
+  EXPECT_EQ(0x03FAu, MakeTagLengthDelimited(0x7F));
+  EXPECT_EQ(0x03FDu, MakeTagFixed<int32_t>(0x7F));
+
+  EXPECT_EQ(0x0400u, MakeTagVarInt(0x80));
+  EXPECT_EQ(0x0401u, MakeTagFixed<double>(0x80));
+  EXPECT_EQ(0x0402u, MakeTagLengthDelimited(0x80));
+  EXPECT_EQ(0x0405u, MakeTagFixed<float>(0x80));
+
+  EXPECT_EQ(0x01FFF8u, MakeTagVarInt(0x3fff));
+  EXPECT_EQ(0x01FFF9u, MakeTagFixed<int64_t>(0x3fff));
+  EXPECT_EQ(0x01FFFAu, MakeTagLengthDelimited(0x3fff));
+  EXPECT_EQ(0x01FFFDu, MakeTagFixed<int32_t>(0x3fff));
+
+  EXPECT_EQ(0x020000u, MakeTagVarInt(0x4000));
+  EXPECT_EQ(0x020001u, MakeTagFixed<int64_t>(0x4000));
+  EXPECT_EQ(0x020002u, MakeTagLengthDelimited(0x4000));
+  EXPECT_EQ(0x020005u, MakeTagFixed<int32_t>(0x4000));
+}
+
+TEST(ProtoUtilsTest, ZigZagEncoding) {
+  EXPECT_EQ(0u, ZigZagEncode(0));
+  EXPECT_EQ(1u, ZigZagEncode(-1));
+  EXPECT_EQ(2u, ZigZagEncode(1));
+  EXPECT_EQ(3u, ZigZagEncode(-2));
+  EXPECT_EQ(4294967293u, ZigZagEncode(-2147483647));
+  EXPECT_EQ(4294967294u, ZigZagEncode(2147483647));
+  EXPECT_EQ(std::numeric_limits<uint32_t>::max(),
+            ZigZagEncode(std::numeric_limits<int32_t>::min()));
+  EXPECT_EQ(std::numeric_limits<uint64_t>::max(),
+            ZigZagEncode(std::numeric_limits<int64_t>::min()));
+}
+
+TEST(ProtoUtilsTest, VarIntEncoding) {
+  for (size_t i = 0; i < ArraySize(kVarIntExpectations); ++i) {
+    const VarIntExpectation& exp = kVarIntExpectations[i];
+    uint8_t buf[32];
+    uint8_t* res = WriteVarInt<uint64_t>(exp.int_value, buf);
+    ASSERT_EQ(exp.encoded_size, static_cast<size_t>(res - buf));
+    ASSERT_EQ(0, memcmp(buf, exp.encoded, exp.encoded_size));
+
+    if (exp.int_value <= std::numeric_limits<uint32_t>::max()) {
+      uint8_t* res_32 =
+          WriteVarInt<uint32_t>(static_cast<uint32_t>(exp.int_value), buf);
+      ASSERT_EQ(exp.encoded_size, static_cast<size_t>(res_32 - buf));
+      ASSERT_EQ(0, memcmp(buf, exp.encoded, exp.encoded_size));
+    }
+  }
+}
+
+TEST(ProtoUtilsTest, RedundantVarIntEncoding) {
+  uint8_t buf[kMessageLengthFieldSize];
+
+  WriteRedundantVarInt(0, buf);
+  EXPECT_EQ(0, memcmp("\x80\x80\x80\x00", buf, sizeof(buf)));
+
+  WriteRedundantVarInt(1, buf);
+  EXPECT_EQ(0, memcmp("\x81\x80\x80\x00", buf, sizeof(buf)));
+
+  WriteRedundantVarInt(0x80, buf);
+  EXPECT_EQ(0, memcmp("\x80\x81\x80\x00", buf, sizeof(buf)));
+
+  WriteRedundantVarInt(0x332211, buf);
+  EXPECT_EQ(0, memcmp("\x91\xC4\xCC\x01", buf, sizeof(buf)));
+
+  // Largest allowed length.
+  WriteRedundantVarInt(0x0FFFFFFF, buf);
+  EXPECT_EQ(0, memcmp("\xFF\xFF\xFF\x7F", buf, sizeof(buf)));
+}
+
+TEST(ProtoUtilsTest, VarIntDecoding) {
+  for (size_t i = 0; i < ArraySize(kVarIntExpectations); ++i) {
+    const VarIntExpectation& exp = kVarIntExpectations[i];
+    uint64_t value = std::numeric_limits<uint64_t>::max();
+    const uint8_t* res = ParseVarInt(
+        reinterpret_cast<const uint8_t*>(exp.encoded),
+        reinterpret_cast<const uint8_t*>(exp.encoded + exp.encoded_size),
+        &value);
+    ASSERT_EQ(reinterpret_cast<const void*>(exp.encoded + exp.encoded_size),
+              reinterpret_cast<const void*>(res));
+    ASSERT_EQ(exp.int_value, value);
+  }
+}
+
+TEST(ProtoUtilsTest, FieldDecoding) {
+  struct FieldExpectation {
+    const char* encoded;
+    size_t encoded_size;
+    uint32_t id;
+    FieldType type;
+    uint64_t int_value;
+  };
+
+  const FieldExpectation kFieldExpectations[] = {
+      {"\x08\x00", 2, 1, kFieldTypeVarInt, 0},
+      {"\x08\x42", 2, 1, kFieldTypeVarInt, 0x42},
+      {"\xF8\x07\x42", 3, 127, kFieldTypeVarInt, 0x42},
+      {"\x90\x4D\xFF\xFF\xFF\xFF\x0F", 7, 1234, kFieldTypeVarInt, 0xFFFFFFFF},
+      {"\x7D\x42\x00\x00\x00", 5, 15, kFieldTypeFixed32, 0x42},
+      {"\x95\x4D\x78\x56\x34\x12", 6, 1234, kFieldTypeFixed32, 0x12345678},
+      {"\x79\x42\x00\x00\x00\x00\x00\x00\x00", 9, 15, kFieldTypeFixed64, 0x42},
+      {"\x91\x4D\x08\x07\x06\x05\x04\x03\x02\x01", 10, 1234, kFieldTypeFixed64,
+       0x0102030405060708},
+      {"\x0A\x00", 2, 1, kFieldTypeLengthDelimited, 0},
+      {"\x0A\x04|abc", 6, 1, kFieldTypeLengthDelimited, 4},
+      {"\x92\x4D\x04|abc", 7, 1234, kFieldTypeLengthDelimited, 4},
+      {"\x92\x4D\x83\x01|abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzab"
+       "cdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstu"
+       "vwx",
+       135, 1234, kFieldTypeLengthDelimited, 131},
+  };
+
+  for (size_t i = 0; i < ArraySize(kFieldExpectations); ++i) {
+    const FieldExpectation& exp = kFieldExpectations[i];
+    FieldType field_type = kFieldTypeVarInt;
+    uint32_t field_id = std::numeric_limits<uint32_t>::max();
+    uint64_t field_intvalue = std::numeric_limits<uint64_t>::max();
+    const uint8_t* res = ParseField(
+        reinterpret_cast<const uint8_t*>(exp.encoded),
+        reinterpret_cast<const uint8_t*>(exp.encoded + exp.encoded_size),
+        &field_id, &field_type, &field_intvalue);
+    ASSERT_EQ(reinterpret_cast<const void*>(exp.encoded + exp.encoded_size),
+              reinterpret_cast<const void*>(res));
+    ASSERT_EQ(exp.id, field_id);
+    ASSERT_EQ(exp.type, field_type);
+    ASSERT_EQ(exp.int_value, field_intvalue);
+  }
+}
+
+}  // namespace
+}  // namespace proto_utils
+}  // namespace protozero
diff --git a/src/protozero/protoc_plugin/BUILD.gn b/src/protozero/protoc_plugin/BUILD.gn
new file mode 100644
index 0000000..ca56cea
--- /dev/null
+++ b/src/protozero/protoc_plugin/BUILD.gn
@@ -0,0 +1,31 @@
+# Copyright (C) 2017 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+if (current_toolchain == host_toolchain) {
+  executable("protoc_plugin") {
+    sources = [
+      "protozero_generator.cc",
+      "protozero_generator.h",
+      "protozero_plugin.cc",
+    ]
+    deps = [
+      "../../../gn:default_deps",
+      "../../../gn:protoc_lib_deps",
+    ]
+    if (is_clang) {
+      # Internal protobuf headers hit this.
+      cflags = [ "-Wno-unreachable-code" ]
+    }
+  }
+}  # host_toolchain
diff --git a/src/protozero/protoc_plugin/protozero_generator.cc b/src/protozero/protoc_plugin/protozero_generator.cc
new file mode 100644
index 0000000..9a3ead9
--- /dev/null
+++ b/src/protozero/protoc_plugin/protozero_generator.cc
@@ -0,0 +1,651 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "src/protozero/protoc_plugin/protozero_generator.h"
+
+#include <map>
+#include <memory>
+#include <set>
+#include <string>
+
+#include "google/protobuf/descriptor.h"
+#include "google/protobuf/io/printer.h"
+#include "google/protobuf/io/zero_copy_stream.h"
+#include "google/protobuf/stubs/strutil.h"
+
+namespace protozero {
+
+using google::protobuf::Descriptor;  // Message descriptor.
+using google::protobuf::EnumDescriptor;
+using google::protobuf::EnumValueDescriptor;
+using google::protobuf::FieldDescriptor;
+using google::protobuf::FileDescriptor;
+using google::protobuf::compiler::GeneratorContext;
+using google::protobuf::io::Printer;
+using google::protobuf::io::ZeroCopyOutputStream;
+
+using google::protobuf::Split;
+using google::protobuf::StripPrefixString;
+using google::protobuf::StripString;
+using google::protobuf::StripSuffixString;
+using google::protobuf::UpperString;
+
+namespace {
+
+inline std::string ProtoStubName(const FileDescriptor* proto) {
+  return StripSuffixString(proto->name(), ".proto") + ".pbzero";
+}
+
+class GeneratorJob {
+ public:
+  GeneratorJob(const FileDescriptor* file,
+               Printer* stub_h_printer,
+               Printer* stub_cc_printer)
+      : source_(file), stub_h_(stub_h_printer), stub_cc_(stub_cc_printer) {}
+
+  bool GenerateStubs() {
+    Preprocess();
+    GeneratePrologue();
+    for (const EnumDescriptor* enumeration : enums_)
+      GenerateEnumDescriptor(enumeration);
+    for (const Descriptor* message : messages_)
+      GenerateMessageDescriptor(message);
+    GenerateEpilogue();
+    return error_.empty();
+  }
+
+  void SetOption(const std::string& name, const std::string& value) {
+    if (name == "wrapper_namespace") {
+      wrapper_namespace_ = value;
+    } else {
+      Abort(std::string() + "Unknown plugin option '" + name + "'.");
+    }
+  }
+
+  // If generator fails to produce stubs for a particular proto definitions
+  // it finishes with undefined output and writes the first error occured.
+  const std::string& GetFirstError() const { return error_; }
+
+ private:
+  // Only the first error will be recorded.
+  void Abort(const std::string& reason) {
+    if (error_.empty())
+      error_ = reason;
+  }
+
+  // Get full name (including outer descriptors) of proto descriptor.
+  template <class T>
+  inline std::string GetDescriptorName(const T* descriptor) {
+    if (!package_.empty()) {
+      return StripPrefixString(descriptor->full_name(), package_ + ".");
+    } else {
+      return descriptor->full_name();
+    }
+  }
+
+  // Get C++ class name corresponding to proto descriptor.
+  // Nested names are splitted by underscores. Underscores in type names aren't
+  // prohibited but not recommended in order to avoid name collisions.
+  template <class T>
+  inline std::string GetCppClassName(const T* descriptor, bool full = false) {
+    std::string name = GetDescriptorName(descriptor);
+    StripString(&name, ".", '_');
+    if (full)
+      name = full_namespace_prefix_ + name;
+    return name;
+  }
+
+  inline std::string GetFieldNumberConstant(const FieldDescriptor* field) {
+    std::string name = field->camelcase_name();
+    if (!name.empty()) {
+      name.at(0) = static_cast<char>(toupper(name.at(0)));
+      name = "k" + name + "FieldNumber";
+    } else {
+      // Protoc allows fields like 'bool _ = 1'.
+      Abort("Empty field name in camel case notation.");
+    }
+    return name;
+  }
+
+  // Small enums can be written faster without involving VarInt encoder.
+  inline bool IsTinyEnumField(const FieldDescriptor* field) {
+    if (field->type() != FieldDescriptor::TYPE_ENUM)
+      return false;
+    const EnumDescriptor* enumeration = field->enum_type();
+
+    for (int i = 0; i < enumeration->value_count(); ++i) {
+      int32_t value = enumeration->value(i)->number();
+      if (value < 0 || value > 0x7F)
+        return false;
+    }
+    return true;
+  }
+
+  void CollectDescriptors() {
+    // Collect message descriptors in DFS order.
+    std::vector<const Descriptor*> stack;
+    for (int i = 0; i < source_->message_type_count(); ++i)
+      stack.push_back(source_->message_type(i));
+
+    while (!stack.empty()) {
+      const Descriptor* message = stack.back();
+      stack.pop_back();
+      messages_.push_back(message);
+      for (int i = 0; i < message->nested_type_count(); ++i) {
+        stack.push_back(message->nested_type(i));
+      }
+    }
+
+    // Collect enums.
+    for (int i = 0; i < source_->enum_type_count(); ++i)
+      enums_.push_back(source_->enum_type(i));
+
+    for (const Descriptor* message : messages_) {
+      for (int i = 0; i < message->enum_type_count(); ++i) {
+        enums_.push_back(message->enum_type(i));
+      }
+    }
+  }
+
+  void CollectDependencies() {
+    // Public import basically means that callers only need to import this
+    // proto in order to use the stuff publicly imported by this proto.
+    for (int i = 0; i < source_->public_dependency_count(); ++i)
+      public_imports_.insert(source_->public_dependency(i));
+
+    if (source_->weak_dependency_count() > 0)
+      Abort("Weak imports are not supported.");
+
+    // Sanity check. Collect public imports (of collected imports) in DFS order.
+    // Visibilty for current proto:
+    // - all imports listed in current proto,
+    // - public imports of everything imported (recursive).
+    std::vector<const FileDescriptor*> stack;
+    for (int i = 0; i < source_->dependency_count(); ++i) {
+      const FileDescriptor* import = source_->dependency(i);
+      stack.push_back(import);
+      if (public_imports_.count(import) == 0) {
+        private_imports_.insert(import);
+      }
+    }
+
+    while (!stack.empty()) {
+      const FileDescriptor* import = stack.back();
+      stack.pop_back();
+      // Having imports under different packages leads to unnecessary
+      // complexity with namespaces.
+      if (import->package() != package_)
+        Abort("Imported proto must be in the same package.");
+
+      for (int i = 0; i < import->public_dependency_count(); ++i) {
+        stack.push_back(import->public_dependency(i));
+      }
+    }
+
+    // Collect descriptors of messages and enums used in current proto.
+    // It will be used to generate necessary forward declarations and performed
+    // sanity check guarantees that everything lays in the same namespace.
+    for (const Descriptor* message : messages_) {
+      for (int i = 0; i < message->field_count(); ++i) {
+        const FieldDescriptor* field = message->field(i);
+
+        if (field->type() == FieldDescriptor::TYPE_MESSAGE) {
+          if (public_imports_.count(field->message_type()->file()) == 0) {
+            // Avoid multiple forward declarations since
+            // public imports have been already included.
+            referenced_messages_.insert(field->message_type());
+          }
+        } else if (field->type() == FieldDescriptor::TYPE_ENUM) {
+          if (public_imports_.count(field->enum_type()->file()) == 0) {
+            referenced_enums_.insert(field->enum_type());
+          }
+        }
+      }
+    }
+  }
+
+  void Preprocess() {
+    // Package name maps to a series of namespaces.
+    package_ = source_->package();
+    namespaces_ = Split(package_, ".");
+    if (!wrapper_namespace_.empty())
+      namespaces_.push_back(wrapper_namespace_);
+
+    full_namespace_prefix_ = "::";
+    for (const std::string& ns : namespaces_)
+      full_namespace_prefix_ += ns + "::";
+
+    CollectDescriptors();
+    CollectDependencies();
+  }
+
+  // Print top header, namespaces and forward declarations.
+  void GeneratePrologue() {
+    std::string greeting =
+        "// Autogenerated by the ProtoZero compiler plugin. DO NOT EDIT.\n";
+    std::string guard = package_ + "_" + source_->name() + "_H_";
+    UpperString(&guard);
+    StripString(&guard, ".-/\\", '_');
+
+    stub_h_->Print(
+        "$greeting$\n"
+        "#ifndef $guard$\n"
+        "#define $guard$\n\n"
+        "#include <stddef.h>\n"
+        "#include <stdint.h>\n\n"
+        "#include \"perfetto/protozero/proto_field_descriptor.h\"\n"
+        "#include \"perfetto/protozero/protozero_message.h\"\n",
+        "greeting", greeting, "guard", guard);
+    stub_cc_->Print(
+        "$greeting$\n"
+        "#include \"$name$.h\"\n",
+        "greeting", greeting, "name", ProtoStubName(source_));
+
+    // Print includes for public imports.
+    for (const FileDescriptor* dependency : public_imports_) {
+      // Dependency name could contain slashes but importing from upper-level
+      // directories is not possible anyway since build system processes each
+      // proto file individually. Hence proto lookup path is always equal to the
+      // directory where particular proto file is located and protoc does not
+      // allow reference to upper directory (aka ..) in import path.
+      //
+      // Laconically said:
+      // - source_->name() may never have slashes,
+      // - dependency->name() may have slashes but always refers to inner path.
+      stub_h_->Print("#include \"$name$.h\"\n", "name",
+                     ProtoStubName(dependency));
+    }
+    stub_h_->Print("\n");
+
+    // Print includes for private imports to .cc file.
+    for (const FileDescriptor* dependency : private_imports_) {
+      stub_cc_->Print("#include \"$name$.h\"\n", "name",
+                      ProtoStubName(dependency));
+    }
+    stub_cc_->Print("\n");
+
+    if (messages_.size() > 0) {
+      stub_cc_->Print(
+          "namespace {\n"
+          "  static const ::protozero::ProtoFieldDescriptor "
+          "kInvalidField = {\"\", "
+          "::protozero::ProtoFieldDescriptor::Type::TYPE_INVALID, "
+          "0, false};\n"
+          "}\n\n");
+    }
+
+    // Print namespaces.
+    for (const std::string& ns : namespaces_) {
+      stub_h_->Print("namespace $ns$ {\n", "ns", ns);
+      stub_cc_->Print("namespace $ns$ {\n", "ns", ns);
+    }
+    stub_h_->Print("\n");
+    stub_cc_->Print("\n");
+
+    // Print forward declarations.
+    for (const Descriptor* message : referenced_messages_) {
+      stub_h_->Print("class $class$;\n", "class", GetCppClassName(message));
+    }
+    for (const EnumDescriptor* enumeration : referenced_enums_) {
+      stub_h_->Print("enum $class$ : int32_t;\n", "class",
+                     GetCppClassName(enumeration));
+    }
+    stub_h_->Print("\n");
+  }
+
+  void GenerateEnumDescriptor(const EnumDescriptor* enumeration) {
+    stub_h_->Print("enum $class$ : int32_t {\n", "class",
+                   GetCppClassName(enumeration));
+    stub_h_->Indent();
+
+    std::string value_name_prefix;
+    if (enumeration->containing_type() != nullptr)
+      value_name_prefix = GetCppClassName(enumeration) + "_";
+
+    for (int i = 0; i < enumeration->value_count(); ++i) {
+      const EnumValueDescriptor* value = enumeration->value(i);
+      stub_h_->Print("$name$ = $number$,\n", "name",
+                     value_name_prefix + value->name(), "number",
+                     std::to_string(value->number()));
+    }
+
+    stub_h_->Outdent();
+    stub_h_->Print("};\n\n");
+  }
+
+  void GenerateSimpleFieldDescriptor(const FieldDescriptor* field) {
+    std::map<std::string, std::string> setter;
+    setter["id"] = std::to_string(field->number());
+    setter["name"] = field->name();
+    setter["action"] = field->is_repeated() ? "add" : "set";
+
+    std::string appender;
+    std::string cpp_type;
+
+    switch (field->type()) {
+      case FieldDescriptor::TYPE_BOOL: {
+        appender = "AppendTinyVarInt";
+        cpp_type = "bool";
+        break;
+      }
+      case FieldDescriptor::TYPE_INT32: {
+        appender = "AppendVarInt";
+        cpp_type = "int32_t";
+        break;
+      }
+      case FieldDescriptor::TYPE_INT64: {
+        appender = "AppendVarInt";
+        cpp_type = "int64_t";
+        break;
+      }
+      case FieldDescriptor::TYPE_UINT32: {
+        appender = "AppendVarInt";
+        cpp_type = "uint32_t";
+        break;
+      }
+      case FieldDescriptor::TYPE_UINT64: {
+        appender = "AppendVarInt";
+        cpp_type = "uint64_t";
+        break;
+      }
+      case FieldDescriptor::TYPE_SINT32: {
+        appender = "AppendSignedVarInt";
+        cpp_type = "int32_t";
+        break;
+      }
+      case FieldDescriptor::TYPE_SINT64: {
+        appender = "AppendSignedVarInt";
+        cpp_type = "int64_t";
+        break;
+      }
+      case FieldDescriptor::TYPE_FIXED32: {
+        appender = "AppendFixed";
+        cpp_type = "uint32_t";
+        break;
+      }
+      case FieldDescriptor::TYPE_FIXED64: {
+        appender = "AppendFixed";
+        cpp_type = "uint64_t";
+        break;
+      }
+      case FieldDescriptor::TYPE_SFIXED32: {
+        appender = "AppendFixed";
+        cpp_type = "int32_t";
+        break;
+      }
+      case FieldDescriptor::TYPE_SFIXED64: {
+        appender = "AppendFixed";
+        cpp_type = "int64_t";
+        break;
+      }
+      case FieldDescriptor::TYPE_FLOAT: {
+        appender = "AppendFixed";
+        cpp_type = "float";
+        break;
+      }
+      case FieldDescriptor::TYPE_DOUBLE: {
+        appender = "AppendFixed";
+        cpp_type = "double";
+        break;
+      }
+      case FieldDescriptor::TYPE_ENUM: {
+        appender = IsTinyEnumField(field) ? "AppendTinyVarInt" : "AppendVarInt";
+        cpp_type = GetCppClassName(field->enum_type(), true);
+        break;
+      }
+      case FieldDescriptor::TYPE_STRING: {
+        appender = "AppendString";
+        cpp_type = "const char*";
+        break;
+      }
+      case FieldDescriptor::TYPE_BYTES: {
+        stub_h_->Print(
+            setter,
+            "void $action$_$name$(const uint8_t* data, size_t size) {\n"
+            "  AppendBytes($id$, data, size);\n"
+            "}\n");
+        return;
+      }
+      case FieldDescriptor::TYPE_GROUP:
+      case FieldDescriptor::TYPE_MESSAGE: {
+        Abort("Unsupported field type.");
+        return;
+      }
+    }
+    setter["appender"] = appender;
+    setter["cpp_type"] = cpp_type;
+    stub_h_->Print(setter,
+                   "void $action$_$name$($cpp_type$ value) {\n"
+                   "  $appender$($id$, value);\n"
+                   "}\n");
+
+    // For strings also generate a variant for non-null terminated strings.
+    if (field->type() == FieldDescriptor::TYPE_STRING) {
+      stub_h_->Print(setter,
+                     "// Doesn't check for null terminator.\n"
+                     "// Expects |value| to be at least |size| long.\n"
+                     "void $action$_$name$($cpp_type$ value, size_t size) {\n"
+                     "  AppendBytes($id$, value, size);\n"
+                     "}\n");
+    }
+  }
+
+  void GenerateNestedMessageFieldDescriptor(const FieldDescriptor* field) {
+    std::string action = field->is_repeated() ? "add" : "set";
+    std::string inner_class = GetCppClassName(field->message_type());
+    std::string outer_class = GetCppClassName(field->containing_type());
+
+    stub_h_->Print("$inner_class$* $action$_$name$();\n", "name", field->name(),
+                   "action", action, "inner_class", inner_class);
+    stub_cc_->Print(
+        "$inner_class$* $outer_class$::$action$_$name$() {\n"
+        "  return BeginNestedMessage<$inner_class$>($id$);\n"
+        "}\n\n",
+        "id", std::to_string(field->number()), "name", field->name(), "action",
+        action, "inner_class", inner_class, "outer_class", outer_class);
+  }
+
+  void GenerateReflectionForMessageFields(const Descriptor* message) {
+    const bool has_fields = (message->field_count() > 0);
+
+    // Field number constants.
+    if (has_fields) {
+      stub_h_->Print("enum : int32_t {\n");
+      stub_h_->Indent();
+
+      for (int i = 0; i < message->field_count(); ++i) {
+        const FieldDescriptor* field = message->field(i);
+        stub_h_->Print("$name$ = $id$,\n", "name",
+                       GetFieldNumberConstant(field), "id",
+                       std::to_string(field->number()));
+      }
+      stub_h_->Outdent();
+      stub_h_->Print("};\n");
+    }
+
+    // Fields reflection table.
+    stub_h_->Print(
+        "static const ::protozero::ProtoFieldDescriptor* "
+        "GetFieldDescriptor(uint32_t field_id);\n");
+
+    std::string class_name = GetCppClassName(message);
+    if (has_fields) {
+      stub_cc_->Print(
+          "static const ::protozero::ProtoFieldDescriptor "
+          "kFields_$class$[] = {\n",
+          "class", class_name);
+      stub_cc_->Indent();
+      for (int i = 0; i < message->field_count(); ++i) {
+        const FieldDescriptor* field = message->field(i);
+        std::string type_const =
+            std::string("TYPE_") + FieldDescriptor::TypeName(field->type());
+        UpperString(&type_const);
+        stub_cc_->Print(
+            "{\"$name$\", "
+            "::protozero::ProtoFieldDescriptor::Type::$type$, "
+            "$number$, $is_repeated$},\n",
+            "name", field->name(), "type", type_const, "number",
+            std::to_string(field->number()), "is_repeated",
+            std::to_string(field->is_repeated()));
+      }
+      stub_cc_->Outdent();
+      stub_cc_->Print("};\n\n");
+    }
+
+    // Fields reflection getter.
+    stub_cc_->Print(
+        "const ::protozero::ProtoFieldDescriptor* "
+        "$class$::GetFieldDescriptor(uint32_t field_id) {\n",
+        "class", class_name);
+    stub_cc_->Indent();
+    if (has_fields) {
+      stub_cc_->Print("switch (field_id) {\n");
+      stub_cc_->Indent();
+      for (int i = 0; i < message->field_count(); ++i) {
+        stub_cc_->Print(
+            "case $field$:\n"
+            "  return &kFields_$class$[$id$];\n",
+            "class", class_name, "field",
+            GetFieldNumberConstant(message->field(i)), "id", std::to_string(i));
+      }
+      stub_cc_->Print(
+          "default:\n"
+          "  return &kInvalidField;\n");
+      stub_cc_->Outdent();
+      stub_cc_->Print("}\n");
+    } else {
+      stub_cc_->Print("return &kInvalidField;\n");
+    }
+    stub_cc_->Outdent();
+    stub_cc_->Print("}\n\n");
+  }
+
+  void GenerateMessageDescriptor(const Descriptor* message) {
+    stub_h_->Print(
+        "class $name$ : public ::protozero::ProtoZeroMessage {\n"
+        " public:\n",
+        "name", GetCppClassName(message));
+    stub_h_->Indent();
+
+    GenerateReflectionForMessageFields(message);
+
+    // Using statements for nested messages.
+    for (int i = 0; i < message->nested_type_count(); ++i) {
+      const Descriptor* nested_message = message->nested_type(i);
+      stub_h_->Print("using $local_name$ = $global_name$;\n", "local_name",
+                     nested_message->name(), "global_name",
+                     GetCppClassName(nested_message, true));
+    }
+
+    // Using statements for nested enums.
+    for (int i = 0; i < message->enum_type_count(); ++i) {
+      const EnumDescriptor* nested_enum = message->enum_type(i);
+      stub_h_->Print("using $local_name$ = $global_name$;\n", "local_name",
+                     nested_enum->name(), "global_name",
+                     GetCppClassName(nested_enum, true));
+    }
+
+    // Values of nested enums.
+    for (int i = 0; i < message->enum_type_count(); ++i) {
+      const EnumDescriptor* nested_enum = message->enum_type(i);
+      std::string value_name_prefix = GetCppClassName(nested_enum) + "_";
+
+      for (int j = 0; j < nested_enum->value_count(); ++j) {
+        const EnumValueDescriptor* value = nested_enum->value(j);
+        stub_h_->Print("static const $class$ $name$ = $full_name$;\n", "class",
+                       nested_enum->name(), "name", value->name(), "full_name",
+                       value_name_prefix + value->name());
+      }
+    }
+
+    // Field descriptors.
+    for (int i = 0; i < message->field_count(); ++i) {
+      const FieldDescriptor* field = message->field(i);
+      if (field->is_packed()) {
+        Abort("Packed repeated fields are not supported.");
+        return;
+      }
+      if (field->type() != FieldDescriptor::TYPE_MESSAGE) {
+        GenerateSimpleFieldDescriptor(field);
+      } else {
+        GenerateNestedMessageFieldDescriptor(field);
+      }
+    }
+
+    stub_h_->Outdent();
+    stub_h_->Print("};\n\n");
+  }
+
+  void GenerateEpilogue() {
+    for (unsigned i = 0; i < namespaces_.size(); ++i) {
+      stub_h_->Print("} // Namespace.\n");
+      stub_cc_->Print("} // Namespace.\n");
+    }
+    stub_h_->Print("#endif  // Include guard.\n");
+  }
+
+  const FileDescriptor* const source_;
+  Printer* const stub_h_;
+  Printer* const stub_cc_;
+  std::string error_;
+
+  std::string package_;
+  std::string wrapper_namespace_;
+  std::vector<std::string> namespaces_;
+  std::string full_namespace_prefix_;
+  std::vector<const Descriptor*> messages_;
+  std::vector<const EnumDescriptor*> enums_;
+
+  std::set<const FileDescriptor*> public_imports_;
+  std::set<const FileDescriptor*> private_imports_;
+  std::set<const Descriptor*> referenced_messages_;
+  std::set<const EnumDescriptor*> referenced_enums_;
+};
+
+}  // namespace
+
+ProtoZeroGenerator::ProtoZeroGenerator() {}
+
+ProtoZeroGenerator::~ProtoZeroGenerator() {}
+
+bool ProtoZeroGenerator::Generate(const FileDescriptor* file,
+                                  const std::string& options,
+                                  GeneratorContext* context,
+                                  std::string* error) const {
+  const std::unique_ptr<ZeroCopyOutputStream> stub_h_file_stream(
+      context->Open(ProtoStubName(file) + ".h"));
+  const std::unique_ptr<ZeroCopyOutputStream> stub_cc_file_stream(
+      context->Open(ProtoStubName(file) + ".cc"));
+
+  // Variables are delimited by $.
+  Printer stub_h_printer(stub_h_file_stream.get(), '$');
+  Printer stub_cc_printer(stub_cc_file_stream.get(), '$');
+  GeneratorJob job(file, &stub_h_printer, &stub_cc_printer);
+
+  // Parse additional options.
+  for (const std::string& option : Split(options, ",")) {
+    std::vector<std::string> option_pair = Split(option, "=");
+    job.SetOption(option_pair[0], option_pair[1]);
+  }
+
+  if (!job.GenerateStubs()) {
+    *error = job.GetFirstError();
+    return false;
+  }
+  return true;
+}
+
+}  // namespace protozero
diff --git a/src/protozero/protoc_plugin/protozero_generator.h b/src/protozero/protoc_plugin/protozero_generator.h
new file mode 100644
index 0000000..a9f7a70
--- /dev/null
+++ b/src/protozero/protoc_plugin/protozero_generator.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SRC_PROTOZERO_PROTOC_PLUGIN_PROTOZERO_GENERATOR_H_
+#define SRC_PROTOZERO_PROTOC_PLUGIN_PROTOZERO_GENERATOR_H_
+
+#include <string>
+
+#include "google/protobuf/compiler/code_generator.h"
+
+namespace protozero {
+
+class ProtoZeroGenerator : public ::google::protobuf::compiler::CodeGenerator {
+ public:
+  explicit ProtoZeroGenerator();
+  ~ProtoZeroGenerator() override;
+
+  // CodeGenerator implementation
+  bool Generate(const google::protobuf::FileDescriptor* file,
+                const std::string& options,
+                google::protobuf::compiler::GeneratorContext* context,
+                std::string* error) const override;
+};
+
+}  // namespace protozero
+
+#endif  // SRC_PROTOZERO_PROTOC_PLUGIN_PROTOZERO_GENERATOR_H_
diff --git a/src/protozero/protoc_plugin/protozero_plugin.cc b/src/protozero/protoc_plugin/protozero_plugin.cc
new file mode 100644
index 0000000..7d9d6a3
--- /dev/null
+++ b/src/protozero/protoc_plugin/protozero_plugin.cc
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "google/protobuf/compiler/plugin.h"
+#include "src/protozero/protoc_plugin/protozero_generator.h"
+
+int main(int argc, char* argv[]) {
+  ::protozero::ProtoZeroGenerator generator;
+  return google::protobuf::compiler::PluginMain(argc, argv, &generator);
+}
diff --git a/src/protozero/protozero_library.gni b/src/protozero/protozero_library.gni
new file mode 100644
index 0000000..ffaedca
--- /dev/null
+++ b/src/protozero/protozero_library.gni
@@ -0,0 +1,55 @@
+# Copyright (C) 2017 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import("//build_overrides/build.gni")
+
+if (!build_with_chromium) {
+  import("//build/proto_library.gni")
+} else {
+  import("//third_party/protobuf/proto_library.gni")
+}
+
+# Equivalent to proto_library (generation of .h/.cc from .proto files) but
+# enables also generation using the protozero plugin.
+# The generated files will have the .pbzero.{cc,h} suffix, as opposed to the
+# .pb.{cc,h} of the official proto library.
+template("protozero_library") {
+  proto_library(target_name) {
+    perfetto_root_path = invoker.perfetto_root_path
+
+    generate_cc = false
+    generator_plugin_label = perfetto_root_path + "src/protozero/protoc_plugin"
+    generator_plugin_suffix = ".pbzero"
+
+    if (defined(invoker.deps)) {
+      deps = invoker.deps
+    } else {
+      deps = []
+    }
+
+    deps += [ perfetto_root_path + "src/protozero" ]
+
+    forward_variables_from(invoker,
+                           [
+                             "defines",
+                             "generator_plugin_options",
+                             "include_dirs",
+                             "proto_in_dir",
+                             "proto_out_dir",
+                             "sources",
+                             "testonly",
+                             "visibility",
+                           ])
+  }
+}
diff --git a/src/protozero/protozero_message.cc b/src/protozero/protozero_message.cc
new file mode 100644
index 0000000..195b5c2
--- /dev/null
+++ b/src/protozero/protozero_message.cc
@@ -0,0 +1,142 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "perfetto/protozero/protozero_message.h"
+
+#include <type_traits>
+
+#include "perfetto/base/logging.h"
+
+#if __BYTE_ORDER__ != __ORDER_LITTLE_ENDIAN__
+// The memcpy() for float and double below needs to be adjusted if we want to
+// support big endian CPUs. There doesn't seem to be a compelling need today.
+#error Unimplemented for big endian archs.
+#endif
+
+namespace protozero {
+
+// static
+constexpr uint32_t ProtoZeroMessage::kMaxNestingDepth;
+
+// Do NOT put any code in the constructor or use default initialization.
+// Use the Reset() method below instead. See the header for the reason why.
+
+// This method is called to initialize both root and nested messages.
+void ProtoZeroMessage::Reset(ScatteredStreamWriter* stream_writer) {
+// Older versions of libstdcxx don't have is_trivially_constructible.
+#if !defined(__GLIBCXX__) || __GLIBCXX__ >= 20170516
+  static_assert(std::is_trivially_constructible<ProtoZeroMessage>::value,
+                "ProtoZeroMessage must be trivially constructible");
+#endif
+
+  static_assert(std::is_trivially_destructible<ProtoZeroMessage>::value,
+                "ProtoZeroMessage must be trivially destructible");
+
+  static_assert(
+      sizeof(ProtoZeroMessage::nested_messages_arena_) >=
+          kMaxNestingDepth * (sizeof(ProtoZeroMessage) -
+                              sizeof(ProtoZeroMessage::nested_messages_arena_)),
+      "ProtoZeroMessage::nested_messages_arena_ is too small");
+
+  stream_writer_ = stream_writer;
+  size_ = 0;
+  size_field_.reset();
+  nested_message_ = nullptr;
+  nesting_depth_ = 0;
+#if PROTOZERO_ENABLE_HANDLE_DEBUGGING()
+  sealed_ = false;
+  handle_ = nullptr;
+#endif
+}
+
+void ProtoZeroMessage::AppendString(uint32_t field_id, const char* str) {
+  AppendBytes(field_id, str, strlen(str));
+}
+
+void ProtoZeroMessage::AppendBytes(uint32_t field_id,
+                                   const void* src,
+                                   size_t size) {
+  if (nested_message_)
+    EndNestedMessage();
+
+  PERFETTO_DCHECK(size < proto_utils::kMaxMessageLength);
+  // Write the proto preamble (field id, type and length of the field).
+  uint8_t buffer[proto_utils::kMaxSimpleFieldEncodedSize];
+  uint8_t* pos = buffer;
+  pos = proto_utils::WriteVarInt(proto_utils::MakeTagLengthDelimited(field_id),
+                                 pos);
+  pos = proto_utils::WriteVarInt(static_cast<uint32_t>(size), pos);
+  WriteToStream(buffer, pos);
+
+  const uint8_t* src_u8 = reinterpret_cast<const uint8_t*>(src);
+  WriteToStream(src_u8, src_u8 + size);
+}
+
+size_t ProtoZeroMessage::Finalize() {
+  if (nested_message_)
+    EndNestedMessage();
+
+  // Write the length of the nested message a posteriori, using a leading-zero
+  // redundant varint encoding.
+  if (size_field_.is_valid()) {
+#if PROTOZERO_ENABLE_HANDLE_DEBUGGING()
+    PERFETTO_DCHECK(!sealed_);
+#endif
+    PERFETTO_DCHECK(size_ < proto_utils::kMaxMessageLength);
+    PERFETTO_DCHECK(proto_utils::kMessageLengthFieldSize == size_field_.size());
+    proto_utils::WriteRedundantVarInt(static_cast<uint32_t>(size_),
+                                      size_field_.begin);
+    size_field_.reset();
+  }
+
+#if PROTOZERO_ENABLE_HANDLE_DEBUGGING()
+  sealed_ = true;
+  if (handle_)
+    handle_->reset_message();
+#endif
+
+  return size_;
+}
+
+void ProtoZeroMessage::BeginNestedMessageInternal(uint32_t field_id,
+                                                  ProtoZeroMessage* message) {
+  if (nested_message_)
+    EndNestedMessage();
+
+  // Write the proto preamble for the nested message.
+  uint8_t data[proto_utils::kMaxTagEncodedSize];
+  uint8_t* data_end = proto_utils::WriteVarInt(
+      proto_utils::MakeTagLengthDelimited(field_id), data);
+  WriteToStream(data, data_end);
+
+  message->Reset(stream_writer_);
+  PERFETTO_CHECK(nesting_depth_ < kMaxNestingDepth);
+  message->nesting_depth_ = nesting_depth_ + 1;
+
+  // The length of the nested message cannot be known upfront. So right now
+  // just reserve the bytes to encode the size after the nested message is done.
+  message->set_size_field(
+      stream_writer_->ReserveBytes(proto_utils::kMessageLengthFieldSize));
+  size_ += proto_utils::kMessageLengthFieldSize;
+  nested_message_ = message;
+}
+
+void ProtoZeroMessage::EndNestedMessage() {
+  size_ += nested_message_->Finalize();
+  nested_message_ = nullptr;
+}
+
+}  // namespace protozero
diff --git a/src/protozero/protozero_message_handle.cc b/src/protozero/protozero_message_handle.cc
new file mode 100644
index 0000000..3e2b4e7
--- /dev/null
+++ b/src/protozero/protozero_message_handle.cc
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "perfetto/protozero/protozero_message_handle.h"
+
+#include <utility>
+
+#include "perfetto/base/logging.h"
+#include "perfetto/protozero/protozero_message.h"
+
+namespace protozero {
+
+ProtoZeroMessageHandleBase::ProtoZeroMessageHandleBase(
+    ProtoZeroMessage* message)
+    : message_(message) {
+#if PROTOZERO_ENABLE_HANDLE_DEBUGGING()
+  if (message_)
+    message_->set_handle(this);
+#endif
+}
+
+ProtoZeroMessageHandleBase::~ProtoZeroMessageHandleBase() {
+  Finalize();
+}
+
+ProtoZeroMessageHandleBase::ProtoZeroMessageHandleBase(
+    ProtoZeroMessageHandleBase&& other) noexcept {
+  Move(std::move(other));
+}
+
+ProtoZeroMessageHandleBase& ProtoZeroMessageHandleBase::operator=(
+    ProtoZeroMessageHandleBase&& other) {
+  // If the current handle was pointing to a message and is being reset to a new
+  // one, finalize the old message.
+  Finalize();
+  Move(std::move(other));
+  return *this;
+}
+
+void ProtoZeroMessageHandleBase::Finalize() {
+  if (!message_)
+    return;
+  const size_t size = message_->Finalize();
+  if (on_finalize_) {
+    on_finalize_(size);
+    on_finalize_ = nullptr;
+  }
+}
+
+void ProtoZeroMessageHandleBase::Move(ProtoZeroMessageHandleBase&& other) {
+  // In theory other->message_ could be nullptr, if |other| is a handle that has
+  // been std::move-d (and hence empty). There isn't a legitimate use case for
+  // doing so, though. Therefore this case is deliberately ignored (if hit, it
+  // will manifest as a segfault when dereferencing |message_| below) to avoid a
+  // useless null-check.
+  message_ = other.message_;
+  other.message_ = nullptr;
+  on_finalize_ = std::move(other.on_finalize_);
+  other.on_finalize_ = nullptr;
+#if PROTOZERO_ENABLE_HANDLE_DEBUGGING()
+  message_->set_handle(this);
+#endif
+}
+
+}  // namespace protozero
diff --git a/src/protozero/protozero_message_unittest.cc b/src/protozero/protozero_message_unittest.cc
new file mode 100644
index 0000000..076612b
--- /dev/null
+++ b/src/protozero/protozero_message_unittest.cc
@@ -0,0 +1,370 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "perfetto/protozero/protozero_message.h"
+
+#include <limits>
+#include <memory>
+#include <utility>
+#include <vector>
+
+#include "gtest/gtest.h"
+#include "perfetto/base/logging.h"
+#include "src/protozero/test/fake_scattered_buffer.h"
+
+namespace protozero {
+
+namespace {
+
+const size_t kChunkSize = 16;
+const uint8_t kTestBytes[] = {0, 0, 0, 0, 0x42, 1, 0x42, 0xff, 0x42, 0};
+const char kStartWatermark[] = {'a', 'b', 'c', 'd', '1', '2', '3', '\0'};
+const char kEndWatermark[] = {'9', '8', '7', '6', 'z', 'w', 'y', '\0'};
+
+class FakeRootMessage : public ProtoZeroMessage {};
+class FakeChildMessage : public ProtoZeroMessage {};
+
+uint32_t SimpleHash(const std::string& str) {
+  uint32_t hash = 5381;
+  for (char c : str)
+    hash = 33 * hash + static_cast<uint32_t>(c);
+  return hash;
+}
+
+class ProtoZeroMessageTest : public ::testing::Test {
+ public:
+  void SetUp() override {
+    buffer_.reset(new FakeScatteredBuffer(kChunkSize));
+    stream_writer_.reset(new ScatteredStreamWriter(buffer_.get()));
+    readback_pos_ = 0;
+  }
+
+  void TearDown() override {
+    // Check that none of the messages created by the text fixtures below did
+    // under/overflow their heap boundaries.
+    for (std::unique_ptr<uint8_t[]>& mem : messages_) {
+      EXPECT_STREQ(kStartWatermark, reinterpret_cast<char*>(mem.get()));
+      EXPECT_STREQ(kEndWatermark,
+                   reinterpret_cast<char*>(mem.get() + sizeof(kStartWatermark) +
+                                           sizeof(ProtoZeroMessage)));
+      mem.reset();
+    }
+    messages_.clear();
+    stream_writer_.reset();
+    buffer_.reset();
+  }
+
+  FakeRootMessage* NewMessage() {
+    std::unique_ptr<uint8_t[]> mem(
+        new uint8_t[sizeof(kStartWatermark) + sizeof(FakeRootMessage) +
+                    sizeof(kEndWatermark)]);
+    uint8_t* msg_start = mem.get() + sizeof(kStartWatermark);
+    memcpy(mem.get(), kStartWatermark, sizeof(kStartWatermark));
+    memset(msg_start, 0, sizeof(FakeRootMessage));
+    memcpy(msg_start + sizeof(FakeRootMessage), kEndWatermark,
+           sizeof(kEndWatermark));
+    messages_.push_back(std::move(mem));
+    FakeRootMessage* msg = reinterpret_cast<FakeRootMessage*>(msg_start);
+    msg->Reset(stream_writer_.get());
+    return msg;
+  }
+
+  size_t GetNumSerializedBytes() {
+    if (buffer_->chunks().empty())
+      return 0;
+    return buffer_->chunks().size() * kChunkSize -
+           stream_writer_->bytes_available();
+  }
+
+  std::string GetNextSerializedBytes(size_t num_bytes) {
+    size_t old_readback_pos = readback_pos_;
+    readback_pos_ += num_bytes;
+    return buffer_->GetBytesAsString(old_readback_pos, num_bytes);
+  }
+
+  static void BuildNestedMessages(ProtoZeroMessage* msg, uint32_t depth = 0) {
+    for (uint32_t i = 1; i <= 128; ++i)
+      msg->AppendBytes(i, kTestBytes, sizeof(kTestBytes));
+
+    if (depth < ProtoZeroMessage::kMaxNestingDepth) {
+      auto* nested_msg =
+          msg->BeginNestedMessage<FakeChildMessage>(1 + depth * 10);
+      BuildNestedMessages(nested_msg, depth + 1);
+    }
+
+    for (uint32_t i = 129; i <= 256; ++i)
+      msg->AppendVarInt(i, 42);
+
+    if ((depth & 2) == 0)
+      msg->Finalize();
+  }
+
+ private:
+  std::unique_ptr<FakeScatteredBuffer> buffer_;
+  std::unique_ptr<ScatteredStreamWriter> stream_writer_;
+  std::vector<std::unique_ptr<uint8_t[]>> messages_;
+  size_t readback_pos_;
+};
+
+TEST_F(ProtoZeroMessageTest, BasicTypesNoNesting) {
+  ProtoZeroMessage* msg = NewMessage();
+  msg->AppendVarInt(1 /* field_id */, 0);
+  msg->AppendVarInt(2 /* field_id */, std::numeric_limits<uint32_t>::max());
+  msg->AppendVarInt(3 /* field_id */, 42);
+  msg->AppendVarInt(4 /* field_id */, std::numeric_limits<uint64_t>::max());
+  msg->AppendFixed(5 /* field_id */, 3.1415f /* float */);
+  msg->AppendFixed(6 /* field_id */, 3.14159265358979323846 /* double */);
+  msg->AppendBytes(7 /* field_id */, kTestBytes, sizeof(kTestBytes));
+
+  // Field ids > 16 are expected to be varint encoded (preamble > 1 byte)
+  msg->AppendString(257 /* field_id */, "0123456789abcdefABCDEF");
+  msg->AppendSignedVarInt(3 /* field_id */, -21);
+
+  EXPECT_EQ(74u, msg->Finalize());
+  EXPECT_EQ(74u, GetNumSerializedBytes());
+
+  // These lines match the serialization of the Append* calls above.
+  ASSERT_EQ("0800", GetNextSerializedBytes(2));
+  ASSERT_EQ("10FFFFFFFF0F", GetNextSerializedBytes(6));
+  ASSERT_EQ("182A", GetNextSerializedBytes(2));
+  ASSERT_EQ("20FFFFFFFFFFFFFFFFFF01", GetNextSerializedBytes(11));
+  ASSERT_EQ("2D560E4940", GetNextSerializedBytes(5));
+  ASSERT_EQ("31182D4454FB210940", GetNextSerializedBytes(9));
+  ASSERT_EQ("3A0A00000000420142FF4200", GetNextSerializedBytes(12));
+  ASSERT_EQ("8A101630313233343536373839616263646566414243444546",
+            GetNextSerializedBytes(25));
+  ASSERT_EQ("1829", GetNextSerializedBytes(2));
+}
+
+TEST_F(ProtoZeroMessageTest, NestedMessagesSimple) {
+  ProtoZeroMessage* root_msg = NewMessage();
+  root_msg->AppendVarInt(1 /* field_id */, 1);
+
+  FakeChildMessage* nested_msg =
+      root_msg->BeginNestedMessage<FakeChildMessage>(128 /* field_id */);
+  ASSERT_EQ(0u, reinterpret_cast<uintptr_t>(nested_msg) % sizeof(void*));
+  nested_msg->AppendVarInt(2 /* field_id */, 2);
+
+  nested_msg =
+      root_msg->BeginNestedMessage<FakeChildMessage>(129 /* field_id */);
+  nested_msg->AppendVarInt(4 /* field_id */, 2);
+
+  root_msg->AppendVarInt(5 /* field_id */, 3);
+
+  // The expected size of the root message is supposed to be 20 bytes:
+  //   2 bytes for the varint field (id: 1) (1 for preamble and one for payload)
+  //   6 bytes for the preamble of the 1st nested message (2 for id, 4 for size)
+  //   2 bytes for the varint field (id: 2) of the 1st nested message
+  //   6 bytes for the premable of the 2nd nested message
+  //   2 bytes for the varint field (id: 4) of the 2nd nested message.
+  //   2 bytes for the last varint (id : 5) field of the root message.
+  // Test also that finalization is idempontent and Finalize() can be safely
+  // called more than once without side effects.
+  for (int i = 0; i < 3; ++i) {
+    EXPECT_EQ(20u, root_msg->Finalize());
+    EXPECT_EQ(20u, GetNumSerializedBytes());
+  }
+
+  ASSERT_EQ("0801", GetNextSerializedBytes(2));
+
+  ASSERT_EQ("820882808000", GetNextSerializedBytes(6));
+  ASSERT_EQ("1002", GetNextSerializedBytes(2));
+
+  ASSERT_EQ("8A0882808000", GetNextSerializedBytes(6));
+  ASSERT_EQ("2002", GetNextSerializedBytes(2));
+
+  ASSERT_EQ("2803", GetNextSerializedBytes(2));
+}
+
+// Checks that the size field of root and nested messages is properly written
+// on finalization.
+TEST_F(ProtoZeroMessageTest, BackfillSizeOnFinalization) {
+  ProtoZeroMessage* root_msg = NewMessage();
+  uint8_t root_msg_size[proto_utils::kMessageLengthFieldSize] = {};
+  root_msg->set_size_field(
+      {&root_msg_size[0],
+       &root_msg_size[proto_utils::kMessageLengthFieldSize]});
+  root_msg->AppendVarInt(1, 0x42);
+
+  FakeChildMessage* nested_msg_1 =
+      root_msg->BeginNestedMessage<FakeChildMessage>(2);
+  nested_msg_1->AppendVarInt(3, 0x43);
+
+  FakeChildMessage* nested_msg_2 =
+      nested_msg_1->BeginNestedMessage<FakeChildMessage>(4);
+  uint8_t buf200[200];
+  memset(buf200, 0x42, sizeof(buf200));
+  nested_msg_2->AppendBytes(5, buf200, sizeof(buf200));
+
+  // The value returned by Finalize() should be == the full size of |root_msg|.
+  EXPECT_EQ(217u, root_msg->Finalize());
+  EXPECT_EQ(217u, GetNumSerializedBytes());
+
+  // However the size written in the size field should take into account the
+  // inc_size_already_written() call and be equal to 118 - 6 = 112, encoded
+  // in a rendundant varint encoding of kMessageLengthFieldSize bytes.
+  EXPECT_STREQ("\xD9\x81\x80\x00", reinterpret_cast<char*>(root_msg_size));
+
+  // Skip 2 bytes for the 0x42 varint + 1 byte for the |nested_msg_1| preamble.
+  GetNextSerializedBytes(3);
+
+  // Check that the size of |nested_msg_1| was backfilled. Its size is:
+  // 203 bytes for |nest_mesg_2| (see below) + 5 bytes for its preamble +
+  // 2 bytes for the 0x43 varint = 210 bytes.
+  EXPECT_EQ("D2818000", GetNextSerializedBytes(4));
+
+  // Skip 2 bytes for the 0x43 varint + 1 byte for the |nested_msg_2| preamble.
+  GetNextSerializedBytes(3);
+
+  // Check that the size of |nested_msg_2| was backfilled. Its size is:
+  // 200 bytes (for |buf200|) + 3 bytes for its preamble = 203 bytes.
+  EXPECT_EQ("CB818000", GetNextSerializedBytes(4));
+}
+
+TEST_F(ProtoZeroMessageTest, StressTest) {
+  std::vector<ProtoZeroMessage*> nested_msgs;
+
+  ProtoZeroMessage* root_msg = NewMessage();
+  BuildNestedMessages(root_msg);
+  root_msg->Finalize();
+
+  // The main point of this test is to stress the code paths and test for
+  // unexpected crashes of the production code. The actual serialization is
+  // already covered in the other text fixtures. Keeping just a final smoke test
+  // here on the full buffer hash.
+  std::string full_buf = GetNextSerializedBytes(GetNumSerializedBytes());
+  size_t buf_hash = SimpleHash(full_buf);
+  EXPECT_EQ(0xfd19cc0a, buf_hash);
+}
+
+TEST_F(ProtoZeroMessageTest, MessageHandle) {
+  FakeRootMessage* msg1 = NewMessage();
+  FakeRootMessage* msg2 = NewMessage();
+  FakeRootMessage* msg3 = NewMessage();
+  FakeRootMessage* ignored_msg = NewMessage();
+  uint8_t msg1_size[proto_utils::kMessageLengthFieldSize] = {};
+  uint8_t msg2_size[proto_utils::kMessageLengthFieldSize] = {};
+  uint8_t msg3_size[proto_utils::kMessageLengthFieldSize] = {};
+  msg1->set_size_field(
+      {&msg1_size[0], &msg1_size[proto_utils::kMessageLengthFieldSize]});
+  msg2->set_size_field(
+      {&msg2_size[0], &msg2_size[proto_utils::kMessageLengthFieldSize]});
+  msg3->set_size_field(
+      {&msg3_size[0], &msg3_size[proto_utils::kMessageLengthFieldSize]});
+
+  // Test that the handle going out of scope causes the finalization of the
+  // target message and triggers the optional callback.
+  size_t callback_arg = 0;
+  {
+    ProtoZeroMessageHandle<FakeRootMessage> handle1(msg1);
+    handle1.set_on_finalize([&callback_arg](size_t sz) { callback_arg = sz; });
+    handle1->AppendBytes(1 /* field_id */, kTestBytes, 1 /* size */);
+    ASSERT_EQ(0u, msg1_size[0]);
+  }
+  ASSERT_EQ(0x83u, msg1_size[0]);
+  ASSERT_EQ(3u, callback_arg);
+
+  // Test that the handle can be late initialized.
+  ProtoZeroMessageHandle<FakeRootMessage> handle2(ignored_msg);
+  handle2 = ProtoZeroMessageHandle<FakeRootMessage>(msg2);
+  handle2->AppendBytes(1 /* field_id */, kTestBytes, 2 /* size */);
+  ASSERT_EQ(0u, msg2_size[0]);  // |msg2| should not be finalized yet.
+
+  // Test that std::move works and does NOT cause finalization of the moved
+  // message.
+  ProtoZeroMessageHandle<FakeRootMessage> handle_swp(ignored_msg);
+  handle_swp = std::move(handle2);
+  ASSERT_EQ(0u, msg2_size[0]);  // msg2 should be NOT finalized yet.
+  handle_swp->AppendBytes(2 /* field_id */, kTestBytes, 3 /* size */);
+
+  ProtoZeroMessageHandle<FakeRootMessage> handle3(msg3);
+  handle3->AppendBytes(1 /* field_id */, kTestBytes, 4 /* size */);
+  ASSERT_EQ(0u, msg3_size[0]);  // msg2 should be NOT finalized yet.
+  callback_arg = 0;
+  handle3.set_on_finalize([&callback_arg](size_t sz) { callback_arg = sz; });
+
+  // Both |handle3| and |handle_swp| point to a valid message (respectively,
+  // |msg3| and |msg2|). Now move |handle3| into |handle_swp|.
+  handle_swp = std::move(handle3);
+  ASSERT_EQ(0x89u, msg2_size[0]);  // |msg2| should be finalized at this point.
+
+  // At this point writing into handle_swp should actually write into |msg3|.
+  ASSERT_EQ(msg3, &*handle_swp);
+  handle_swp->AppendBytes(2 /* field_id */, kTestBytes, 8 /* size */);
+  ProtoZeroMessageHandle<FakeRootMessage> another_handle(ignored_msg);
+  ASSERT_EQ(0u, callback_arg);
+  handle_swp = std::move(another_handle);
+  ASSERT_EQ(0x90u, msg3_size[0]);  // |msg3| should be finalized at this point.
+  ASSERT_EQ(0x10u, callback_arg);
+
+#if PROTOZERO_ENABLE_HANDLE_DEBUGGING()
+  // In developer builds w/ PERFETTO_DCHECK on a finalized message should
+  // invalidate the handle, in order to early catch bugs in the client code.
+  FakeRootMessage* msg4 = NewMessage();
+  ProtoZeroMessageHandle<FakeRootMessage> handle4(msg4);
+  ASSERT_EQ(msg4, &*handle4);
+  msg4->Finalize();
+  ASSERT_EQ(nullptr, &*handle4);
+#endif
+
+  // Test also the behavior of handle with non-root (nested) messages.
+
+  ContiguousMemoryRange size_msg_2;
+  {
+    auto* nested_msg_1 = NewMessage()->BeginNestedMessage<FakeChildMessage>(3);
+    ProtoZeroMessageHandle<FakeChildMessage> child_handle_1(nested_msg_1);
+    ContiguousMemoryRange size_msg_1 = nested_msg_1->size_field();
+    memset(size_msg_1.begin, 0, size_msg_1.size());
+    child_handle_1->AppendVarInt(1, 0x11);
+
+    auto* nested_msg_2 = NewMessage()->BeginNestedMessage<FakeChildMessage>(2);
+    size_msg_2 = nested_msg_2->size_field();
+    memset(size_msg_2.begin, 0, size_msg_2.size());
+    ProtoZeroMessageHandle<FakeChildMessage> child_handle_2(nested_msg_2);
+    child_handle_2->AppendVarInt(2, 0xFF);
+
+    // |nested_msg_1| should not be finalized yet.
+    ASSERT_EQ(0u, size_msg_1.begin[0]);
+
+    // This move should cause |nested_msg_1| to be finalized, but not
+    // |nested_msg_2|, which will be finalized only after the current scope.
+    child_handle_1 = std::move(child_handle_2);
+    ASSERT_EQ(0x82u, size_msg_1.begin[0]);
+    ASSERT_EQ(0u, size_msg_2.begin[0]);
+  }
+  ASSERT_EQ(0x83u, size_msg_2.begin[0]);
+}
+
+TEST_F(ProtoZeroMessageTest, MoveMessageHandle) {
+  FakeRootMessage* msg = NewMessage();
+  uint8_t msg_size[proto_utils::kMessageLengthFieldSize] = {};
+  msg->set_size_field(
+      {&msg_size[0], &msg_size[proto_utils::kMessageLengthFieldSize]});
+
+  // Test that the handle going out of scope causes the finalization of the
+  // target message.
+  {
+    ProtoZeroMessageHandle<FakeRootMessage> handle1(msg);
+    ProtoZeroMessageHandle<FakeRootMessage> handle2{};
+    handle1->AppendBytes(1 /* field_id */, kTestBytes, 1 /* size */);
+    handle2 = std::move(handle1);
+    ASSERT_EQ(0u, msg_size[0]);
+  }
+  ASSERT_EQ(0x83u, msg_size[0]);
+}
+
+}  // namespace
+}  // namespace protozero
diff --git a/src/protozero/scattered_stream_writer.cc b/src/protozero/scattered_stream_writer.cc
new file mode 100644
index 0000000..23fb12c
--- /dev/null
+++ b/src/protozero/scattered_stream_writer.cc
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "perfetto/protozero/scattered_stream_writer.h"
+
+#include <algorithm>
+
+#include "perfetto/base/logging.h"
+
+namespace protozero {
+
+ScatteredStreamWriter::Delegate::~Delegate() {}
+
+ScatteredStreamWriter::ScatteredStreamWriter(Delegate* delegate)
+    : delegate_(delegate),
+      cur_range_({nullptr, nullptr}),
+      write_ptr_(nullptr) {}
+
+ScatteredStreamWriter::~ScatteredStreamWriter() {}
+
+void ScatteredStreamWriter::Reset(ContiguousMemoryRange range) {
+  cur_range_ = range;
+  write_ptr_ = range.begin;
+  PERFETTO_DCHECK(write_ptr_ < cur_range_.end);
+}
+
+void ScatteredStreamWriter::Extend() {
+  Reset(delegate_->GetNewBuffer());
+}
+
+void ScatteredStreamWriter::WriteBytesSlowPath(const uint8_t* src,
+                                               size_t size) {
+  size_t bytes_left = size;
+  while (bytes_left > 0) {
+    if (write_ptr_ >= cur_range_.end)
+      Extend();
+    const size_t burst_size = std::min(bytes_available(), bytes_left);
+    WriteBytesUnsafe(src, burst_size);
+    bytes_left -= burst_size;
+    src += burst_size;
+  }
+}
+
+// TODO(primiano): perf optimization: I suspect that at the end this will always
+// be called with |size| == 4, in which case we might just hardcode it.
+ContiguousMemoryRange ScatteredStreamWriter::ReserveBytes(size_t size) {
+  if (write_ptr_ + size > cur_range_.end) {
+    // Assume the reservations are always < Delegate::GetNewBuffer().size(),
+    // so that one single call to Extend() will definitely give enough headroom.
+    Extend();
+    PERFETTO_DCHECK(write_ptr_ + size <= cur_range_.end);
+  }
+  uint8_t* begin = write_ptr_;
+  write_ptr_ += size;
+#ifndef NDEBUG
+  memset(begin, '\xFF', size);
+#endif
+  return {begin, begin + size};
+}
+
+}  // namespace protozero
diff --git a/src/protozero/scattered_stream_writer_unittest.cc b/src/protozero/scattered_stream_writer_unittest.cc
new file mode 100644
index 0000000..3dc38fb
--- /dev/null
+++ b/src/protozero/scattered_stream_writer_unittest.cc
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "perfetto/protozero/scattered_stream_writer.h"
+
+#include <string.h>
+
+#include <memory>
+
+#include "gtest/gtest.h"
+#include "perfetto/base/logging.h"
+#include "src/protozero/test/fake_scattered_buffer.h"
+
+namespace protozero {
+namespace {
+
+const size_t kChunkSize = 8;
+
+TEST(ScatteredStreamWriterTest, ScatteredWrites) {
+  FakeScatteredBuffer delegate(kChunkSize);
+  ScatteredStreamWriter ssw(&delegate);
+
+  const uint8_t kOneByteBuf[] = {0x40};
+  const uint8_t kThreeByteBuf[] = {0x50, 0x51, 0x52};
+  const uint8_t kFourByteBuf[] = {0x60, 0x61, 0x62, 0x63};
+  uint8_t kTwentyByteBuf[20];
+  for (uint8_t i = 0; i < sizeof(kTwentyByteBuf); ++i)
+    kTwentyByteBuf[i] = 0xA0 + i;
+
+  // Writing up to the chunk size should cause only the initial extension.
+  for (uint8_t i = 0; i < kChunkSize; ++i) {
+    ssw.WriteByte(i);
+    EXPECT_EQ(kChunkSize - i - 1, ssw.bytes_available());
+  }
+  EXPECT_EQ(1u, delegate.chunks().size());
+  EXPECT_EQ(0u, ssw.bytes_available());
+
+  // This extra write will cause the first extension.
+  ssw.WriteBytes(kOneByteBuf, sizeof(kOneByteBuf));
+  EXPECT_EQ(2u, delegate.chunks().size());
+  EXPECT_EQ(7u, ssw.bytes_available());
+
+  // This starts at offset 1, to make sure we don't hardcode any assumption
+  // about alignment.
+  ContiguousMemoryRange reserved_range_1 = ssw.ReserveBytes(4);
+  EXPECT_EQ(2u, delegate.chunks().size());
+  EXPECT_EQ(3u, ssw.bytes_available());
+
+  ssw.WriteByte(0xFF);
+  ssw.WriteBytes(kThreeByteBuf, sizeof(kThreeByteBuf));
+  EXPECT_EQ(3u, delegate.chunks().size());
+  EXPECT_EQ(7u, ssw.bytes_available());
+
+  ContiguousMemoryRange reserved_range_2 = ssw.ReserveBytes(4);
+  ssw.WriteBytes(kTwentyByteBuf, sizeof(kTwentyByteBuf));
+  EXPECT_EQ(6u, delegate.chunks().size());
+  EXPECT_EQ(7u, ssw.bytes_available());
+
+  // Writing reserved bytes should not change the bytes_available().
+  memcpy(reserved_range_1.begin, kFourByteBuf, sizeof(kFourByteBuf));
+  memcpy(reserved_range_2.begin, kFourByteBuf, sizeof(kFourByteBuf));
+  EXPECT_EQ(6u, delegate.chunks().size());
+  EXPECT_EQ(7u, ssw.bytes_available());
+
+  // Check that reserving more bytes than what left creates a brand new chunk
+  // even if the previous one is not exhausted
+  for (uint8_t i = 0; i < 5; ++i)
+    ssw.WriteByte(0xFF);
+  memcpy(ssw.ReserveBytes(4).begin, kFourByteBuf, sizeof(kFourByteBuf));
+  memcpy(ssw.ReserveBytesUnsafe(3), kThreeByteBuf, sizeof(kThreeByteBuf));
+  memcpy(ssw.ReserveBytes(3).begin, kThreeByteBuf, sizeof(kThreeByteBuf));
+  memcpy(ssw.ReserveBytesUnsafe(1), kOneByteBuf, sizeof(kOneByteBuf));
+  memcpy(ssw.ReserveBytes(1).begin, kOneByteBuf, sizeof(kOneByteBuf));
+
+  EXPECT_EQ(8u, delegate.chunks().size());
+  EXPECT_EQ(3u, ssw.bytes_available());
+
+  EXPECT_EQ("0001020304050607", delegate.GetChunkAsString(0));
+  EXPECT_EQ("4060616263FF5051", delegate.GetChunkAsString(1));
+  EXPECT_EQ("5260616263A0A1A2", delegate.GetChunkAsString(2));
+  EXPECT_EQ("A3A4A5A6A7A8A9AA", delegate.GetChunkAsString(3));
+  EXPECT_EQ("ABACADAEAFB0B1B2", delegate.GetChunkAsString(4));
+  EXPECT_EQ("B3FFFFFFFFFF0000", delegate.GetChunkAsString(5));
+  EXPECT_EQ("6061626350515200", delegate.GetChunkAsString(6));
+  EXPECT_EQ("5051524040000000", delegate.GetChunkAsString(7));
+
+  // Finally reset the writer to a new buffer.
+  uint8_t other_buffer[8] = {0};
+  ssw.Reset({other_buffer, other_buffer + sizeof(other_buffer)});
+  EXPECT_EQ(other_buffer, ssw.write_ptr());
+  ssw.WriteByte(1);
+  ssw.WriteBytes(kThreeByteBuf, sizeof(kThreeByteBuf));
+  EXPECT_EQ(1u, other_buffer[0]);
+  EXPECT_EQ(0x52u, other_buffer[3]);
+}
+
+}  // namespace
+}  // namespace protozero
diff --git a/src/protozero/test/example_proto/library.proto b/src/protozero/test/example_proto/library.proto
new file mode 100644
index 0000000..4ff8fee
--- /dev/null
+++ b/src/protozero/test/example_proto/library.proto
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+syntax = "proto2";
+option optimize_for = LITE_RUNTIME;
+
+package foo.bar;
+
+import public "src/protozero/test/example_proto/library_internals/galaxies.proto";
+
+message TransgalacticMessage {
+  optional Galaxy origin_galaxy = 1;
+  optional string origin_planet = 2;
+  optional Galaxy destination_galaxy = 3;
+  optional string destination_planet = 4;
+  optional bytes proto_message = 5;
+}
diff --git a/src/protozero/test/example_proto/library_internals/galaxies.proto b/src/protozero/test/example_proto/library_internals/galaxies.proto
new file mode 100644
index 0000000..0b1c1c5
--- /dev/null
+++ b/src/protozero/test/example_proto/library_internals/galaxies.proto
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+syntax = "proto2";
+option optimize_for = LITE_RUNTIME;
+
+package foo.bar;
+
+import public "src/protozero/test/example_proto/upper_import.proto";
+
+enum Galaxy {
+  MILKY_WAY = 1;
+  ANDROMEDA = 2;
+  SUNFLOWER = 3;
+}
diff --git a/src/protozero/test/example_proto/test_messages.proto b/src/protozero/test/example_proto/test_messages.proto
new file mode 100644
index 0000000..077b088
--- /dev/null
+++ b/src/protozero/test/example_proto/test_messages.proto
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+syntax = "proto2";
+option optimize_for = LITE_RUNTIME;
+
+package foo.bar;
+
+import "src/protozero/test/example_proto/library.proto";
+
+// This file contains comprehensive set of supported message structures and
+// data types. Unit tests depends on the plugin-processed version of this file.
+
+// Tests importing message definition from another proto file.
+message TransgalacticParcel {
+  optional TransgalacticMessage message = 1;
+  optional string tracking_code = 2;
+}
+
+enum SmallEnum {
+  TO_BE = 1;
+  NOT_TO_BE = 0;
+}
+
+enum SignedEnum {
+  POSITIVE = 1;
+  NEUTRAL = 0;
+  NEGATIVE = -1;
+}
+
+enum BigEnum {
+  BEGIN = 10;
+  END = 100500;
+}
+
+message EveryField {
+  optional int32 field_int32 = 1;
+  optional int64 field_int64 = 2;
+  optional uint32 field_uint32 = 3;
+  optional uint64 field_uint64 = 4;
+  optional sint32 field_sint32 = 5;
+  optional sint64 field_sint64 = 6;
+  optional fixed32 field_fixed32 = 7;
+  optional fixed64 field_fixed64 = 8;
+  optional sfixed32 field_sfixed32 = 9;
+  optional sfixed64 field_sfixed64 = 10;
+  optional float field_float = 11;
+  optional double field_double = 12;
+  optional bool field_bool = 13;
+
+  optional SmallEnum small_enum = 51;
+  optional SignedEnum signed_enum = 52;
+  optional BigEnum big_enum = 53;
+
+  optional string field_string = 500;
+  optional bytes field_bytes = 505;
+
+  enum NestedEnum {
+    PING = 1;
+    PONG = 2;
+  }
+  optional NestedEnum nested_enum = 600;
+
+  repeated int32 repeated_int32 = 999;
+}
+
+message NestedA {
+  message NestedB {
+    message NestedC { optional int32 value_c = 1; }
+    optional NestedC value_b = 1;
+  }
+  repeated NestedB repeated_a = 2;
+  optional NestedB.NestedC super_nested = 3;
+}
+
+message CamelCaseFields {
+  // To check that any reasonable name converts to camel case correctly.
+  optional bool foo_bar_baz = 1;
+  optional bool barBaz = 2;
+  optional bool MooMoo = 3;
+  optional bool URLEncoder = 4;
+  optional bool XMap = 5;
+  optional bool UrLE_nco__der = 6;
+  optional bool __bigBang = 7;
+  optional bool U2 = 8;
+  optional bool bangBig__ = 9;
+}
diff --git a/src/protozero/test/example_proto/upper_import.proto b/src/protozero/test/example_proto/upper_import.proto
new file mode 100644
index 0000000..4aacd41
--- /dev/null
+++ b/src/protozero/test/example_proto/upper_import.proto
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+syntax = "proto2";
+option optimize_for = LITE_RUNTIME;
+
+package foo.bar;
+
+message TrickyPublicImport {}
diff --git a/src/protozero/test/fake_scattered_buffer.cc b/src/protozero/test/fake_scattered_buffer.cc
new file mode 100644
index 0000000..3c48f48
--- /dev/null
+++ b/src/protozero/test/fake_scattered_buffer.cc
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "src/protozero/test/fake_scattered_buffer.h"
+
+#include <sstream>
+#include <utility>
+
+#include "gtest/gtest.h"
+
+namespace protozero {
+
+namespace {
+
+std::string ToHex(const void* data, size_t length) {
+  std::ostringstream ss;
+  ss << std::hex << std::setfill('0');
+  ss << std::uppercase;
+  for (size_t i = 0; i < length; i++) {
+    char c = reinterpret_cast<const char*>(data)[i];
+    ss << std::setw(2) << (static_cast<unsigned>(c) & 0xFF);
+  }
+  return ss.str();
+}
+
+}  // namespace
+
+FakeScatteredBuffer::FakeScatteredBuffer(size_t chunk_size)
+    : chunk_size_(chunk_size) {}
+
+FakeScatteredBuffer::~FakeScatteredBuffer() {}
+
+ContiguousMemoryRange FakeScatteredBuffer::GetNewBuffer() {
+  std::unique_ptr<uint8_t[]> chunk(new uint8_t[chunk_size_]);
+  uint8_t* begin = chunk.get();
+  memset(begin, 0, chunk_size_);
+  chunks_.push_back(std::move(chunk));
+  return {begin, begin + chunk_size_};
+}
+
+std::string FakeScatteredBuffer::GetChunkAsString(size_t chunk_index) {
+  return ToHex(chunks_[chunk_index].get(), chunk_size_);
+}
+
+void FakeScatteredBuffer::GetBytes(size_t start, size_t length, uint8_t* buf) {
+  ASSERT_LE(start + length, chunks_.size() * chunk_size_);
+  for (size_t pos = 0; pos < length; ++pos) {
+    size_t chunk_index = (start + pos) / chunk_size_;
+    size_t chunk_offset = (start + pos) % chunk_size_;
+    buf[pos] = chunks_[chunk_index].get()[chunk_offset];
+  }
+}
+
+std::string FakeScatteredBuffer::GetBytesAsString(size_t start, size_t length) {
+  std::unique_ptr<uint8_t[]> buffer(new uint8_t[length]);
+  GetBytes(start, length, buffer.get());
+  return ToHex(buffer.get(), length);
+}
+
+}  // namespace protozero
diff --git a/src/protozero/test/fake_scattered_buffer.h b/src/protozero/test/fake_scattered_buffer.h
new file mode 100644
index 0000000..2ccfb81
--- /dev/null
+++ b/src/protozero/test/fake_scattered_buffer.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SRC_PROTOZERO_TEST_FAKE_SCATTERED_BUFFER_H_
+#define SRC_PROTOZERO_TEST_FAKE_SCATTERED_BUFFER_H_
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "perfetto/protozero/scattered_stream_writer.h"
+
+namespace protozero {
+
+// A simple ScatteredStreamWriter::Delegate implementation which just allocates
+// chunks of a fixed size.
+class FakeScatteredBuffer : public ScatteredStreamWriter::Delegate {
+ public:
+  explicit FakeScatteredBuffer(size_t chunk_size);
+  ~FakeScatteredBuffer() override;
+
+  // ScatteredStreamWriter::Delegate implementation.
+  ContiguousMemoryRange GetNewBuffer() override;
+
+  std::string GetChunkAsString(size_t chunk_index);
+
+  void GetBytes(size_t start, size_t length, uint8_t* buf);
+  std::string GetBytesAsString(size_t start, size_t length);
+
+  const std::vector<std::unique_ptr<uint8_t[]>>& chunks() const {
+    return chunks_;
+  }
+
+ private:
+  const size_t chunk_size_;
+  std::vector<std::unique_ptr<uint8_t[]>> chunks_;
+};
+
+}  // namespace protozero
+
+#endif  // SRC_PROTOZERO_TEST_FAKE_SCATTERED_BUFFER_H_
diff --git a/src/protozero/test/protozero_conformance_unittest.cc b/src/protozero/test/protozero_conformance_unittest.cc
new file mode 100644
index 0000000..bb60620
--- /dev/null
+++ b/src/protozero/test/protozero_conformance_unittest.cc
@@ -0,0 +1,191 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <limits>
+#include <memory>
+#include <vector>
+
+#include "gtest/gtest.h"
+#include "perfetto/protozero/protozero_message_handle.h"
+#include "src/protozero/test/fake_scattered_buffer.h"
+
+// Autogenerated headers in out/*/gen/
+#include "src/protozero/test/example_proto/library.pbzero.h"
+#include "src/protozero/test/example_proto/test_messages.pb.h"
+#include "src/protozero/test/example_proto/test_messages.pbzero.h"
+
+namespace pbtest = foo::bar::pbzero;  // Generated by the protozero plugin.
+namespace pbgold = foo::bar;  // Generated by the official protobuf compiler.
+
+namespace protozero {
+namespace {
+
+constexpr size_t kChunkSize = 42;
+
+class ProtoZeroConformanceTest : public ::testing::Test {
+ public:
+  void SetUp() override {
+    buffer_.reset(new FakeScatteredBuffer(kChunkSize));
+    stream_writer_.reset(new ScatteredStreamWriter(buffer_.get()));
+  }
+
+  void TearDown() override {
+    root_messages_.clear();
+    stream_writer_.reset();
+    buffer_.reset();
+    google::protobuf::ShutdownProtobufLibrary();
+  }
+
+ protected:
+  template <class T>
+  T* CreateMessage() {
+    T* message = new T();
+    root_messages_.push_back(std::unique_ptr<T>(message));
+    message->Reset(stream_writer_.get());
+    return message;
+  }
+
+  size_t GetNumSerializedBytes() {
+    return buffer_->chunks().size() * kChunkSize -
+           stream_writer_->bytes_available();
+  }
+
+  void GetSerializedBytes(size_t start, size_t length, uint8_t* buffer) {
+    return buffer_->GetBytes(start, length, buffer);
+  }
+
+ private:
+  std::unique_ptr<FakeScatteredBuffer> buffer_;
+  std::unique_ptr<ScatteredStreamWriter> stream_writer_;
+  std::vector<std::unique_ptr<ProtoZeroMessage>> root_messages_;
+};
+
+TEST_F(ProtoZeroConformanceTest, SimpleFieldsNoNesting) {
+  auto* msg = CreateMessage<pbtest::EveryField>();
+
+  msg->set_field_int32(-1);
+  msg->set_field_int64(-333123456789ll);
+  msg->set_field_uint32(600);
+  msg->set_field_uint64(333123456789ll);
+  msg->set_field_sint32(-5);
+  msg->set_field_sint64(-9000);
+  msg->set_field_fixed32(12345);
+  msg->set_field_fixed64(444123450000ll);
+  msg->set_field_sfixed32(-69999);
+  msg->set_field_sfixed64(-200);
+  msg->set_field_float(3.14f);
+  msg->set_field_double(0.5555);
+  msg->set_field_bool(true);
+  msg->set_small_enum(pbtest::SmallEnum::TO_BE);
+  msg->set_signed_enum(pbtest::SignedEnum::NEGATIVE);
+  msg->set_big_enum(pbtest::BigEnum::BEGIN);
+  msg->set_field_string("FizzBuzz");
+  msg->set_field_bytes(reinterpret_cast<const uint8_t*>("\x11\x00\xBE\xEF"), 4);
+  msg->add_repeated_int32(1);
+  msg->add_repeated_int32(-1);
+  msg->add_repeated_int32(100);
+  msg->add_repeated_int32(2000000);
+
+  size_t msg_size = GetNumSerializedBytes();
+  EXPECT_EQ(126u, msg_size);
+
+  std::unique_ptr<uint8_t[]> msg_binary(new uint8_t[msg_size]);
+  GetSerializedBytes(0, msg_size, msg_binary.get());
+
+  pbgold::EveryField gold_msg;
+  gold_msg.ParseFromArray(msg_binary.get(), static_cast<int>(msg_size));
+  EXPECT_EQ(-1, gold_msg.field_int32());
+  EXPECT_EQ(-333123456789ll, gold_msg.field_int64());
+  EXPECT_EQ(600u, gold_msg.field_uint32());
+  EXPECT_EQ(333123456789ull, gold_msg.field_uint64());
+  EXPECT_EQ(-5, gold_msg.field_sint32());
+  EXPECT_EQ(-9000, gold_msg.field_sint64());
+  EXPECT_EQ(12345u, gold_msg.field_fixed32());
+  EXPECT_EQ(444123450000ull, gold_msg.field_fixed64());
+  EXPECT_EQ(-69999, gold_msg.field_sfixed32());
+  EXPECT_EQ(-200, gold_msg.field_sfixed64());
+  EXPECT_FLOAT_EQ(3.14f, gold_msg.field_float());
+  EXPECT_DOUBLE_EQ(0.5555, gold_msg.field_double());
+  EXPECT_EQ(true, gold_msg.field_bool());
+  EXPECT_EQ(pbgold::SmallEnum::TO_BE, gold_msg.small_enum());
+  EXPECT_EQ(pbgold::SignedEnum::NEGATIVE, gold_msg.signed_enum());
+  EXPECT_EQ(pbgold::BigEnum::BEGIN, gold_msg.big_enum());
+  EXPECT_EQ("FizzBuzz", gold_msg.field_string());
+  EXPECT_EQ(std::string("\x11\x00\xBE\xEF", 4), gold_msg.field_bytes());
+  EXPECT_EQ(4, gold_msg.repeated_int32_size());
+  EXPECT_EQ(1, gold_msg.repeated_int32(0));
+  EXPECT_EQ(-1, gold_msg.repeated_int32(1));
+  EXPECT_EQ(100, gold_msg.repeated_int32(2));
+  EXPECT_EQ(2000000, gold_msg.repeated_int32(3));
+}
+
+TEST_F(ProtoZeroConformanceTest, NestedMessages) {
+  auto* msg_a = CreateMessage<pbtest::NestedA>();
+
+  pbtest::NestedA::NestedB* msg_b = msg_a->add_repeated_a();
+  pbtest::NestedA::NestedB::NestedC* msg_c = msg_b->set_value_b();
+  msg_c->set_value_c(321);
+  msg_b = msg_a->add_repeated_a();
+  msg_c = msg_a->set_super_nested();
+  msg_c->set_value_c(1000);
+  msg_a->Finalize();
+
+  size_t msg_size = GetNumSerializedBytes();
+  EXPECT_EQ(26u, msg_size);
+
+  std::unique_ptr<uint8_t[]> msg_binary(new uint8_t[msg_size]);
+  GetSerializedBytes(0, msg_size, msg_binary.get());
+
+  pbgold::NestedA gold_msg_a;
+  gold_msg_a.ParseFromArray(msg_binary.get(), static_cast<int>(msg_size));
+  EXPECT_EQ(2, gold_msg_a.repeated_a_size());
+  EXPECT_EQ(321, gold_msg_a.repeated_a(0).value_b().value_c());
+  EXPECT_FALSE(gold_msg_a.repeated_a(1).has_value_b());
+  EXPECT_EQ(1000, gold_msg_a.super_nested().value_c());
+}
+
+TEST(ProtoZeroTest, Simple) {
+  // Test the includes for indirect public import: library.pbzero.h ->
+  // library_internals/galaxies.pbzero.h -> upper_import.pbzero.h .
+  EXPECT_LE(0u, sizeof(pbtest::TrickyPublicImport));
+}
+
+TEST(ProtoZeroTest, Reflection) {
+  // Tests camel case conversion as well.
+  EXPECT_EQ(1, pbtest::CamelCaseFields::kFooBarBazFieldNumber);
+  EXPECT_EQ(2, pbtest::CamelCaseFields::kBarBazFieldNumber);
+  EXPECT_EQ(3, pbtest::CamelCaseFields::kMooMooFieldNumber);
+  EXPECT_EQ(4, pbtest::CamelCaseFields::kURLEncoderFieldNumber);
+  EXPECT_EQ(5, pbtest::CamelCaseFields::kXMapFieldNumber);
+  EXPECT_EQ(6, pbtest::CamelCaseFields::kUrLENcoDerFieldNumber);
+  EXPECT_EQ(7, pbtest::CamelCaseFields::kBigBangFieldNumber);
+  EXPECT_EQ(8, pbtest::CamelCaseFields::kU2FieldNumber);
+  EXPECT_EQ(9, pbtest::CamelCaseFields::kBangBigFieldNumber);
+
+  const ProtoFieldDescriptor* reflection =
+      pbtest::EveryField::GetFieldDescriptor(
+          pbtest::EveryField::kFieldInt32FieldNumber);
+  EXPECT_STREQ("field_int32", reflection->name());
+  EXPECT_EQ(ProtoFieldDescriptor::Type::TYPE_INT32, reflection->type());
+  EXPECT_EQ(1u, reflection->number());
+  EXPECT_FALSE(reflection->is_repeated());
+  EXPECT_TRUE(reflection->is_valid());
+
+  EXPECT_FALSE(pbtest::TransgalacticParcel::GetFieldDescriptor(42)->is_valid());
+}
+
+}  // namespace
+}  // namespace protozero