pw_protobuf: Add encoder+decoder size reports

Adds size reports for pw_protobuf's base encoder and decoder types,
excluding any codegen.

Change-Id: I64ca6468a62d5aa111249c9614a4c2bc003d8edf
Reviewed-on: https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/109011
Commit-Queue: Armando Montanez <amontanez@google.com>
Pigweed-Auto-Submit: Armando Montanez <amontanez@google.com>
Reviewed-by: Keir Mierle <keir@google.com>
diff --git a/pw_protobuf/BUILD.gn b/pw_protobuf/BUILD.gn
index a68ae74..b307c1d 100644
--- a/pw_protobuf/BUILD.gn
+++ b/pw_protobuf/BUILD.gn
@@ -96,8 +96,9 @@
 pw_doc_group("docs") {
   sources = [ "docs.rst" ]
   report_deps = [
-    "size_report:decoder_full",
     "size_report:decoder_incremental",
+    "size_report:decoder_partial",
+    "size_report:protobuf_overview",
   ]
 }
 
diff --git a/pw_protobuf/docs.rst b/pw_protobuf/docs.rst
index b25c02e..0c192ae 100644
--- a/pw_protobuf/docs.rst
+++ b/pw_protobuf/docs.rst
@@ -27,9 +27,10 @@
  2. Per-Field Writers and Readers,
  3. Direct Writers and Readers.
 
-This has a few benefits. The primary one is that it allows the library to be
-incredibly small, with the encoder and decoder each having a code size of
-around 1.5K and negligible RAM usage.
+This has a few benefits. The primary one is that it allows the core proto
+serialization and deserialization libraries to be relatively small.
+
+.. include:: size_report/protobuf_overview
 
 To demonstrate these layers, we use the following protobuf message definition
 in the examples:
@@ -2016,7 +2017,7 @@
 decode methods and a decode callback for a proto message containing each of the
 protobuf field types.
 
-.. include:: size_report/decoder_full
+.. include:: size_report/decoder_partial
 
 
 Incremental size report
diff --git a/pw_protobuf/size_report/BUILD.bazel b/pw_protobuf/size_report/BUILD.bazel
new file mode 100644
index 0000000..aea516d
--- /dev/null
+++ b/pw_protobuf/size_report/BUILD.bazel
@@ -0,0 +1,95 @@
+# Copyright 2022 The Pigweed Authors
+#
+# 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
+#
+#     https://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.
+
+load(
+    "//pw_build:pigweed.bzl",
+    "pw_cc_binary",
+    "pw_cc_library",
+)
+
+package(default_visibility = ["//visibility:public"])
+
+licenses(["notice"])
+
+pw_cc_binary(
+    name = "decoder_partial",
+    srcs = [
+        "decoder_partial.cc",
+    ],
+    deps = [
+        "//pw_bloat:bloat_this_binary",
+        "//pw_preprocessor",
+        "//pw_protobuf",
+    ],
+)
+
+pw_cc_binary(
+    name = "decoder_incremental",
+    srcs = [
+        "decoder_incremental.cc",
+    ],
+    deps = [
+        "//pw_bloat:bloat_this_binary",
+        "//pw_preprocessor",
+        "//pw_protobuf",
+    ],
+)
+
+pw_cc_library(
+    name = "proto_bloat",
+    srcs = [
+        "proto_bloat.cc",
+    ],
+    hdrs = ["proto_bloat.h"],
+    deps = [
+        "//pw_containers",
+        "//pw_preprocessor",
+        "//pw_protobuf",
+        "//pw_status",
+        "//pw_stream",
+    ],
+)
+
+pw_cc_binary(
+    name = "encode_decode_core",
+    srcs = [
+        "encode_decode_core.cc",
+    ],
+    deps = [
+        ":proto_bloat",
+        "//pw_bloat:bloat_this_binary",
+    ],
+)
+
+pw_cc_binary(
+    name = "message_core",
+    srcs = [
+        "message_core.cc",
+    ],
+    deps = [
+        ":proto_bloat",
+        "//pw_bloat:bloat_this_binary",
+    ],
+)
+
+pw_cc_binary(
+    name = "proto_base",
+    srcs = [
+        "proto_base.cc",
+    ],
+    deps = [
+        ":proto_bloat",
+        "//pw_bloat:bloat_this_binary",
+    ],
+)
diff --git a/pw_protobuf/size_report/BUILD.gn b/pw_protobuf/size_report/BUILD.gn
index 1c71180..dd3d310 100644
--- a/pw_protobuf/size_report/BUILD.gn
+++ b/pw_protobuf/size_report/BUILD.gn
@@ -15,24 +15,61 @@
 import("//build_overrides/pigweed.gni")
 
 import("$dir_pw_bloat/bloat.gni")
+import("$dir_pw_build/target_types.gni")
 
-_decoder_full = {
+_decoder_partial = {
   deps = [
     "$dir_pw_bloat:bloat_this_binary",
     "$dir_pw_preprocessor",
-    "$dir_pw_protobuf:pw_protobuf",
+    "$dir_pw_protobuf",
   ]
-  sources = [ "decoder_full.cc" ]
+  sources = [ "decoder_partial.cc" ]
 }
 
-pw_toolchain_size_diff("decoder_full") {
+pw_source_set("proto_bloat") {
+  public = [ "proto_bloat.h" ]
+  deps = [
+    "$dir_pw_containers",
+    "$dir_pw_preprocessor",
+    "$dir_pw_protobuf",
+    "$dir_pw_status",
+    "$dir_pw_stream",
+  ]
+  sources = [ "proto_bloat.cc" ]
+}
+
+pw_executable("proto_base") {
+  deps = [
+    ":proto_bloat",
+    "$dir_pw_bloat:bloat_this_binary",
+  ]
+  sources = [ "proto_base.cc" ]
+}
+
+pw_executable("encode_decode_core") {
+  deps = [
+    ":proto_bloat",
+    "$dir_pw_bloat:bloat_this_binary",
+  ]
+  sources = [ "encode_decode_core.cc" ]
+}
+
+pw_executable("message_core") {
+  deps = [
+    ":proto_bloat",
+    "$dir_pw_bloat:bloat_this_binary",
+  ]
+  sources = [ "message_core.cc" ]
+}
+
+pw_toolchain_size_diff("decoder_partial") {
   base_executable = pw_bloat_empty_base
-  diff_executable = _decoder_full
+  diff_executable = _decoder_partial
   title = "Size of all decoder methods"
 }
 
 pw_toolchain_size_diff("decoder_incremental") {
-  base_executable = _decoder_full
+  base_executable = _decoder_partial
   diff_executable = {
     deps = [
       "$dir_pw_bloat:bloat_this_binary",
@@ -43,3 +80,20 @@
   }
   title = "Adding more fields to decode callback"
 }
+
+pw_size_diff("protobuf_overview") {
+  title = "Pigweed protobuf encoder size report"
+  source_filter = "pw::protobuf::*|section .code"
+  binaries = [
+    {
+      target = ":encode_decode_core"
+      base = ":proto_base"
+      label = "Full wire-format proto encode/decode library"
+    },
+    {
+      target = ":message_core"
+      base = ":proto_base"
+      label = "Including table-based `Message` encoder and decoder"
+    },
+  ]
+}
diff --git a/pw_protobuf/size_report/decoder_full.cc b/pw_protobuf/size_report/decoder_partial.cc
similarity index 100%
rename from pw_protobuf/size_report/decoder_full.cc
rename to pw_protobuf/size_report/decoder_partial.cc
diff --git a/pw_protobuf/size_report/encode_decode_core.cc b/pw_protobuf/size_report/encode_decode_core.cc
new file mode 100644
index 0000000..4a85404
--- /dev/null
+++ b/pw_protobuf/size_report/encode_decode_core.cc
@@ -0,0 +1,25 @@
+// Copyright 2022 The Pigweed Authors
+//
+// 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
+//
+//     https://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 "proto_bloat.h"
+#include "pw_bloat/bloat_this_binary.h"
+
+int main() {
+  pw::bloat::BloatThisBinary();
+  pw::protobuf_size_report::BloatWithBase();
+  pw::protobuf_size_report::BloatWithEncoder();
+  pw::protobuf_size_report::BloatWithDecoder();
+  pw::protobuf_size_report::BloatWithStreamDecoder();
+  return 0;
+}
diff --git a/pw_protobuf/size_report/message_core.cc b/pw_protobuf/size_report/message_core.cc
new file mode 100644
index 0000000..a5e2759
--- /dev/null
+++ b/pw_protobuf/size_report/message_core.cc
@@ -0,0 +1,27 @@
+// Copyright 2022 The Pigweed Authors
+//
+// 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
+//
+//     https://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 "proto_bloat.h"
+#include "pw_bloat/bloat_this_binary.h"
+
+int main() {
+  pw::bloat::BloatThisBinary();
+  pw::protobuf_size_report::BloatWithBase();
+  pw::protobuf_size_report::BloatWithEncoder();
+  pw::protobuf_size_report::BloatWithDecoder();
+  pw::protobuf_size_report::BloatWithStreamDecoder();
+  pw::protobuf_size_report::BloatWithTableEncoder();
+  pw::protobuf_size_report::BloatWithTableDecoder();
+  return 0;
+}
diff --git a/pw_protobuf/size_report/proto_base.cc b/pw_protobuf/size_report/proto_base.cc
new file mode 100644
index 0000000..49be2a6
--- /dev/null
+++ b/pw_protobuf/size_report/proto_base.cc
@@ -0,0 +1,22 @@
+// Copyright 2022 The Pigweed Authors
+//
+// 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
+//
+//     https://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 "proto_bloat.h"
+#include "pw_bloat/bloat_this_binary.h"
+
+int main() {
+  pw::bloat::BloatThisBinary();
+  pw::protobuf_size_report::BloatWithBase();
+  return 0;
+}
diff --git a/pw_protobuf/size_report/proto_bloat.cc b/pw_protobuf/size_report/proto_bloat.cc
new file mode 100644
index 0000000..542653d
--- /dev/null
+++ b/pw_protobuf/size_report/proto_bloat.cc
@@ -0,0 +1,350 @@
+// Copyright 2022 The Pigweed Authors
+//
+// 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
+//
+//     https://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 "proto_bloat.h"
+
+#include <array>
+#include <cstdint>
+#include <string_view>
+
+#include "pw_containers/vector.h"
+#include "pw_preprocessor/concat.h"
+#include "pw_protobuf/decoder.h"
+#include "pw_protobuf/encoder.h"
+#include "pw_protobuf/stream_decoder.h"
+#include "pw_status/status.h"
+#include "pw_stream/null_stream.h"
+#include "pw_stream/stream.h"
+
+namespace pw::protobuf_size_report {
+namespace {
+
+template <typename T>
+constexpr std::array<T, 4> GetIntegerArray() {
+  return std::array<T, 4>{958736, 2085792374, 0, 42};
+}
+
+template <typename T>
+constexpr Vector<T, 4> GetIntegerVector() {
+  return Vector<T, 4>{958736, 2085792374, 0, 42};
+}
+
+constexpr std::array<bool, 5> GetBoolArray() {
+  return std::array<bool, 5>{true, false, false, true, true};
+}
+
+Vector<bool, 5> GetBoolVector() {
+  return Vector<bool, 5>{true, false, false, true, true};
+}
+
+constexpr std::array<float, 5> GetFloatArray() {
+  return std::array<float, 5>{1.2f, 3.4f, 5.6e20f, 0.0789f, 0.0f};
+}
+
+Vector<float, 5> GetFloatVector() {
+  return Vector<float, 5>{1.2f, 3.4f, 5.6e20f, 0.0789f, 0.0f};
+}
+
+constexpr std::array<double, 5> GetDoubleArray() {
+  return std::array<double, 5>{1.2, 3.4, 5.6e20, 0.0789, 0.0};
+}
+
+Vector<double, 5> GetDoubleVector() {
+  return Vector<double, 5>{1.2, 3.4, 5.6e20, 0.0789, 0.0};
+}
+
+constexpr std::string_view kTestString("I eat chips too often");
+
+constexpr protobuf::internal::MessageField kFakeTable[] = {
+    {4567,
+     protobuf::WireType::kDelimited,
+     234567,
+     protobuf::internal::VarintType::kNormal,
+     false,
+     true,
+     true,
+     true,
+     true,
+     260,
+     840245,
+     nullptr},
+    {4567,
+     protobuf::WireType::kDelimited,
+     234567,
+     protobuf::internal::VarintType::kNormal,
+     false,
+     true,
+     true,
+     true,
+     true,
+     260,
+     840245,
+     nullptr}};
+
+class FakeMessageEncoder : public protobuf::StreamEncoder {
+ public:
+  FakeMessageEncoder(stream::Writer& writer)
+      : protobuf::StreamEncoder(writer, ByteSpan()) {}
+  void DoBloat() { Write(ByteSpan(), kFakeTable); }
+};
+
+class FakeMessageDecoder : public protobuf::StreamDecoder {
+ public:
+  FakeMessageDecoder(stream::Reader& reader)
+      : protobuf::StreamDecoder(reader) {}
+  void DoBloat() { Read(ByteSpan(), kFakeTable); }
+};
+
+void CodeToSetUpSizeReportEnvironment() {
+  [[maybe_unused]] volatile auto arr1 = GetIntegerArray<uint32_t>();
+  [[maybe_unused]] volatile auto arr2 = GetIntegerArray<int32_t>();
+  [[maybe_unused]] volatile auto arr3 = GetIntegerArray<uint64_t>();
+  [[maybe_unused]] volatile auto arr4 = GetIntegerArray<int64_t>();
+
+  [[maybe_unused]] volatile auto vec1 = GetIntegerVector<uint32_t>();
+  [[maybe_unused]] volatile auto vec2 = GetIntegerVector<int32_t>();
+  [[maybe_unused]] volatile auto vec3 = GetIntegerVector<uint64_t>();
+  [[maybe_unused]] volatile auto vec4 = GetIntegerVector<int64_t>();
+
+  [[maybe_unused]] volatile auto bool1 = GetBoolArray();
+  [[maybe_unused]] volatile auto bool2 = GetBoolVector();
+
+  [[maybe_unused]] volatile auto float1 = GetFloatArray();
+  [[maybe_unused]] volatile auto float2 = GetFloatVector();
+
+  [[maybe_unused]] volatile auto double1 = GetDoubleArray();
+  [[maybe_unused]] volatile auto double2 = GetDoubleVector();
+
+  [[maybe_unused]] volatile std::string_view test_string = kTestString;
+
+  [[maybe_unused]] volatile stream::NullStream null_stream;
+}
+
+void Dependencies() {
+  std::array<std::byte, 2> buffer;
+  stream::NullStream null_stream;
+  stream::MemoryWriter memory_writer(buffer);
+  memory_writer.Write(buffer);
+  null_stream.Write(buffer);
+  stream::MemoryReader memory_reader(buffer);
+  memory_reader.Read(buffer).IgnoreError();
+}
+
+void CodeToPullInProtoEncoder() {
+  std::array<std::byte, 1024> buffer;
+  protobuf::MemoryEncoder encoder(buffer);
+
+  encoder.WriteUint32(1, 1);
+  encoder.WritePackedUint32(1, GetIntegerArray<uint32_t>());
+  encoder.WriteRepeatedUint32(1, GetIntegerVector<uint32_t>());
+
+  encoder.WriteInt32(1, 1);
+  encoder.WritePackedInt32(1, GetIntegerArray<int32_t>());
+  encoder.WriteRepeatedInt32(1, GetIntegerVector<int32_t>());
+
+  encoder.WriteUint64(1, 1);
+  encoder.WritePackedUint64(1, GetIntegerArray<uint64_t>());
+  encoder.WriteRepeatedUint64(1, GetIntegerVector<uint64_t>());
+
+  encoder.WriteInt64(1, 1);
+  encoder.WritePackedInt64(1, GetIntegerArray<int64_t>());
+  encoder.WriteRepeatedInt64(1, GetIntegerVector<int64_t>());
+
+  encoder.WriteSint32(1, 1);
+  encoder.WritePackedSint32(1, GetIntegerArray<int32_t>());
+  encoder.WriteRepeatedSint32(1, GetIntegerVector<int32_t>());
+
+  encoder.WriteSint64(1, 1);
+  encoder.WritePackedSint64(1, GetIntegerArray<int64_t>());
+  encoder.WriteRepeatedSint64(1, GetIntegerVector<int64_t>());
+
+  encoder.WriteFixed32(1, 1);
+  encoder.WritePackedFixed32(1, GetIntegerArray<uint32_t>());
+  encoder.WriteRepeatedFixed32(1, GetIntegerVector<uint32_t>());
+
+  encoder.WriteFixed64(1, 1);
+  encoder.WritePackedFixed64(1, GetIntegerArray<uint64_t>());
+  encoder.WriteRepeatedFixed64(1, GetIntegerVector<uint64_t>());
+
+  encoder.WriteSfixed32(1, 1);
+  encoder.WritePackedSfixed32(1, GetIntegerArray<int32_t>());
+  encoder.WriteRepeatedSfixed32(1, GetIntegerVector<int32_t>());
+
+  encoder.WriteSfixed64(1, 1);
+  encoder.WritePackedSfixed64(1, GetIntegerArray<int64_t>());
+  encoder.WriteRepeatedSfixed64(1, GetIntegerVector<int64_t>());
+
+  {
+    protobuf::StreamEncoder child = encoder.GetNestedEncoder(0xc01dfee7);
+
+    child.WriteFloat(234, 3.14f);
+    child.WritePackedFloat(234, GetFloatArray());
+    child.WriteRepeatedFloat(234, GetFloatVector());
+
+    child.WriteFloat(234, 3.14);
+    child.WritePackedDouble(234, GetDoubleArray());
+    child.WriteRepeatedDouble(234, GetDoubleVector());
+
+    child.WriteBool(7, true);
+    child.WritePackedBool(8, GetBoolArray());
+    child.WriteRepeatedBool(8, GetBoolVector());
+
+    encoder.WriteBytes(93, as_bytes(span(GetDoubleArray())));
+    encoder.WriteString(21343, kTestString);
+  }
+
+  stream::NullStream null_stream;
+  protobuf::StreamEncoder stream_encoder(null_stream, buffer);
+  stream_encoder.WriteBytesFromStream(3636, null_stream, 10824, buffer);
+}
+
+void CodeToPullInTableEncoder() {
+  stream::NullStream stream;
+  FakeMessageEncoder fake_encoder(stream);
+  fake_encoder.DoBloat();
+}
+
+void CodeToPullInTableDecoder() {
+  stream::NullStream stream;
+  FakeMessageDecoder fake_decoder(stream);
+  fake_decoder.DoBloat();
+}
+
+#define _PW_USE_FUNCTIONS_FOR_STREAM_DECODER(type_camel_case, underlying_type) \
+  do {                                                                         \
+    Status status;                                                             \
+    Vector<underlying_type, 3> vec;                                            \
+    span<underlying_type> packed_span;                                         \
+    status.Update(decoder.PW_CONCAT(Read, type_camel_case)().status());        \
+    status.Update(                                                             \
+        decoder.PW_CONCAT(ReadPacked, type_camel_case)(packed_span).status()); \
+    status.Update(decoder.PW_CONCAT(ReadRepeated, type_camel_case)(vec));      \
+    [[maybe_unused]] volatile bool ok = status.ok();                           \
+  } while (0)
+
+void CodeToPullInProtoStreamDecoder() {
+  stream::NullStream null_stream;
+  protobuf::StreamDecoder decoder(null_stream);
+  decoder.Next().IgnoreError();
+
+  _PW_USE_FUNCTIONS_FOR_STREAM_DECODER(Int32, int32_t);
+  _PW_USE_FUNCTIONS_FOR_STREAM_DECODER(Uint32, uint32_t);
+  _PW_USE_FUNCTIONS_FOR_STREAM_DECODER(Int64, int64_t);
+  _PW_USE_FUNCTIONS_FOR_STREAM_DECODER(Uint64, uint64_t);
+
+  _PW_USE_FUNCTIONS_FOR_STREAM_DECODER(Sint32, int32_t);
+  _PW_USE_FUNCTIONS_FOR_STREAM_DECODER(Sint64, int64_t);
+
+  _PW_USE_FUNCTIONS_FOR_STREAM_DECODER(Bool, bool);
+
+  _PW_USE_FUNCTIONS_FOR_STREAM_DECODER(Fixed32, uint32_t);
+  _PW_USE_FUNCTIONS_FOR_STREAM_DECODER(Fixed64, uint64_t);
+  _PW_USE_FUNCTIONS_FOR_STREAM_DECODER(Sfixed32, int32_t);
+  _PW_USE_FUNCTIONS_FOR_STREAM_DECODER(Sfixed64, int64_t);
+
+  _PW_USE_FUNCTIONS_FOR_STREAM_DECODER(Float, float);
+  _PW_USE_FUNCTIONS_FOR_STREAM_DECODER(Double, double);
+
+  {
+    Status status;
+    span<char> str_out;
+    span<std::byte> bytes_out;
+    status.Update(decoder.ReadString(str_out).status());
+    status.Update(decoder.ReadBytes(bytes_out).status());
+    status.Update(decoder.GetLengthDelimitedPayloadBounds().status());
+    [[maybe_unused]] volatile Result<uint32_t> field_number =
+        decoder.FieldNumber();
+    [[maybe_unused]] volatile protobuf::StreamDecoder::BytesReader
+        bytes_reader = decoder.GetBytesReader();
+    [[maybe_unused]] volatile bool ok = status.ok();
+  }
+}
+
+#define _PW_USE_FUNCTIONS_FOR_DECODER(type_camel_case, underlying_type) \
+  do {                                                                  \
+    Status status;                                                      \
+    underlying_type val;                                                \
+    status.Update(decoder.PW_CONCAT(Read, type_camel_case)(&val));      \
+    [[maybe_unused]] volatile bool ok = status.ok();                    \
+  } while (0)
+
+void CodeToPullInProtoDecoder() {
+  std::array<std::byte, 3> buffer = {
+      std::byte(0x01), std::byte(0xff), std::byte(0x08)};
+  protobuf::Decoder decoder(buffer);
+  decoder.Next().IgnoreError();
+
+  _PW_USE_FUNCTIONS_FOR_DECODER(Int32, int32_t);
+  _PW_USE_FUNCTIONS_FOR_DECODER(Uint32, uint32_t);
+  _PW_USE_FUNCTIONS_FOR_DECODER(Int64, int64_t);
+  _PW_USE_FUNCTIONS_FOR_DECODER(Uint64, uint64_t);
+
+  _PW_USE_FUNCTIONS_FOR_DECODER(Sint32, int32_t);
+  _PW_USE_FUNCTIONS_FOR_DECODER(Sint64, int64_t);
+
+  _PW_USE_FUNCTIONS_FOR_DECODER(Bool, bool);
+
+  _PW_USE_FUNCTIONS_FOR_DECODER(Fixed32, uint32_t);
+  _PW_USE_FUNCTIONS_FOR_DECODER(Fixed64, uint64_t);
+  _PW_USE_FUNCTIONS_FOR_DECODER(Sfixed32, int32_t);
+  _PW_USE_FUNCTIONS_FOR_DECODER(Sfixed64, int64_t);
+
+  _PW_USE_FUNCTIONS_FOR_DECODER(Float, float);
+  _PW_USE_FUNCTIONS_FOR_DECODER(Double, double);
+
+  {
+    Status status;
+    std::string_view str_out;
+    span<const std::byte> bytes_out;
+    status.Update(decoder.ReadString(&str_out));
+    status.Update(decoder.ReadBytes(&bytes_out));
+    decoder.Reset(buffer);
+    [[maybe_unused]] volatile uint32_t field_number = decoder.FieldNumber();
+    [[maybe_unused]] volatile bool ok = status.ok();
+  }
+}
+
+}  // namespace
+
+void BloatWithBase() {
+  CodeToSetUpSizeReportEnvironment();
+  Dependencies();
+}
+
+void BloatWithEncoder() {
+  BloatWithBase();
+  CodeToPullInProtoEncoder();
+}
+
+void BloatWithTableEncoder() {
+  BloatWithBase();
+  CodeToPullInTableEncoder();
+}
+
+void BloatWithTableDecoder() {
+  BloatWithBase();
+  CodeToPullInTableDecoder();
+}
+
+void BloatWithStreamDecoder() {
+  BloatWithBase();
+  CodeToPullInProtoStreamDecoder();
+}
+
+void BloatWithDecoder() {
+  BloatWithBase();
+  CodeToPullInProtoDecoder();
+}
+
+}  // namespace pw::protobuf_size_report
diff --git a/pw_protobuf/size_report/proto_bloat.h b/pw_protobuf/size_report/proto_bloat.h
new file mode 100644
index 0000000..51654d2
--- /dev/null
+++ b/pw_protobuf/size_report/proto_bloat.h
@@ -0,0 +1,45 @@
+// Copyright 2022 The Pigweed Authors
+//
+// 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
+//
+//     https://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.
+#pragma once
+
+// This namespace is NOT in pw::protobuf to allow filtering of symbols not
+// intended to be reflected in the size report.
+namespace pw::protobuf_size_report {
+
+// Includes ambient glue-related code that is required to set up size reports.
+// Include this in base size reports to prevent irrelevant symbols from showing
+// up in the final diffs.
+void BloatWithBase();
+
+// Adds pw_protobuf's StreamEncoder and MemoryEncoders to the size report.
+void BloatWithEncoder();
+
+// Adds pw_protobuf's StreamDecoder to the size report. This does not include
+// the memory-buffer-only Decoder class, as the implementation is very
+// different.
+void BloatWithStreamDecoder();
+
+// Adds pw_protobuf's Decoder to the size report. This does not include the
+// StreamDecoder class, as the implementation is very different.
+void BloatWithDecoder();
+
+// Adds pw_protobuf's table-based Message encoder to the size report in addition
+// to the StreamEncoder/MemoryEncoder.
+void BloatWithTableEncoder();
+
+// Adds pw_protobuf's table-based Message decoder to the size report in addition
+// to the StreamDecoder.
+void BloatWithTableDecoder();
+
+}  // namespace pw::protobuf_size_report