Reorganize upb file structure

This change moves almost everything in the `upb/` directory up one level, so
that for example `upb/upb/generated_code_support.h` becomes just
`upb/generated_code_support.h`. The only exceptions I made to this were that I
left `upb/cmake` and `upb/BUILD` where they are, mostly because that avoids
conflict with other files and the current locations seem reasonable for now.

The `python/` directory is a little bit of a challenge because we had to merge
the existing directory there with `upb/python/`. I made `upb/python/BUILD` into
the BUILD file for the merged directory, and it effectively loads the contents
of the other BUILD file via `python/build_targets.bzl`, but I plan to clean
this up soon.

PiperOrigin-RevId: 568651768
diff --git a/protos_generator/BUILD b/protos_generator/BUILD
new file mode 100644
index 0000000..fae587a
--- /dev/null
+++ b/protos_generator/BUILD
@@ -0,0 +1,101 @@
+# Copyright (c) 2009-2021, Google LLC
+# All rights reserved.
+#
+# Use of this source code is governed by a BSD-style
+# license that can be found in the LICENSE file or at
+# https://developers.google.com/open-source/licenses/bsd
+
+load(
+    "//bazel:build_defs.bzl",
+    "UPB_DEFAULT_CPPOPTS",
+)
+
+# begin:google_only
+# package(default_applicable_licenses = ["//upb:license"])
+# end:google_only
+
+licenses(["notice"])
+
+cc_binary(
+    name = "protoc-gen-upb-protos",
+    srcs = [
+        "protoc-gen-upb-protos.cc",
+    ],
+    copts = UPB_DEFAULT_CPPOPTS,
+    visibility = ["//visibility:public"],
+    deps = [
+        ":gen_utils",
+        ":generator",
+        ":names",
+        ":output",
+        "//:protobuf",
+        "//src/google/protobuf/compiler:code_generator",
+        "//upbc:file_layout",
+    ],
+)
+
+cc_library(
+    name = "generator",
+    srcs = [
+        "gen_accessors.cc",
+        "gen_enums.cc",
+        "gen_extensions.cc",
+        "gen_messages.cc",
+        "gen_repeated_fields.cc",
+    ],
+    hdrs = [
+        "gen_accessors.h",
+        "gen_enums.h",
+        "gen_extensions.h",
+        "gen_messages.h",
+        "gen_repeated_fields.h",
+    ],
+    visibility = ["//visibility:private"],
+    deps = [
+        ":gen_utils",
+        ":names",
+        ":output",
+        "//:protobuf",
+        "@com_google_absl//absl/container:flat_hash_set",
+        "@com_google_absl//absl/strings",
+        "//upbc:common",
+        "//upbc:file_layout",
+        "//upbc:keywords",
+        "//upbc:names",
+    ],
+)
+
+cc_library(
+    name = "output",
+    srcs = ["output.cc"],
+    hdrs = ["output.h"],
+    visibility = ["//visibility:private"],
+    deps = [
+        "//:protobuf",
+        "@com_google_absl//absl/log:absl_log",
+        "@com_google_absl//absl/strings",
+    ],
+)
+
+cc_library(
+    name = "gen_utils",
+    srcs = ["gen_utils.cc"],
+    hdrs = ["gen_utils.h"],
+    visibility = ["//visibility:public"],
+    deps = [
+        "//:protobuf",
+        "//src/google/protobuf/compiler:code_generator",
+        "@com_google_absl//absl/strings",
+    ],
+)
+
+cc_library(
+    name = "names",
+    srcs = ["names.cc"],
+    hdrs = ["names.h"],
+    visibility = ["//visibility:private"],
+    deps = [
+        ":output",
+        "//upbc:keywords",
+    ],
+)
diff --git a/protos_generator/README.md b/protos_generator/README.md
new file mode 100644
index 0000000..05b64c0
--- /dev/null
+++ b/protos_generator/README.md
@@ -0,0 +1,8 @@
+`protos` Generator
+==================
+
+This directory contains the generator for the [`protos`
+API](https://github.com/protocolbuffers/protobuf/tree/main/protos), an
+experimental C++ protobuf implementation. Most users should use the standard
+C++ implementation
+[here](https://github.com/protocolbuffers/protobuf/tree/main/src).
diff --git a/protos_generator/gen_accessors.cc b/protos_generator/gen_accessors.cc
new file mode 100644
index 0000000..410a42b
--- /dev/null
+++ b/protos_generator/gen_accessors.cc
@@ -0,0 +1,592 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2023 Google LLC.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google LLC nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "protos_generator/gen_accessors.h"
+
+#include <string>
+
+#include "absl/container/flat_hash_set.h"
+#include "absl/strings/match.h"
+#include "absl/strings/str_cat.h"
+#include "absl/strings/string_view.h"
+#include "google/protobuf/descriptor.h"
+#include "protos_generator/gen_repeated_fields.h"
+#include "protos_generator/gen_utils.h"
+#include "protos_generator/names.h"
+#include "protos_generator/output.h"
+#include "upbc/common.h"
+#include "upbc/keywords.h"
+#include "upbc/names.h"
+
+namespace protos_generator {
+
+namespace protobuf = ::google::protobuf;
+
+using NameToFieldDescriptorMap =
+    absl::flat_hash_map<absl::string_view, const protobuf::FieldDescriptor*>;
+
+void WriteFieldAccessorHazzer(const protobuf::Descriptor* desc,
+                              const protobuf::FieldDescriptor* field,
+                              absl::string_view resolved_field_name,
+                              absl::string_view resolved_upbc_name,
+                              Output& output);
+void WriteFieldAccessorClear(const protobuf::Descriptor* desc,
+                             const protobuf::FieldDescriptor* field,
+                             absl::string_view resolved_field_name,
+                             absl::string_view resolved_upbc_name,
+                             Output& output);
+void WriteMapFieldAccessors(const protobuf::Descriptor* desc,
+                            const protobuf::FieldDescriptor* field,
+                            absl::string_view resolved_field_name,
+                            absl::string_view resolved_upbc_name,
+                            Output& output);
+
+void WriteMapAccessorDefinitions(const protobuf::Descriptor* message,
+                                 const protobuf::FieldDescriptor* field,
+                                 absl::string_view resolved_field_name,
+                                 absl::string_view class_name, Output& output);
+
+// Returns C++ class member name by resolving naming conflicts across
+// proto field names (such as clear_ prefixes) and keyword collisions.
+//
+// The Upb C generator prefixes all accessors with package and class names
+// avoiding collisions. Therefore we need to use raw field names when calling
+// into C accessors but need to fully resolve conflicts for C++ class members.
+std::string ResolveFieldName(const protobuf::FieldDescriptor* field,
+                             const NameToFieldDescriptorMap& field_names);
+
+NameToFieldDescriptorMap CreateFieldNameMap(
+    const protobuf::Descriptor* message) {
+  NameToFieldDescriptorMap field_names;
+  for (int i = 0; i < message->field_count(); i++) {
+    const protobuf::FieldDescriptor* field = message->field(i);
+    field_names.emplace(field->name(), field);
+  }
+  return field_names;
+}
+
+void WriteFieldAccessorsInHeader(const protobuf::Descriptor* desc,
+                                 Output& output) {
+  // Generate const methods.
+  OutputIndenter i(output);
+
+  auto field_names = CreateFieldNameMap(desc);
+  auto upbc_field_names = upbc::CreateFieldNameMap(desc);
+
+  for (const auto* field : FieldNumberOrder(desc)) {
+    std::string resolved_field_name = ResolveFieldName(field, field_names);
+    std::string resolved_upbc_name =
+        upbc::ResolveFieldName(field, upbc_field_names);
+    WriteFieldAccessorHazzer(desc, field, resolved_field_name,
+                             resolved_upbc_name, output);
+    WriteFieldAccessorClear(desc, field, resolved_field_name,
+                            resolved_upbc_name, output);
+
+    if (field->is_map()) {
+      WriteMapFieldAccessors(desc, field, resolved_field_name,
+                             resolved_upbc_name, output);
+    } else if (desc->options().map_entry()) {
+      // TODO Implement map entry
+    } else if (field->is_repeated()) {
+      WriteRepeatedFieldsInMessageHeader(desc, field, resolved_field_name,
+                                         resolved_upbc_name, output);
+    } else {
+      // non-repeated.
+      if (field->cpp_type() == protobuf::FieldDescriptor::CPPTYPE_STRING) {
+        output(R"cc(
+                 $0 $1() const;
+                 void set_$1($0 value);
+               )cc",
+               CppConstType(field), resolved_field_name);
+      } else if (field->cpp_type() ==
+                 protobuf::FieldDescriptor::CPPTYPE_MESSAGE) {
+        output(R"cc(
+                 $1 $2() const;
+                 $0 mutable_$2();
+               )cc",
+               MessagePtrConstType(field, /* const */ false),
+               MessagePtrConstType(field, /* const */ true),
+               resolved_field_name, resolved_upbc_name);
+      } else {
+        output(
+            R"cc(
+              inline $0 $1() const { return $2_$3(msg_); }
+              inline void set_$1($0 value) { return $2_set_$3(msg_, value); }
+            )cc",
+            CppConstType(field), resolved_field_name, MessageName(desc),
+            resolved_upbc_name);
+      }
+    }
+  }
+}
+
+void WriteFieldAccessorHazzer(const protobuf::Descriptor* desc,
+                              const protobuf::FieldDescriptor* field,
+                              const absl::string_view resolved_field_name,
+                              const absl::string_view resolved_upbc_name,
+                              Output& output) {
+  // Generate hazzer (if any).
+  if (field->has_presence()) {
+    // Has presence.
+    output("inline bool has_$0() const { return $1_has_$2(msg_); }\n",
+           resolved_field_name, MessageName(desc), resolved_upbc_name);
+  }
+}
+
+void WriteFieldAccessorClear(const protobuf::Descriptor* desc,
+                             const protobuf::FieldDescriptor* field,
+                             const absl::string_view resolved_field_name,
+                             const absl::string_view resolved_upbc_name,
+                             Output& output) {
+  if (field->has_presence()) {
+    output("void clear_$0() { $2_clear_$1(msg_); }\n", resolved_field_name,
+           resolved_upbc_name, MessageName(desc));
+  }
+}
+
+void WriteMapFieldAccessors(const protobuf::Descriptor* desc,
+                            const protobuf::FieldDescriptor* field,
+                            const absl::string_view resolved_field_name,
+                            const absl::string_view resolved_upbc_name,
+                            Output& output) {
+  const protobuf::Descriptor* entry = field->message_type();
+  const protobuf::FieldDescriptor* key = entry->FindFieldByNumber(1);
+  const protobuf::FieldDescriptor* val = entry->FindFieldByNumber(2);
+  output(
+      R"cc(
+        inline size_t $0_size() const { return $1_$3_size(msg_); }
+        inline void clear_$0() { $1_clear_$3(msg_); }
+        void delete_$0($2 key);
+      )cc",
+      resolved_field_name, MessageName(desc), CppConstType(key),
+      resolved_upbc_name);
+
+  if (val->cpp_type() == protobuf::FieldDescriptor::CPPTYPE_MESSAGE) {
+    output(
+        R"cc(
+          bool set_$0($1 key, $3 value);
+          bool set_$0($1 key, $4 value);
+          absl::StatusOr<$3> get_$0($1 key);
+        )cc",
+        resolved_field_name, CppConstType(key), CppConstType(val),
+        MessagePtrConstType(val, /* is_const */ true),
+        MessagePtrConstType(val, /* is_const */ false));
+  } else {
+    output(
+        R"cc(
+          bool set_$0($1 key, $2 value);
+          absl::StatusOr<$2> get_$0($1 key);
+        )cc",
+        resolved_field_name, CppConstType(key), CppConstType(val));
+  }
+}
+
+void WriteAccessorsInSource(const protobuf::Descriptor* desc, Output& output) {
+  std::string class_name = ClassName(desc);
+  absl::StrAppend(&class_name, "Access");
+  output("namespace internal {\n");
+  const char arena_expression[] = "arena_";
+  auto field_names = CreateFieldNameMap(desc);
+  auto upbc_field_names = upbc::CreateFieldNameMap(desc);
+
+  // Generate const methods.
+  OutputIndenter i(output);
+  for (const auto* field : FieldNumberOrder(desc)) {
+    std::string resolved_field_name = ResolveFieldName(field, field_names);
+    std::string resolved_upbc_name =
+        upbc::ResolveFieldName(field, upbc_field_names);
+    if (field->is_map()) {
+      WriteMapAccessorDefinitions(desc, field, resolved_field_name, class_name,
+                                  output);
+    } else if (desc->options().map_entry()) {
+      // TODO Implement map entry
+    } else if (field->is_repeated()) {
+      if (field->cpp_type() == protobuf::FieldDescriptor::CPPTYPE_MESSAGE) {
+        WriteRepeatedMessageAccessor(desc, field, resolved_field_name,
+                                     class_name, output);
+      } else if (field->cpp_type() ==
+                 protobuf::FieldDescriptor::CPPTYPE_STRING) {
+        WriteRepeatedStringAccessor(desc, field, resolved_field_name,
+                                    class_name, output);
+      } else {
+        WriteRepeatedScalarAccessor(desc, field, resolved_field_name,
+                                    class_name, output);
+      }
+    } else {
+      // non-repeated field.
+      if (field->cpp_type() == protobuf::FieldDescriptor::CPPTYPE_STRING) {
+        output(
+            R"cc(
+              $1 $0::$2() const {
+                return ::protos::UpbStrToStringView($3_$4(msg_));
+              }
+            )cc",
+            class_name, CppConstType(field), resolved_field_name,
+            MessageName(desc), resolved_upbc_name);
+        // Set string.
+        output(
+            R"cc(
+              void $0::set_$2($1 value) {
+                $4_set_$3(msg_, ::protos::UpbStrFromStringView(value, $5));
+              }
+            )cc",
+            class_name, CppConstType(field), resolved_field_name,
+            resolved_upbc_name, MessageName(desc), arena_expression);
+      } else if (field->cpp_type() ==
+                 protobuf::FieldDescriptor::CPPTYPE_MESSAGE) {
+        output(
+            R"cc(
+              $1 $0::$2() const {
+                if (!has_$2()) {
+                  return $4::default_instance();
+                }
+                return ::protos::internal::CreateMessage<$4>(
+                    (upb_Message*)($3_$5(msg_)), arena_);
+              }
+            )cc",
+            class_name, MessagePtrConstType(field, /* is_const */ true),
+            resolved_field_name, MessageName(desc),
+            MessageBaseType(field, /* maybe_const */ false),
+            resolved_upbc_name);
+
+        output(
+            R"cc(
+              $1 $0::mutable_$2() {
+                return ::protos::internal::CreateMessageProxy<$4>(
+                    (upb_Message*)($3_mutable_$5(msg_, $6)), $6);
+              }
+            )cc",
+            class_name, MessagePtrConstType(field, /* is_const */ false),
+            resolved_field_name, MessageName(desc),
+            MessageBaseType(field, /* maybe_const */ false), resolved_upbc_name,
+            arena_expression);
+      }
+    }
+  }
+  output("\n");
+  output("}  // namespace internal\n\n");
+}
+
+void WriteMapAccessorDefinitions(const protobuf::Descriptor* message,
+                                 const protobuf::FieldDescriptor* field,
+                                 const absl::string_view resolved_field_name,
+                                 const absl::string_view class_name,
+                                 Output& output) {
+  const protobuf::Descriptor* entry = field->message_type();
+  const protobuf::FieldDescriptor* key = entry->FindFieldByNumber(1);
+  const protobuf::FieldDescriptor* val = entry->FindFieldByNumber(2);
+  absl::string_view upbc_name = field->name();
+  absl::string_view converted_key_name = "key";
+  absl::string_view optional_conversion_code = "";
+
+  if (key->cpp_type() == protobuf::FieldDescriptor::CPPTYPE_STRING) {
+    // Insert conversion from absl::string_view to upb_StringView.
+    // Creates upb_StringView on stack to prevent allocation.
+    converted_key_name = "upb_key";
+    optional_conversion_code =
+        "upb_StringView upb_key = {key.data(), key.size()};\n";
+  }
+  if (val->cpp_type() == protobuf::FieldDescriptor::CPPTYPE_MESSAGE) {
+    output(
+        R"cc(
+          bool $0::set_$1($2 key, $3 value) {
+            upb_Message* clone = upb_Message_DeepClone(
+                ::protos::internal::PrivateAccess::GetInternalMsg(value), &$9,
+                arena_);
+            $6return $4_$8_set(msg_, $7, ($5*)clone, arena_);
+          }
+        )cc",
+        class_name, resolved_field_name, CppConstType(key),
+        MessagePtrConstType(val, /* is_const */ true), MessageName(message),
+        MessageName(val->message_type()), optional_conversion_code,
+        converted_key_name, upbc_name,
+        ::upbc::MessageInit(val->message_type()->full_name()));
+    output(
+        R"cc(
+          bool $0::set_$1($2 key, $3 value) {
+            upb_Message* clone = upb_Message_DeepClone(
+                ::protos::internal::PrivateAccess::GetInternalMsg(value), &$9,
+                arena_);
+            $6return $4_$8_set(msg_, $7, ($5*)clone, arena_);
+          }
+        )cc",
+        class_name, resolved_field_name, CppConstType(key),
+        MessagePtrConstType(val, /* is_const */ false), MessageName(message),
+        MessageName(val->message_type()), optional_conversion_code,
+        converted_key_name, upbc_name,
+        ::upbc::MessageInit(val->message_type()->full_name()));
+    output(
+        R"cc(
+          absl::StatusOr<$3> $0::get_$1($2 key) {
+            $5* msg_value;
+            $7bool success = $4_$9_get(msg_, $8, &msg_value);
+            if (success) {
+              return ::protos::internal::CreateMessage<$6>(msg_value, arena_);
+            }
+            return absl::NotFoundError("");
+          }
+        )cc",
+        class_name, resolved_field_name, CppConstType(key),
+        MessagePtrConstType(val, /* is_const */ true), MessageName(message),
+        MessageName(val->message_type()),
+        QualifiedClassName(val->message_type()), optional_conversion_code,
+        converted_key_name, upbc_name);
+    output(
+        R"cc(
+          void $0::delete_$1($2 key) { $6$4_$8_delete(msg_, $7); }
+        )cc",
+        class_name, resolved_field_name, CppConstType(key),
+        MessagePtrConstType(val, /* is_const */ false), MessageName(message),
+        MessageName(val->message_type()), optional_conversion_code,
+        converted_key_name, upbc_name);
+  } else if (val->cpp_type() == protobuf::FieldDescriptor::CPPTYPE_STRING) {
+    output(
+        R"cc(
+          bool $0::set_$1($2 key, $3 value) {
+            $5return $4_$7_set(msg_, $6,
+                               ::protos::UpbStrFromStringView(value, arena_),
+                               arena_);
+          }
+        )cc",
+        class_name, resolved_field_name, CppConstType(key), CppConstType(val),
+        MessageName(message), optional_conversion_code, converted_key_name,
+        upbc_name);
+    output(
+        R"cc(
+          absl::StatusOr<$3> $0::get_$1($2 key) {
+            upb_StringView value;
+            $5bool success = $4_$7_get(msg_, $6, &value);
+            if (success) {
+              return absl::string_view(value.data, value.size);
+            }
+            return absl::NotFoundError("");
+          }
+        )cc",
+        class_name, resolved_field_name, CppConstType(key), CppConstType(val),
+        MessageName(message), optional_conversion_code, converted_key_name,
+        upbc_name);
+    output(
+        R"cc(
+          void $0::delete_$1($2 key) { $5$4_$7_delete(msg_, $6); }
+        )cc",
+        class_name, resolved_field_name, CppConstType(key), CppConstType(val),
+        MessageName(message), optional_conversion_code, converted_key_name,
+        upbc_name);
+  } else {
+    output(
+        R"cc(
+          bool $0::set_$1($2 key, $3 value) {
+            $5return $4_$7_set(msg_, $6, value, arena_);
+          }
+        )cc",
+        class_name, resolved_field_name, CppConstType(key), CppConstType(val),
+        MessageName(message), optional_conversion_code, converted_key_name,
+        upbc_name);
+    output(
+        R"cc(
+          absl::StatusOr<$3> $0::get_$1($2 key) {
+            $3 value;
+            $5bool success = $4_$7_get(msg_, $6, &value);
+            if (success) {
+              return value;
+            }
+            return absl::NotFoundError("");
+          }
+        )cc",
+        class_name, resolved_field_name, CppConstType(key), CppConstType(val),
+        MessageName(message), optional_conversion_code, converted_key_name,
+        upbc_name);
+    output(
+        R"cc(
+          void $0::delete_$1($2 key) { $5$4_$7_delete(msg_, $6); }
+        )cc",
+        class_name, resolved_field_name, CppConstType(key), CppConstType(val),
+        MessageName(message), optional_conversion_code, converted_key_name,
+        upbc_name);
+  }
+}
+
+void WriteUsingAccessorsInHeader(const protobuf::Descriptor* desc,
+                                 MessageClassType handle_type, Output& output) {
+  bool read_only = handle_type == MessageClassType::kMessageCProxy;
+
+  // Generate const methods.
+  OutputIndenter i(output);
+  std::string class_name = ClassName(desc);
+  auto field_names = CreateFieldNameMap(desc);
+
+  for (const auto* field : FieldNumberOrder(desc)) {
+    std::string resolved_field_name = ResolveFieldName(field, field_names);
+    // Generate hazzer (if any).
+    if (field->has_presence()) {
+      output("using $0Access::has_$1;\n", class_name, resolved_field_name);
+      if (!read_only) {
+        output("using $0Access::clear_$1;\n", class_name, resolved_field_name);
+      }
+    }
+    if (field->is_map()) {
+      output(
+          R"cc(
+            using $0Access::$1_size;
+            using $0Access::get_$1;
+          )cc",
+          class_name, resolved_field_name);
+      if (!read_only) {
+        output(
+            R"cc(
+              using $0Access::clear_$1;
+              using $0Access::delete_$1;
+              using $0Access::set_$1;
+            )cc",
+            class_name, resolved_field_name);
+      }
+    } else if (desc->options().map_entry()) {
+      // TODO Implement map entry
+    } else if (field->is_repeated()) {
+      WriteRepeatedFieldUsingAccessors(field, class_name, resolved_field_name,
+                                       output, read_only);
+    } else {
+      if (field->cpp_type() == protobuf::FieldDescriptor::CPPTYPE_MESSAGE) {
+        output("using $0Access::$1;\n", ClassName(desc), resolved_field_name);
+        if (!read_only) {
+          output("using $0Access::mutable_$1;\n", class_name,
+                 resolved_field_name);
+        }
+      } else {
+        output("using $0Access::$1;\n", class_name, resolved_field_name);
+        if (!read_only) {
+          output("using $0Access::set_$1;\n", class_name, resolved_field_name);
+        }
+      }
+    }
+  }
+  for (int i = 0; i < desc->real_oneof_decl_count(); ++i) {
+    const protobuf::OneofDescriptor* oneof = desc->oneof_decl(i);
+    output("using $0Access::$1_case;\n", class_name, oneof->name());
+    output("using $0Access::$1Case;\n", class_name,
+           ToCamelCase(oneof->name(), /*lower_first=*/false));
+    for (int j = 0; j < oneof->field_count(); ++j) {
+      const protobuf::FieldDescriptor* field = oneof->field(j);
+      output("using $0Access::k$1;\n", class_name,
+             ToCamelCase(field->name(), /*lower_first=*/false),
+             field->number());
+    }
+    output("using $0Access::$1_NOT_SET;\n", class_name,
+           absl::AsciiStrToUpper(oneof->name()));
+  }
+}
+
+void WriteOneofAccessorsInHeader(const protobuf::Descriptor* desc,
+                                 Output& output) {
+  // Generate const methods.
+  OutputIndenter i(output);
+  std::string class_name = ClassName(desc);
+  auto field_names = CreateFieldNameMap(desc);
+  for (int i = 0; i < desc->real_oneof_decl_count(); ++i) {
+    const protobuf::OneofDescriptor* oneof = desc->oneof_decl(i);
+    output("enum $0Case {\n",
+           ToCamelCase(oneof->name(), /*lower_first=*/false));
+    for (int j = 0; j < oneof->field_count(); ++j) {
+      const protobuf::FieldDescriptor* field = oneof->field(j);
+      output("  k$0 = $1,\n", ToCamelCase(field->name(), /*lower_first=*/false),
+             field->number());
+    }
+    output("  $0_NOT_SET = 0,\n", absl::AsciiStrToUpper(oneof->name()));
+    output("};\n\n");
+    output("$0Case $1_case() const {\n",
+           ToCamelCase(oneof->name(), /*lower_first=*/false), oneof->name());
+    for (int j = 0; j < oneof->field_count(); ++j) {
+      const protobuf::FieldDescriptor* field = oneof->field(j);
+      std::string resolved_field_name = ResolveFieldName(field, field_names);
+      output("  if (has_$0()) { return k$1; }\n", resolved_field_name,
+             ToCamelCase(field->name(), /*lower_first=*/false));
+    }
+    output("  return $0_NOT_SET;\n", absl::AsciiStrToUpper(oneof->name()));
+    output("}\n;");
+  }
+}
+
+std::string ResolveFieldName(const protobuf::FieldDescriptor* field,
+                             const NameToFieldDescriptorMap& field_names) {
+  // C++ implementation specific reserved names.
+  static const auto& kReservedNames =
+      *new absl::flat_hash_set<absl::string_view>({
+          "msg",
+          "msg_",
+          "arena",
+          "arena_",
+      });
+
+  // C++ specific prefixes used by code generator for field access.
+  static constexpr absl::string_view kClearMethodPrefix = "clear_";
+  static constexpr absl::string_view kSetMethodPrefix = "set_";
+  static constexpr absl::string_view kHasMethodPrefix = "has_";
+  static constexpr absl::string_view kDeleteMethodPrefix = "delete_";
+  static constexpr absl::string_view kAddToRepeatedMethodPrefix = "add_";
+  static constexpr absl::string_view kResizeArrayMethodPrefix = "resize_";
+
+  // List of generated accessor prefixes to check against.
+  // Example:
+  //     optional repeated string phase = 236;
+  //     optional bool clear_phase = 237;
+  static constexpr absl::string_view kAccessorPrefixes[] = {
+      kClearMethodPrefix,       kDeleteMethodPrefix, kAddToRepeatedMethodPrefix,
+      kResizeArrayMethodPrefix, kSetMethodPrefix,    kHasMethodPrefix};
+
+  absl::string_view field_name = field->name();
+  if (kReservedNames.count(field_name) > 0) {
+    if (absl::EndsWith(field_name, "_")) {
+      return absl::StrCat(field_name, "_");
+    } else {
+      return absl::StrCat(field_name, "__");
+    }
+  }
+  for (const auto prefix : kAccessorPrefixes) {
+    // If field name starts with a prefix such as clear_ and the proto
+    // contains a field name with trailing end, depending on type of field
+    // (repeated, map, message) we have a conflict to resolve.
+    if (absl::StartsWith(field_name, prefix)) {
+      auto match = field_names.find(field_name.substr(prefix.size()));
+      if (match != field_names.end()) {
+        const auto* candidate = match->second;
+        if (candidate->is_repeated() || candidate->is_map() ||
+            (candidate->cpp_type() ==
+                 protobuf::FieldDescriptor::CPPTYPE_STRING &&
+             prefix == kClearMethodPrefix) ||
+            prefix == kSetMethodPrefix || prefix == kHasMethodPrefix) {
+          return absl::StrCat(field_name, "_");
+        }
+      }
+    }
+  }
+  return upbc::ResolveKeywordConflict(std::string(field_name));
+}
+
+}  // namespace protos_generator
diff --git a/protos_generator/gen_accessors.h b/protos_generator/gen_accessors.h
new file mode 100644
index 0000000..8372fc3
--- /dev/null
+++ b/protos_generator/gen_accessors.h
@@ -0,0 +1,51 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2023 Google LLC.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google LLC nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef UPB_PROTOS_GENERATOR_ACCESSORS_H_
+#define UPB_PROTOS_GENERATOR_ACCESSORS_H_
+
+#include "google/protobuf/descriptor.h"
+#include "protos_generator/gen_utils.h"
+#include "protos_generator/output.h"
+
+namespace protos_generator {
+
+namespace protobuf = ::google::protobuf;
+
+void WriteFieldAccessorsInHeader(const protobuf::Descriptor* desc,
+                                 Output& output);
+void WriteAccessorsInSource(const protobuf::Descriptor* desc, Output& output);
+void WriteUsingAccessorsInHeader(const protobuf::Descriptor* desc,
+                                 MessageClassType handle_type, Output& output);
+void WriteOneofAccessorsInHeader(const protobuf::Descriptor* desc,
+                                 Output& output);
+}  // namespace protos_generator
+
+#endif  // UPB_PROTOS_GENERATOR_ACCESSORS_H_
diff --git a/protos_generator/gen_enums.cc b/protos_generator/gen_enums.cc
new file mode 100644
index 0000000..7c535a7
--- /dev/null
+++ b/protos_generator/gen_enums.cc
@@ -0,0 +1,144 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2023 Google LLC.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google LLC nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "protos_generator/gen_enums.h"
+
+#include <algorithm>
+#include <limits>
+#include <string>
+#include <vector>
+
+#include "google/protobuf/descriptor.pb.h"
+#include "google/protobuf/descriptor.h"
+#include "protos_generator/gen_utils.h"
+#include "protos_generator/names.h"
+
+namespace protos_generator {
+
+namespace protobuf = ::google::protobuf;
+
+// Convert enum value to C++ literal.
+//
+// In C++, an value of -2147483648 gets interpreted as the negative of
+// 2147483648, and since 2147483648 can't fit in an integer, this produces a
+// compiler warning.  This works around that issue.
+std::string EnumInt32ToString(int number) {
+  if (number == std::numeric_limits<int32_t>::min()) {
+    // This needs to be special-cased, see explanation here:
+    // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=52661
+    return absl::StrCat(number + 1, " - 1");
+  } else {
+    return absl::StrCat(number);
+  }
+}
+
+std::string EnumTypeName(const protobuf::EnumDescriptor* enum_descriptor) {
+  auto containing_type = enum_descriptor->containing_type();
+  if (containing_type == nullptr) {
+    // enums types with no package name are prefixed with protos_ to prevent
+    // conflicts with generated C headers.
+    if (enum_descriptor->file()->package().empty()) {
+      return absl::StrCat(kNoPackageNamePrefix,
+                          ToCIdent(enum_descriptor->name()));
+    }
+    return ToCIdent(enum_descriptor->name());
+  } else {
+    // Since the enum is in global name space (no package), it will have the
+    // same classified name as the C header include, to prevent collision
+    // rename as above.
+    if (containing_type->file()->package().empty()) {
+      return ToCIdent(absl::StrCat(containing_type->name(), "_",
+                                   kNoPackageNamePrefix,
+                                   enum_descriptor->name()));
+    } else {
+      return ToCIdent(
+          absl::StrCat(containing_type->name(), "_", enum_descriptor->name()));
+    }
+  }
+}
+
+std::string EnumValueSymbolInNameSpace(
+    const protobuf::EnumDescriptor* desc,
+    const protobuf::EnumValueDescriptor* value) {
+  auto containing_type = desc->containing_type();
+  if (containing_type != nullptr) {
+    return ToCIdent(absl::StrCat(containing_type->name(), "_", desc->name(),
+                                 "_", value->name()));
+  } else {
+    // protos enum values with no package name are prefixed with protos_ to
+    // prevent conflicts with generated C headers.
+    if (desc->file()->package().empty()) {
+      return absl::StrCat(kNoPackageNamePrefix, ToCIdent(value->name()));
+    }
+    return ToCIdent(value->name());
+  }
+}
+
+void WriteEnumValues(const protobuf::EnumDescriptor* desc, Output& output) {
+  std::vector<const protobuf::EnumValueDescriptor*> values;
+  auto value_count = desc->value_count();
+  values.reserve(value_count);
+  for (int i = 0; i < value_count; i++) {
+    values.push_back(desc->value(i));
+  }
+  std::sort(values.begin(), values.end(),
+            [](const protobuf::EnumValueDescriptor* a,
+               const protobuf::EnumValueDescriptor* b) {
+              return a->number() < b->number();
+            });
+
+  for (size_t i = 0; i < values.size(); i++) {
+    auto value = values[i];
+    output("  $0", EnumValueSymbolInNameSpace(desc, value));
+    output(" = $0", EnumInt32ToString(value->number()));
+    if (i != values.size() - 1) {
+      output(",");
+    }
+    output("\n");
+  }
+}
+
+void WriteEnumDeclarations(
+    const std::vector<const protobuf::EnumDescriptor*>& enums, Output& output) {
+  for (auto enumdesc : enums) {
+    output("enum $0 : int {\n", EnumTypeName(enumdesc));
+    WriteEnumValues(enumdesc, output);
+    output("};\n\n");
+  }
+}
+
+void WriteHeaderEnumForwardDecls(
+    std::vector<const protobuf::EnumDescriptor*>& enums, Output& output) {
+  for (const auto* enumdesc : enums) {
+    output("enum $0 : int;\n", EnumTypeName(enumdesc));
+  }
+}
+
+}  // namespace protos_generator
diff --git a/protos_generator/gen_enums.h b/protos_generator/gen_enums.h
new file mode 100644
index 0000000..6078762
--- /dev/null
+++ b/protos_generator/gen_enums.h
@@ -0,0 +1,52 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2023 Google LLC.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google LLC nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef UPB_PROTOS_GENERATOR_ENUMS_H_
+#define UPB_PROTOS_GENERATOR_ENUMS_H_
+
+#include "google/protobuf/descriptor.h"
+#include "protos_generator/output.h"
+
+namespace protos_generator {
+
+namespace protobuf = ::google::protobuf;
+
+std::string EnumTypeName(const protobuf::EnumDescriptor* enum_descriptor);
+std::string EnumValueSymbolInNameSpace(
+    const protobuf::EnumDescriptor* desc,
+    const protobuf::EnumValueDescriptor* value);
+void WriteHeaderEnumForwardDecls(
+    std::vector<const protobuf::EnumDescriptor*>& enums, Output& output);
+void WriteEnumDeclarations(
+    const std::vector<const protobuf::EnumDescriptor*>& enums, Output& output);
+
+}  // namespace protos_generator
+
+#endif  // UPB_PROTOS_GENERATOR_ENUMS_H_
diff --git a/protos_generator/gen_extensions.cc b/protos_generator/gen_extensions.cc
new file mode 100644
index 0000000..be0cbc5
--- /dev/null
+++ b/protos_generator/gen_extensions.cc
@@ -0,0 +1,117 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2023 Google LLC.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google LLC nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "protos_generator/gen_extensions.h"
+
+#include "absl/strings/str_cat.h"
+#include "protos_generator/gen_utils.h"
+#include "protos_generator/names.h"
+
+namespace protos_generator {
+
+namespace protobuf = ::google::protobuf;
+
+std::string ExtensionIdentifierBase(const protobuf::FieldDescriptor* ext) {
+  assert(ext->is_extension());
+  std::string ext_scope;
+  if (ext->extension_scope()) {
+    return MessageName(ext->extension_scope());
+  } else {
+    return ToCIdent(ext->file()->package());
+  }
+}
+
+std::string ContainingTypeName(const protobuf::FieldDescriptor* ext) {
+  return ext->containing_type()->file() != ext->file()
+             ? QualifiedClassName(ext->containing_type())
+             : ClassName(ext->containing_type());
+}
+
+void WriteExtensionIdentifierHeader(const protobuf::FieldDescriptor* ext,
+                                    Output& output) {
+  std::string mini_table_name =
+      absl::StrCat(ExtensionIdentifierBase(ext), "_", ext->name(), "_ext");
+  if (ext->extension_scope()) {
+    output(
+        R"cc(
+          static const ::protos::internal::ExtensionIdentifier<$0, $1> $2;
+        )cc",
+        ContainingTypeName(ext), CppTypeParameterName(ext), ext->name());
+  } else {
+    output(
+        R"cc(
+          extern const ::protos::internal::ExtensionIdentifier<$0, $1> $2;
+        )cc",
+        ContainingTypeName(ext), CppTypeParameterName(ext), ext->name());
+  }
+}
+
+void WriteExtensionIdentifiersHeader(
+    const std::vector<const protobuf::FieldDescriptor*>& extensions,
+    Output& output) {
+  for (const auto* ext : extensions) {
+    if (!ext->extension_scope()) {
+      WriteExtensionIdentifierHeader(ext, output);
+    }
+  }
+}
+
+void WriteExtensionIdentifier(const protobuf::FieldDescriptor* ext,
+                              Output& output) {
+  std::string mini_table_name =
+      absl::StrCat(ExtensionIdentifierBase(ext), "_", ext->name(), "_ext");
+  if (ext->extension_scope()) {
+    output(
+        R"cc(
+          const ::protos::internal::ExtensionIdentifier<$0, $3> $4::$2(&$1);
+        )cc",
+        ContainingTypeName(ext), mini_table_name, ext->name(),
+        CppTypeParameterName(ext), ClassName(ext->extension_scope()));
+  } else {
+    output(
+        R"cc(
+          const ::protos::internal::ExtensionIdentifier<$0, $3> $2(&$1);
+        )cc",
+        ContainingTypeName(ext), mini_table_name, ext->name(),
+        CppTypeParameterName(ext));
+  }
+}
+
+void WriteExtensionIdentifiers(
+    const std::vector<const protobuf::FieldDescriptor*>& extensions,
+    Output& output) {
+  for (const auto* ext : extensions) {
+    if (!ext->extension_scope()) {
+      WriteExtensionIdentifier(ext, output);
+    }
+  }
+}
+
+}  // namespace protos_generator
diff --git a/protos_generator/gen_extensions.h b/protos_generator/gen_extensions.h
new file mode 100644
index 0000000..8ec740a
--- /dev/null
+++ b/protos_generator/gen_extensions.h
@@ -0,0 +1,54 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2023 Google LLC.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google LLC nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef UPB_PROTOS_GENERATOR_GEN_EXTENSIONS_H_
+#define UPB_PROTOS_GENERATOR_GEN_EXTENSIONS_H_
+
+#include "google/protobuf/descriptor.h"
+#include "protos_generator/output.h"
+
+namespace protos_generator {
+
+namespace protobuf = ::google::protobuf;
+
+void WriteExtensionIdentifiersHeader(
+    const std::vector<const protobuf::FieldDescriptor*>& extensions,
+    Output& output);
+void WriteExtensionIdentifierHeader(const protobuf::FieldDescriptor* ext,
+                                    Output& output);
+void WriteExtensionIdentifiers(
+    const std::vector<const protobuf::FieldDescriptor*>& extensions,
+    Output& output);
+void WriteExtensionIdentifier(const protobuf::FieldDescriptor* ext,
+                              Output& output);
+
+}  // namespace protos_generator
+
+#endif  // UPB_PROTOS_GENERATOR_GEN_EXTENSIONS_H_
diff --git a/protos_generator/gen_messages.cc b/protos_generator/gen_messages.cc
new file mode 100644
index 0000000..ca75ba7
--- /dev/null
+++ b/protos_generator/gen_messages.cc
@@ -0,0 +1,507 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2023 Google LLC.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google LLC nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "protos_generator/gen_messages.h"
+
+#include <string>
+#include <vector>
+
+#include "google/protobuf/descriptor.pb.h"
+#include "absl/strings/str_cat.h"
+#include "google/protobuf/descriptor.h"
+#include "protos_generator/gen_accessors.h"
+#include "protos_generator/gen_enums.h"
+#include "protos_generator/gen_extensions.h"
+#include "protos_generator/gen_utils.h"
+#include "protos_generator/names.h"
+#include "protos_generator/output.h"
+#include "upbc/common.h"
+#include "upbc/file_layout.h"
+
+namespace protos_generator {
+
+namespace protobuf = ::google::protobuf;
+
+void WriteModelAccessDeclaration(const protobuf::Descriptor* descriptor,
+                                 Output& output);
+void WriteModelPublicDeclaration(
+    const protobuf::Descriptor* descriptor,
+    const std::vector<const protobuf::FieldDescriptor*>& file_exts,
+    const std::vector<const protobuf::EnumDescriptor*>& file_enums,
+    Output& output);
+void WriteExtensionIdentifiersInClassHeader(
+    const protobuf::Descriptor* message,
+    const std::vector<const protobuf::FieldDescriptor*>& file_exts,
+    Output& output);
+void WriteModelProxyDeclaration(const protobuf::Descriptor* descriptor,
+                                Output& output);
+void WriteModelCProxyDeclaration(const protobuf::Descriptor* descriptor,
+                                 Output& output);
+void WriteInternalForwardDeclarationsInHeader(
+    const protobuf::Descriptor* message, Output& output);
+void WriteDefaultInstanceHeader(const protobuf::Descriptor* message,
+                                Output& output);
+void WriteExtensionIdentifiersImplementation(
+    const protobuf::Descriptor* message,
+    const std::vector<const protobuf::FieldDescriptor*>& file_exts,
+    Output& output);
+void WriteUsingEnumsInHeader(
+    const protobuf::Descriptor* message,
+    const std::vector<const protobuf::EnumDescriptor*>& file_enums,
+    Output& output);
+
+// Writes message class declarations into .upb.proto.h.
+//
+// For each proto Foo, FooAccess and FooProxy/FooCProxy are generated
+// that are exposed to users as Foo , Ptr<Foo> and Ptr<const Foo>.
+void WriteMessageClassDeclarations(
+    const protobuf::Descriptor* descriptor,
+    const std::vector<const protobuf::FieldDescriptor*>& file_exts,
+    const std::vector<const protobuf::EnumDescriptor*>& file_enums,
+    Output& output) {
+  if (IsMapEntryMessage(descriptor)) {
+    // Skip map entry generation. Low level accessors for maps are
+    // generated that don't require a separate map type.
+    return;
+  }
+
+  // Forward declaration of Proto Class for GCC handling of free friend method.
+  output("class $0;\n", ClassName(descriptor));
+  output("namespace internal {\n\n");
+  WriteModelAccessDeclaration(descriptor, output);
+  output("\n");
+  WriteInternalForwardDeclarationsInHeader(descriptor, output);
+  output("\n");
+  output("}  // namespace internal\n\n");
+  WriteModelPublicDeclaration(descriptor, file_exts, file_enums, output);
+  output("namespace internal {\n");
+  WriteModelCProxyDeclaration(descriptor, output);
+  WriteModelProxyDeclaration(descriptor, output);
+  output("}  // namespace internal\n\n");
+}
+
+void WriteModelAccessDeclaration(const protobuf::Descriptor* descriptor,
+                                 Output& output) {
+  output(
+      R"cc(
+        class $0Access {
+         public:
+          $0Access() {}
+          $0Access($1* msg, upb_Arena* arena) : msg_(msg), arena_(arena) {
+            assert(arena != nullptr);
+          }  // NOLINT
+          $0Access(const $1* msg, upb_Arena* arena)
+              : msg_(const_cast<$1*>(msg)), arena_(arena) {
+            assert(arena != nullptr);
+          }  // NOLINT
+          void* GetInternalArena() const { return arena_; }
+      )cc",
+      ClassName(descriptor), MessageName(descriptor));
+  WriteFieldAccessorsInHeader(descriptor, output);
+  WriteOneofAccessorsInHeader(descriptor, output);
+  output.Indent();
+  output(
+      R"cc(
+        private:
+        friend class $2;
+        friend class $0Proxy;
+        friend class $0CProxy;
+        friend struct ::protos::internal::PrivateAccess;
+        $1* msg_;
+        upb_Arena* arena_;
+      )cc",
+      ClassName(descriptor), MessageName(descriptor),
+      QualifiedClassName(descriptor));
+  output.Outdent();
+  output("};\n");
+}
+
+void WriteModelPublicDeclaration(
+    const protobuf::Descriptor* descriptor,
+    const std::vector<const protobuf::FieldDescriptor*>& file_exts,
+    const std::vector<const protobuf::EnumDescriptor*>& file_enums,
+    Output& output) {
+  output(
+      R"cc(
+        class $0 final : private internal::$0Access {
+         public:
+          using Access = internal::$0Access;
+          using Proxy = internal::$0Proxy;
+          using CProxy = internal::$0CProxy;
+
+          $0();
+
+          $0(const $0& from);
+          $0& operator=(const $3& from);
+          $0(const CProxy& from);
+          $0(const Proxy& from);
+          $0& operator=(const CProxy& from);
+
+          $0($0&& m)
+              : Access(absl::exchange(m.msg_, nullptr),
+                       absl::exchange(m.arena_, nullptr)),
+                owned_arena_(std::move(m.owned_arena_)) {}
+
+          $0& operator=($0&& m) {
+            msg_ = absl::exchange(m.msg_, nullptr);
+            arena_ = absl::exchange(m.arena_, nullptr);
+            owned_arena_ = std::move(m.owned_arena_);
+            return *this;
+          }
+      )cc",
+      ClassName(descriptor), ::upbc::MessageInit(descriptor->full_name()),
+      MessageName(descriptor), QualifiedClassName(descriptor));
+
+  WriteUsingAccessorsInHeader(descriptor, MessageClassType::kMessage, output);
+  WriteUsingEnumsInHeader(descriptor, file_enums, output);
+  WriteDefaultInstanceHeader(descriptor, output);
+  WriteExtensionIdentifiersInClassHeader(descriptor, file_exts, output);
+  if (descriptor->extension_range_count()) {
+    // for typetrait checking
+    output("using ExtendableType = $0;\n", ClassName(descriptor));
+  }
+  // Note: free function friends that are templates such as ::protos::Parse
+  // require explicit <$2> type parameter in declaration to be able to compile
+  // with gcc otherwise the compiler will fail with
+  // "has not been declared within namespace" error. Even though there is a
+  // namespace qualifier, cross namespace matching fails.
+  output.Indent();
+  output(
+      R"cc(
+        static const upb_MiniTable* minitable();
+        using $0Access::GetInternalArena;
+      )cc",
+      ClassName(descriptor));
+  output("\n");
+  output(
+      R"cc(
+        private:
+        const void* msg() const { return msg_; }
+        void* msg() { return msg_; }
+
+        $0(upb_Message* msg, upb_Arena* arena) : $0Access() {
+          msg_ = ($1*)msg;
+          arena_ = owned_arena_.ptr();
+          upb_Arena_Fuse(arena_, arena);
+        }
+        ::protos::Arena owned_arena_;
+        friend struct ::protos::internal::PrivateAccess;
+        friend Proxy;
+        friend CProxy;
+        friend absl::StatusOr<$2>(::protos::Parse<$2>(absl::string_view bytes,
+                                                      int options));
+        friend absl::StatusOr<$2>(::protos::Parse<$2>(
+            absl::string_view bytes,
+            const ::protos::ExtensionRegistry& extension_registry,
+            int options));
+        friend upb_Arena* ::protos::internal::GetArena<$0>($0* message);
+        friend upb_Arena* ::protos::internal::GetArena<$0>(::protos::Ptr<$0> message);
+        friend $0(::protos::internal::MoveMessage<$0>(upb_Message* msg,
+                                                      upb_Arena* arena));
+      )cc",
+      ClassName(descriptor), MessageName(descriptor),
+      QualifiedClassName(descriptor));
+  output.Outdent();
+  output("};\n\n");
+}
+
+void WriteModelProxyDeclaration(const protobuf::Descriptor* descriptor,
+                                Output& output) {
+  // Foo::Proxy.
+  output(
+      R"cc(
+        class $0Proxy final : private internal::$0Access {
+         public:
+          $0Proxy() = delete;
+          $0Proxy(const $0Proxy& m) : internal::$0Access() {
+            msg_ = m.msg_;
+            arena_ = m.arena_;
+          }
+          $0Proxy($0* m) : internal::$0Access() {
+            msg_ = m->msg_;
+            arena_ = m->arena_;
+          }
+          $0Proxy operator=(const $0Proxy& m) {
+            msg_ = m.msg_;
+            arena_ = m.arena_;
+            return *this;
+          }
+          using $0Access::GetInternalArena;
+      )cc",
+      ClassName(descriptor));
+
+  WriteUsingAccessorsInHeader(descriptor, MessageClassType::kMessageProxy,
+                              output);
+  output("\n");
+  output.Indent(1);
+  output(
+      R"cc(
+        private:
+        void* msg() const { return msg_; }
+
+        $0Proxy(void* msg, upb_Arena* arena) : internal::$0Access(($1*)msg, arena) {}
+        friend $0::Proxy(::protos::CreateMessage<$0>(::protos::Arena& arena));
+        friend $0::Proxy(::protos::internal::CreateMessageProxy<$0>(
+            upb_Message*, upb_Arena*));
+        friend struct ::protos::internal::PrivateAccess;
+        friend class RepeatedFieldProxy;
+        friend class $0CProxy;
+        friend class $0Access;
+        friend class ::protos::Ptr<$0>;
+        friend class ::protos::Ptr<const $0>;
+        static const upb_MiniTable* minitable() { return $0::minitable(); }
+        friend const upb_MiniTable* ::protos::internal::GetMiniTable<$0Proxy>(
+            const $0Proxy* message);
+        friend const upb_MiniTable* ::protos::internal::GetMiniTable<$0Proxy>(
+            ::protos::Ptr<$0Proxy> message);
+        friend upb_Arena* ::protos::internal::GetArena<$2>($2* message);
+        friend upb_Arena* ::protos::internal::GetArena<$2>(::protos::Ptr<$2> message);
+        static void Rebind($0Proxy& lhs, const $0Proxy& rhs) {
+          lhs.msg_ = rhs.msg_;
+          lhs.arena_ = rhs.arena_;
+        }
+      )cc",
+      ClassName(descriptor), MessageName(descriptor),
+      QualifiedClassName(descriptor));
+  output.Outdent(1);
+  output("};\n\n");
+}
+
+void WriteModelCProxyDeclaration(const protobuf::Descriptor* descriptor,
+                                 Output& output) {
+  // Foo::CProxy.
+  output(
+      R"cc(
+        class $0CProxy final : private internal::$0Access {
+         public:
+          $0CProxy() = delete;
+          $0CProxy(const $0* m)
+              : internal::$0Access(m->msg_, ::protos::internal::GetArena(m)) {}
+          $0CProxy($0Proxy m);
+          using $0Access::GetInternalArena;
+      )cc",
+      ClassName(descriptor), MessageName(descriptor));
+
+  WriteUsingAccessorsInHeader(descriptor, MessageClassType::kMessageCProxy,
+                              output);
+
+  output.Indent(1);
+  output(
+      R"cc(
+        private:
+        using AsNonConst = $0Proxy;
+        const void* msg() const { return msg_; }
+
+        $0CProxy(const void* msg, upb_Arena* arena)
+            : internal::$0Access(($1*)msg, arena){};
+        friend struct ::protos::internal::PrivateAccess;
+        friend class RepeatedFieldProxy;
+        friend class ::protos::Ptr<$0>;
+        friend class ::protos::Ptr<const $0>;
+        static const upb_MiniTable* minitable() { return $0::minitable(); }
+        friend const upb_MiniTable* ::protos::internal::GetMiniTable<$0CProxy>(
+            const $0CProxy* message);
+        friend const upb_MiniTable* ::protos::internal::GetMiniTable<$0CProxy>(
+            ::protos::Ptr<$0CProxy> message);
+
+        static void Rebind($0CProxy& lhs, const $0CProxy& rhs) {
+          lhs.msg_ = rhs.msg_;
+          lhs.arena_ = rhs.arena_;
+        }
+      )cc",
+      ClassName(descriptor), MessageName(descriptor));
+  output.Outdent(1);
+  output("};\n\n");
+}
+
+void WriteDefaultInstanceHeader(const protobuf::Descriptor* message,
+                                Output& output) {
+  output("  static ::protos::Ptr<const $0> default_instance();\n",
+         ClassName(message));
+}
+
+void WriteMessageImplementation(
+    const protobuf::Descriptor* descriptor,
+    const std::vector<const protobuf::FieldDescriptor*>& file_exts,
+    Output& output) {
+  bool message_is_map_entry = descriptor->options().map_entry();
+  if (!message_is_map_entry) {
+    // Constructor.
+    output(
+        R"cc(
+          $0::$0() : $0Access() {
+            arena_ = owned_arena_.ptr();
+            msg_ = $1_new(arena_);
+          }
+          $0::$0(const $0& from) : $0Access() {
+            arena_ = owned_arena_.ptr();
+            msg_ = ($1*)::protos::internal::DeepClone(from.msg_, &$2, arena_);
+          }
+          $0::$0(const CProxy& from) : $0Access() {
+            arena_ = owned_arena_.ptr();
+            msg_ = ($1*)::protos::internal::DeepClone(
+                ::protos::internal::GetInternalMsg(&from), &$2, arena_);
+          }
+          $0::$0(const Proxy& from) : $0(static_cast<const CProxy&>(from)) {}
+          internal::$0CProxy::$0CProxy($0Proxy m) : $0Access() {
+            arena_ = m.arena_;
+            msg_ = ($1*)::protos::internal::GetInternalMsg(&m);
+          }
+          $0& $0::operator=(const $3& from) {
+            arena_ = owned_arena_.ptr();
+            msg_ = ($1*)::protos::internal::DeepClone(from.msg_, &$2, arena_);
+            return *this;
+          }
+          $0& $0::operator=(const CProxy& from) {
+            arena_ = owned_arena_.ptr();
+            msg_ = ($1*)::protos::internal::DeepClone(
+                ::protos::internal::GetInternalMsg(&from), &$2, arena_);
+            return *this;
+          }
+        )cc",
+        ClassName(descriptor), MessageName(descriptor),
+        ::upbc::MessageInit(descriptor->full_name()),
+        QualifiedClassName(descriptor));
+    output("\n");
+    // Minitable
+    output(
+        R"cc(
+          const upb_MiniTable* $0::minitable() { return &$1; }
+        )cc",
+        ClassName(descriptor), ::upbc::MessageInit(descriptor->full_name()));
+    output("\n");
+  }
+
+  WriteAccessorsInSource(descriptor, output);
+
+  if (!message_is_map_entry) {
+    output(
+        R"cc(
+          struct $0DefaultTypeInternal {
+            $1* msg;
+            upb_Arena* arena;
+          };
+          static $0DefaultTypeInternal _$0DefaultTypeBuilder() {
+            upb_Arena* arena = upb_Arena_New();
+            return $0DefaultTypeInternal{$1_new(arena), arena};
+          }
+          $0DefaultTypeInternal _$0_default_instance_ = _$0DefaultTypeBuilder();
+        )cc",
+        ClassName(descriptor), MessageName(descriptor));
+
+    output(
+        R"cc(
+          ::protos::Ptr<const $0> $0::default_instance() {
+            return ::protos::internal::CreateMessage<$0>(
+                (upb_Message *)_$0_default_instance_.msg,
+                _$0_default_instance_.arena);
+          }
+        )cc",
+        ClassName(descriptor));
+
+    WriteExtensionIdentifiersImplementation(descriptor, file_exts, output);
+  }
+}
+
+void WriteInternalForwardDeclarationsInHeader(
+    const protobuf::Descriptor* message, Output& output) {
+  // Write declaration for internal re-usable default_instance without
+  // leaking implementation.
+  output(
+      R"cc(
+        struct $0DefaultTypeInternal;
+        extern $0DefaultTypeInternal _$0_default_instance_;
+      )cc",
+      ClassName(message));
+}
+
+void WriteExtensionIdentifiersInClassHeader(
+    const protobuf::Descriptor* message,
+    const std::vector<const protobuf::FieldDescriptor*>& file_exts,
+    Output& output) {
+  for (auto* ext : file_exts) {
+    if (ext->extension_scope() &&
+        ext->extension_scope()->full_name() == message->full_name()) {
+      WriteExtensionIdentifierHeader(ext, output);
+    }
+  }
+}
+
+void WriteExtensionIdentifiersImplementation(
+    const protobuf::Descriptor* message,
+    const std::vector<const protobuf::FieldDescriptor*>& file_exts,
+    Output& output) {
+  for (auto* ext : file_exts) {
+    if (ext->extension_scope() &&
+        ext->extension_scope()->full_name() == message->full_name()) {
+      WriteExtensionIdentifier(ext, output);
+    }
+  }
+}
+
+void WriteUsingEnumsInHeader(
+    const protobuf::Descriptor* message,
+    const std::vector<const protobuf::EnumDescriptor*>& file_enums,
+    Output& output) {
+  for (auto* enum_descriptor : file_enums) {
+    std::string enum_type_name = EnumTypeName(enum_descriptor);
+    std::string enum_resolved_type_name =
+        enum_descriptor->file()->package().empty() &&
+                enum_descriptor->containing_type() == nullptr
+            ? absl::StrCat(kNoPackageNamePrefix,
+                           ToCIdent(enum_descriptor->name()))
+            : enum_type_name;
+    if (enum_descriptor->containing_type() == nullptr ||
+        enum_descriptor->containing_type()->full_name() !=
+            message->full_name()) {
+      continue;
+    }
+    output("using $0", enum_descriptor->name());
+    if (enum_descriptor->options().deprecated()) {
+      output(" ABSL_DEPRECATED(\"Proto enum $0\")", enum_descriptor->name());
+    }
+    output(" = $0;", enum_resolved_type_name);
+    output("\n");
+    int value_count = enum_descriptor->value_count();
+    for (int i = 0; i < value_count; i++) {
+      output("static constexpr $0 $1", enum_descriptor->name(),
+             enum_descriptor->value(i)->name());
+      if (enum_descriptor->options().deprecated() ||
+          enum_descriptor->value(i)->options().deprecated()) {
+        output(" ABSL_DEPRECATED(\"Proto enum value $0\") ",
+               enum_descriptor->value(i)->name());
+      }
+      output(" = $0;\n", EnumValueSymbolInNameSpace(enum_descriptor,
+                                                    enum_descriptor->value(i)));
+    }
+  }
+}
+
+}  // namespace protos_generator
diff --git a/protos_generator/gen_messages.h b/protos_generator/gen_messages.h
new file mode 100644
index 0000000..3236964
--- /dev/null
+++ b/protos_generator/gen_messages.h
@@ -0,0 +1,51 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2023 Google LLC.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google LLC nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef UPB_PROTOS_GENERATOR_GEN_MESSAGES_H_
+#define UPB_PROTOS_GENERATOR_GEN_MESSAGES_H_
+
+#include "google/protobuf/descriptor.h"
+#include "protos_generator/output.h"
+
+namespace protos_generator {
+namespace protobuf = ::google::protobuf;
+
+void WriteMessageClassDeclarations(
+    const protobuf::Descriptor* descriptor,
+    const std::vector<const protobuf::FieldDescriptor*>& file_exts,
+    const std::vector<const protobuf::EnumDescriptor*>& file_enums,
+    Output& output);
+void WriteMessageImplementation(
+    const protobuf::Descriptor* descriptor,
+    const std::vector<const protobuf::FieldDescriptor*>& file_exts,
+    Output& output);
+}  // namespace protos_generator
+
+#endif  // UPB_PROTOS_GENERATOR_GEN_MESSAGES_H_
diff --git a/protos_generator/gen_repeated_fields.cc b/protos_generator/gen_repeated_fields.cc
new file mode 100644
index 0000000..d5b4bab
--- /dev/null
+++ b/protos_generator/gen_repeated_fields.cc
@@ -0,0 +1,347 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2023 Google LLC.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google LLC nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#include "protos_generator/gen_repeated_fields.h"
+
+#include <string>
+#include <vector>
+
+#include "google/protobuf/descriptor.pb.h"
+#include "absl/strings/string_view.h"
+#include "google/protobuf/descriptor.h"
+#include "protos_generator/gen_accessors.h"
+#include "protos_generator/gen_enums.h"
+#include "protos_generator/gen_extensions.h"
+#include "protos_generator/gen_utils.h"
+#include "protos_generator/names.h"
+#include "protos_generator/output.h"
+#include "upbc/common.h"
+#include "upbc/file_layout.h"
+#include "upbc/names.h"
+
+namespace protos_generator {
+namespace protobuf = ::google::protobuf;
+
+// Adds using accessors to reuse base Access class members from a Proxy/CProxy.
+void WriteRepeatedFieldUsingAccessors(const protobuf::FieldDescriptor* field,
+                                      absl::string_view class_name,
+                                      absl::string_view resolved_field_name,
+                                      Output& output, bool read_only) {
+  if (field->cpp_type() == protobuf::FieldDescriptor::CPPTYPE_MESSAGE) {
+    output(
+        R"cc(
+          using $0Access::$1;
+          using $0Access::$1_size;
+        )cc",
+        class_name, resolved_field_name);
+    if (!read_only) {
+      output(
+          R"cc(
+            using $0Access::add_$1;
+            using $0Access::mutable_$1;
+          )cc",
+          class_name, resolved_field_name);
+    }
+  } else {
+    output(
+        R"cc(
+          using $0Access::$1;
+          using $0Access::$1_size;
+        )cc",
+        class_name, resolved_field_name);
+    if (!read_only) {
+      output(
+          R"cc(
+            using $0Access::add_$1;
+            using $0Access::mutable_$1;
+            using $0Access::resize_$1;
+            using $0Access::set_$1;
+          )cc",
+          class_name, resolved_field_name);
+    }
+  }
+}
+
+void WriteRepeatedFieldsInMessageHeader(const protobuf::Descriptor* desc,
+                                        const protobuf::FieldDescriptor* field,
+                                        absl::string_view resolved_field_name,
+                                        absl::string_view resolved_upbc_name,
+                                        Output& output) {
+  output(
+      R"cc(
+        inline size_t $1_size() const {
+          size_t len;
+          $0_$2(msg_, &len);
+          return len;
+        }
+      )cc",
+      MessageName(desc), resolved_field_name, resolved_upbc_name);
+
+  if (field->cpp_type() == protobuf::FieldDescriptor::CPPTYPE_MESSAGE) {
+    output(
+        R"cc(
+          $1 $2(size_t index) const;
+          const ::protos::RepeatedField<const $4>::CProxy $2() const;
+          ::protos::Ptr<::protos::RepeatedField<$4>> mutable_$2();
+          absl::StatusOr<$0> add_$2();
+          $0 mutable_$2(size_t index) const;
+        )cc",
+        MessagePtrConstType(field, /* const */ false),   // $0
+        MessagePtrConstType(field, /* const */ true),    // $1
+        resolved_field_name,                             // $2
+        resolved_upbc_name,                              // $3
+        MessageBaseType(field, /* maybe_const */ false)  // $4
+    );
+  } else if (field->cpp_type() == protobuf::FieldDescriptor::CPPTYPE_STRING) {
+    output(
+        R"cc(
+          $0 $1(size_t index) const;
+          const ::protos::RepeatedField<$0>::CProxy $1() const;
+          ::protos::Ptr<::protos::RepeatedField<$0>> mutable_$1();
+          bool add_$1($0 val);
+          void set_$1(size_t index, $0 val);
+          bool resize_$1(size_t len);
+        )cc",
+        CppConstType(field), resolved_field_name);
+  } else {
+    output(
+        R"cc(
+          $0 $1(size_t index) const;
+          const ::protos::RepeatedField<$0>::CProxy $1() const;
+          ::protos::Ptr<::protos::RepeatedField<$0>> mutable_$1();
+          bool add_$1($0 val);
+          void set_$1(size_t index, $0 val);
+          bool resize_$1(size_t len);
+        )cc",
+        CppConstType(field), resolved_field_name);
+  }
+}
+
+void WriteRepeatedMessageAccessor(const protobuf::Descriptor* message,
+                                  const protobuf::FieldDescriptor* field,
+                                  const absl::string_view resolved_field_name,
+                                  const absl::string_view class_name,
+                                  Output& output) {
+  const char arena_expression[] = "arena_";
+  absl::string_view upbc_name = field->name();
+  output(
+      R"cc(
+        $1 $0::$2(size_t index) const {
+          size_t len;
+          auto* ptr = $3_$5(msg_, &len);
+          assert(index < len);
+          return ::protos::internal::CreateMessage<$4>(
+              (upb_Message*)*(ptr + index), arena_);
+        }
+      )cc",
+      class_name, MessagePtrConstType(field, /* is_const */ true),
+      resolved_field_name, MessageName(message),
+      MessageBaseType(field, /* maybe_const */ false), upbc_name);
+  output(
+      R"cc(
+        absl::StatusOr<$1> $0::add_$2() {
+          auto new_msg = $3_add_$6(msg_, $5);
+          if (!new_msg) {
+            return ::protos::MessageAllocationError();
+          }
+          return ::protos::internal::CreateMessageProxy<$4>((upb_Message*)new_msg, $5);
+        }
+      )cc",
+      class_name, MessagePtrConstType(field, /* const */ false),
+      resolved_field_name, MessageName(message),
+      MessageBaseType(field, /* maybe_const */ false), arena_expression,
+      upbc_name);
+  output(
+      R"cc(
+        $1 $0::mutable_$2(size_t index) const {
+          size_t len;
+          auto* ptr = $3_$6(msg_, &len);
+          assert(index < len);
+          return ::protos::internal::CreateMessageProxy<$4>(
+              (upb_Message*)*(ptr + index), $5);
+        }
+      )cc",
+      class_name, MessagePtrConstType(field, /* is_const */ false),
+      resolved_field_name, MessageName(message),
+      MessageBaseType(field, /* maybe_const */ false), arena_expression,
+      upbc_name);
+  output(
+      R"cc(
+        const ::protos::RepeatedField<const $1>::CProxy $0::$2() const {
+          size_t size;
+          const upb_Array* arr = _$3_$4_$5(msg_, &size);
+          return ::protos::RepeatedField<const $1>::CProxy(arr, arena_);
+        };
+        ::protos::Ptr<::protos::RepeatedField<$1>> $0::mutable_$2() {
+          size_t size;
+          upb_Array* arr = _$3_$4_$6(msg_, &size, arena_);
+          return ::protos::RepeatedField<$1>::Proxy(arr, arena_);
+        }
+      )cc",
+      class_name,                                       // $0
+      MessageBaseType(field, /* maybe_const */ false),  // $1
+      resolved_field_name,                              // $2
+      MessageName(message),                             // $3
+      upbc_name,                                        // $4
+      upbc::kRepeatedFieldArrayGetterPostfix,           // $5
+      upbc::kRepeatedFieldMutableArrayGetterPostfix     // $6
+  );
+}
+
+void WriteRepeatedStringAccessor(const protobuf::Descriptor* message,
+                                 const protobuf::FieldDescriptor* field,
+                                 const absl::string_view resolved_field_name,
+                                 const absl::string_view class_name,
+                                 Output& output) {
+  absl::string_view upbc_name = field->name();
+  output(
+      R"cc(
+        $1 $0::$2(size_t index) const {
+          size_t len;
+          auto* ptr = $3_mutable_$4(msg_, &len);
+          assert(index < len);
+          return ::protos::UpbStrToStringView(*(ptr + index));
+        }
+      )cc",
+      class_name, CppConstType(field), resolved_field_name,
+      MessageName(message), upbc_name);
+  output(
+      R"cc(
+        bool $0::resize_$1(size_t len) {
+          return $2_resize_$3(msg_, len, arena_);
+        }
+      )cc",
+      class_name, resolved_field_name, MessageName(message), upbc_name);
+  output(
+      R"cc(
+        bool $0::add_$2($1 val) {
+          return $3_add_$4(msg_, ::protos::UpbStrFromStringView(val, arena_), arena_);
+        }
+      )cc",
+      class_name, CppConstType(field), resolved_field_name,
+      MessageName(message), upbc_name);
+  output(
+      R"cc(
+        void $0::set_$2(size_t index, $1 val) {
+          size_t len;
+          auto* ptr = $3_mutable_$4(msg_, &len);
+          assert(index < len);
+          *(ptr + index) = ::protos::UpbStrFromStringView(val, arena_);
+        }
+      )cc",
+      class_name, CppConstType(field), resolved_field_name,
+      MessageName(message), upbc_name);
+  output(
+      R"cc(
+        const ::protos::RepeatedField<$1>::CProxy $0::$2() const {
+          size_t size;
+          const upb_Array* arr = _$3_$4_$5(msg_, &size);
+          return ::protos::RepeatedField<$1>::CProxy(arr, arena_);
+        };
+        ::protos::Ptr<::protos::RepeatedField<$1>> $0::mutable_$2() {
+          size_t size;
+          upb_Array* arr = _$3_$4_$6(msg_, &size, arena_);
+          return ::protos::RepeatedField<$1>::Proxy(arr, arena_);
+        }
+      )cc",
+      class_name,                                    // $0
+      CppConstType(field),                           // $1
+      resolved_field_name,                           // $2
+      MessageName(message),                          // $3
+      upbc_name,                                     // $4
+      upbc::kRepeatedFieldArrayGetterPostfix,        // $5
+      upbc::kRepeatedFieldMutableArrayGetterPostfix  // $6
+  );
+}
+
+void WriteRepeatedScalarAccessor(const protobuf::Descriptor* message,
+                                 const protobuf::FieldDescriptor* field,
+                                 const absl::string_view resolved_field_name,
+                                 const absl::string_view class_name,
+                                 Output& output) {
+  absl::string_view upbc_name = field->name();
+  output(
+      R"cc(
+        $1 $0::$2(size_t index) const {
+          size_t len;
+          auto* ptr = $3_mutable_$4(msg_, &len);
+          assert(index < len);
+          return *(ptr + index);
+        }
+      )cc",
+      class_name, CppConstType(field), resolved_field_name,
+      MessageName(message), upbc_name);
+  output(
+      R"cc(
+        bool $0::resize_$1(size_t len) {
+          return $2_resize_$3(msg_, len, arena_);
+        }
+      )cc",
+      class_name, resolved_field_name, MessageName(message), upbc_name);
+  output(
+      R"cc(
+        bool $0::add_$2($1 val) { return $3_add_$4(msg_, val, arena_); }
+      )cc",
+      class_name, CppConstType(field), resolved_field_name,
+      MessageName(message), upbc_name);
+  output(
+      R"cc(
+        void $0::set_$2(size_t index, $1 val) {
+          size_t len;
+          auto* ptr = $3_mutable_$4(msg_, &len);
+          assert(index < len);
+          *(ptr + index) = val;
+        }
+      )cc",
+      class_name, CppConstType(field), resolved_field_name,
+      MessageName(message), upbc_name);
+  output(
+      R"cc(
+        const ::protos::RepeatedField<$1>::CProxy $0::$2() const {
+          size_t size;
+          const upb_Array* arr = _$3_$4_$5(msg_, &size);
+          return ::protos::RepeatedField<$1>::CProxy(arr, arena_);
+        };
+        ::protos::Ptr<::protos::RepeatedField<$1>> $0::mutable_$2() {
+          size_t size;
+          upb_Array* arr = _$3_$4_$6(msg_, &size, arena_);
+          return ::protos::RepeatedField<$1>::Proxy(arr, arena_);
+        }
+      )cc",
+      class_name,                                    // $0
+      CppConstType(field),                           // $1
+      resolved_field_name,                           // $2
+      MessageName(message),                          // $3
+      upbc_name,                                     // $4
+      upbc::kRepeatedFieldArrayGetterPostfix,        // $5
+      upbc::kRepeatedFieldMutableArrayGetterPostfix  // $6
+  );
+}
+
+}  // namespace protos_generator
diff --git a/protos_generator/gen_repeated_fields.h b/protos_generator/gen_repeated_fields.h
new file mode 100644
index 0000000..1650eb0
--- /dev/null
+++ b/protos_generator/gen_repeated_fields.h
@@ -0,0 +1,69 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2023 Google LLC.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google LLC nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef THIRD_PARTY_UPB_PROTOS_GENERATOR_GEN_REPEATED_FIELDS_H_
+#define THIRD_PARTY_UPB_PROTOS_GENERATOR_GEN_REPEATED_FIELDS_H_
+
+#include "absl/strings/string_view.h"
+#include "google/protobuf/descriptor.h"
+#include "protos_generator/output.h"
+
+namespace protos_generator {
+namespace protobuf = ::google::protobuf;
+
+void WriteRepeatedFieldUsingAccessors(const protobuf::FieldDescriptor* field,
+                                      absl::string_view class_name,
+                                      absl::string_view resolved_field_name,
+                                      Output& output, bool read_only);
+
+void WriteRepeatedFieldsInMessageHeader(const protobuf::Descriptor* desc,
+                                        const protobuf::FieldDescriptor* field,
+                                        absl::string_view resolved_field_name,
+                                        absl::string_view resolved_upbc_name,
+                                        Output& output);
+
+void WriteRepeatedMessageAccessor(const protobuf::Descriptor* message,
+                                  const protobuf::FieldDescriptor* field,
+                                  absl::string_view resolved_field_name,
+                                  absl::string_view class_name, Output& output);
+
+void WriteRepeatedStringAccessor(const protobuf::Descriptor* message,
+                                 const protobuf::FieldDescriptor* field,
+                                 absl::string_view resolved_field_name,
+                                 absl::string_view class_name, Output& output);
+
+void WriteRepeatedScalarAccessor(const protobuf::Descriptor* message,
+                                 const protobuf::FieldDescriptor* field,
+                                 absl::string_view resolved_field_name,
+                                 absl::string_view class_name, Output& output);
+
+}  // namespace protos_generator
+
+#endif  // THIRD_PARTY_UPB_PROTOS_GENERATOR_GEN_REPEATED_FIELDS_H_
diff --git a/protos_generator/gen_utils.cc b/protos_generator/gen_utils.cc
new file mode 100644
index 0000000..79dd5a1
--- /dev/null
+++ b/protos_generator/gen_utils.cc
@@ -0,0 +1,152 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2023 Google LLC.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google LLC nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "protos_generator/gen_utils.h"
+
+#include <algorithm>
+#include <string>
+#include <vector>
+
+#include "absl/strings/ascii.h"
+
+namespace protos_generator {
+
+namespace protobuf = ::google::protobuf;
+
+void AddEnums(const protobuf::Descriptor* message,
+              std::vector<const protobuf::EnumDescriptor*>* enums) {
+  enums->reserve(enums->size() + message->enum_type_count());
+  for (int i = 0; i < message->enum_type_count(); i++) {
+    enums->push_back(message->enum_type(i));
+  }
+  for (int i = 0; i < message->nested_type_count(); i++) {
+    AddEnums(message->nested_type(i), enums);
+  }
+}
+
+std::vector<const protobuf::EnumDescriptor*> SortedEnums(
+    const protobuf::FileDescriptor* file) {
+  std::vector<const protobuf::EnumDescriptor*> enums;
+  enums.reserve(file->enum_type_count());
+  for (int i = 0; i < file->enum_type_count(); i++) {
+    enums.push_back(file->enum_type(i));
+  }
+  for (int i = 0; i < file->message_type_count(); i++) {
+    AddEnums(file->message_type(i), &enums);
+  }
+  return enums;
+}
+
+void AddMessages(const protobuf::Descriptor* message,
+                 std::vector<const protobuf::Descriptor*>* messages) {
+  messages->push_back(message);
+  for (int i = 0; i < message->nested_type_count(); i++) {
+    AddMessages(message->nested_type(i), messages);
+  }
+}
+
+std::vector<const protobuf::Descriptor*> SortedMessages(
+    const protobuf::FileDescriptor* file) {
+  std::vector<const protobuf::Descriptor*> messages;
+  for (int i = 0; i < file->message_type_count(); i++) {
+    AddMessages(file->message_type(i), &messages);
+  }
+  return messages;
+}
+
+void AddExtensionsFromMessage(
+    const protobuf::Descriptor* message,
+    std::vector<const protobuf::FieldDescriptor*>* exts) {
+  for (int i = 0; i < message->extension_count(); i++) {
+    exts->push_back(message->extension(i));
+  }
+  for (int i = 0; i < message->nested_type_count(); i++) {
+    AddExtensionsFromMessage(message->nested_type(i), exts);
+  }
+}
+
+std::vector<const protobuf::FieldDescriptor*> SortedExtensions(
+    const protobuf::FileDescriptor* file) {
+  const int extension_count = file->extension_count();
+  const int message_type_count = file->message_type_count();
+
+  std::vector<const protobuf::FieldDescriptor*> ret;
+  ret.reserve(extension_count + message_type_count);
+
+  for (int i = 0; i < extension_count; i++) {
+    ret.push_back(file->extension(i));
+  }
+  for (int i = 0; i < message_type_count; i++) {
+    AddExtensionsFromMessage(file->message_type(i), &ret);
+  }
+
+  return ret;
+}
+
+std::vector<const protobuf::FieldDescriptor*> FieldNumberOrder(
+    const protobuf::Descriptor* message) {
+  std::vector<const protobuf::FieldDescriptor*> fields;
+  fields.reserve(message->field_count());
+  for (int i = 0; i < message->field_count(); i++) {
+    fields.push_back(message->field(i));
+  }
+  std::sort(fields.begin(), fields.end(),
+            [](const protobuf::FieldDescriptor* a,
+               const protobuf::FieldDescriptor* b) {
+              return a->number() < b->number();
+            });
+  return fields;
+}
+
+std::string ToCamelCase(const std::string& input, bool lower_first) {
+  bool capitalize_next = !lower_first;
+  std::string result;
+  result.reserve(input.size());
+
+  for (char character : input) {
+    if (character == '_') {
+      capitalize_next = true;
+    } else if (capitalize_next) {
+      result.push_back(absl::ascii_toupper(character));
+      capitalize_next = false;
+    } else {
+      result.push_back(character);
+    }
+  }
+
+  // Lower-case the first letter.
+  if (lower_first && !result.empty()) {
+    result[0] = absl::ascii_tolower(result[0]);
+  }
+
+  return result;
+}
+
+}  // namespace protos_generator
diff --git a/protos_generator/gen_utils.h b/protos_generator/gen_utils.h
new file mode 100644
index 0000000..71f325f
--- /dev/null
+++ b/protos_generator/gen_utils.h
@@ -0,0 +1,68 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2023 Google LLC.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google LLC nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef UPB_PROTOS_GENERATOR_GEN_UTILS_H_
+#define UPB_PROTOS_GENERATOR_GEN_UTILS_H_
+
+#include <string>
+#include <vector>
+
+#include "google/protobuf/descriptor.pb.h"
+#include "google/protobuf/compiler/code_generator.h"
+#include "google/protobuf/descriptor.h"
+
+namespace protos_generator {
+
+namespace protobuf = ::google::protobuf;
+
+enum class MessageClassType {
+  kMessage,
+  kMessageCProxy,
+  kMessageProxy,
+  kMessageAccess,
+};
+
+inline bool IsMapEntryMessage(const protobuf::Descriptor* descriptor) {
+  return descriptor->options().map_entry();
+}
+std::vector<const protobuf::EnumDescriptor*> SortedEnums(
+    const protobuf::FileDescriptor* file);
+std::vector<const protobuf::Descriptor*> SortedMessages(
+    const protobuf::FileDescriptor* file);
+std::vector<const protobuf::FieldDescriptor*> SortedExtensions(
+    const protobuf::FileDescriptor* file);
+std::vector<const protobuf::FieldDescriptor*> FieldNumberOrder(
+    const protobuf::Descriptor* message);
+
+std::string ToCamelCase(const std::string& input, bool lower_first);
+
+}  // namespace protos_generator
+
+#endif  // UPB_PROTOS_GENERATOR_GEN_UTILS_H_
diff --git a/protos_generator/names.cc b/protos_generator/names.cc
new file mode 100644
index 0000000..eb3fbee
--- /dev/null
+++ b/protos_generator/names.cc
@@ -0,0 +1,201 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2023 Google LLC.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google LLC nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "protos_generator/names.h"
+
+#include <string>
+
+#include "upbc/keywords.h"
+
+namespace protos_generator {
+
+namespace protobuf = ::google::protobuf;
+
+namespace {
+
+std::string NamespaceFromPackageName(absl::string_view package_name) {
+  return absl::StrCat(absl::StrReplaceAll(package_name, {{".", "::"}}),
+                      "::protos");
+}
+
+std::string DotsToColons(const std::string& name) {
+  return absl::StrReplaceAll(name, {{".", "::"}});
+}
+
+std::string Namespace(const std::string& package) {
+  if (package.empty()) return "";
+  return "::" + DotsToColons(package);
+}
+
+// Return the qualified C++ name for a file level symbol.
+std::string QualifiedFileLevelSymbol(const protobuf::FileDescriptor* file,
+                                     const std::string& name) {
+  if (file->package().empty()) {
+    return absl::StrCat("::", name);
+  }
+  // Append ::protos postfix to package name.
+  return absl::StrCat(Namespace(file->package()), "::protos::", name);
+}
+
+std::string CppTypeInternal(const protobuf::FieldDescriptor* field,
+                            bool is_const, bool is_type_parameter) {
+  std::string maybe_const = is_const ? "const " : "";
+  switch (field->cpp_type()) {
+    case protobuf::FieldDescriptor::CPPTYPE_MESSAGE: {
+      if (is_type_parameter) {
+        return absl::StrCat(maybe_const,
+                            QualifiedClassName(field->message_type()));
+      } else {
+        return absl::StrCat(maybe_const,
+                            QualifiedClassName(field->message_type()), "*");
+      }
+    }
+    case protobuf::FieldDescriptor::CPPTYPE_BOOL:
+      return "bool";
+    case protobuf::FieldDescriptor::CPPTYPE_FLOAT:
+      return "float";
+    case protobuf::FieldDescriptor::CPPTYPE_INT32:
+    case protobuf::FieldDescriptor::CPPTYPE_ENUM:
+      return "int32_t";
+    case protobuf::FieldDescriptor::CPPTYPE_UINT32:
+      return "uint32_t";
+    case protobuf::FieldDescriptor::CPPTYPE_DOUBLE:
+      return "double";
+    case protobuf::FieldDescriptor::CPPTYPE_INT64:
+      return "int64_t";
+    case protobuf::FieldDescriptor::CPPTYPE_UINT64:
+      return "uint64_t";
+    case protobuf::FieldDescriptor::CPPTYPE_STRING:
+      return "absl::string_view";
+    default:
+      ABSL_LOG(FATAL) << "Unexpected type: " << field->cpp_type();
+  }
+}
+
+}  // namespace
+
+std::string ClassName(const protobuf::Descriptor* descriptor) {
+  const protobuf::Descriptor* parent = descriptor->containing_type();
+  std::string res;
+  // Classes in global namespace without package names are prefixed
+  // by protos_ to avoid collision with C compiler structs defined in
+  // proto.upb.h.
+  if ((parent && parent->file()->package().empty()) ||
+      descriptor->file()->package().empty()) {
+    res = std::string(kNoPackageNamePrefix);
+  }
+  if (parent) res += ClassName(parent) + "_";
+  absl::StrAppend(&res, descriptor->name());
+  return ::upbc::ResolveKeywordConflict(res);
+}
+
+std::string QualifiedClassName(const protobuf::Descriptor* descriptor) {
+  return QualifiedFileLevelSymbol(descriptor->file(), ClassName(descriptor));
+}
+
+std::string QualifiedInternalClassName(const protobuf::Descriptor* descriptor) {
+  return QualifiedFileLevelSymbol(
+      descriptor->file(), absl::StrCat("internal::", ClassName(descriptor)));
+}
+
+std::string CppSourceFilename(const google::protobuf::FileDescriptor* file) {
+  return StripExtension(file->name()) + ".upb.proto.cc";
+}
+
+std::string ForwardingHeaderFilename(const google::protobuf::FileDescriptor* file) {
+  return StripExtension(file->name()) + ".upb.fwd.h";
+}
+
+std::string UpbCFilename(const google::protobuf::FileDescriptor* file) {
+  return StripExtension(file->name()) + ".upb.h";
+}
+
+std::string CppHeaderFilename(const google::protobuf::FileDescriptor* file) {
+  return StripExtension(file->name()) + ".upb.proto.h";
+}
+
+void WriteStartNamespace(const protobuf::FileDescriptor* file, Output& output) {
+  // Skip namespace generation if package name is not specified.
+  if (file->package().empty()) {
+    return;
+  }
+
+  output("namespace $0 {\n\n", NamespaceFromPackageName(file->package()));
+}
+
+void WriteEndNamespace(const protobuf::FileDescriptor* file, Output& output) {
+  if (file->package().empty()) {
+    return;
+  }
+  output("} //  namespace $0\n\n", NamespaceFromPackageName(file->package()));
+}
+
+std::string CppConstType(const protobuf::FieldDescriptor* field) {
+  return CppTypeInternal(field, /* is_const= */ true,
+                         /* is_type_parameter= */ false);
+}
+
+std::string CppTypeParameterName(const protobuf::FieldDescriptor* field) {
+  return CppTypeInternal(field, /* is_const= */ false,
+                         /* is_type_parameter= */ true);
+}
+
+std::string MessageBaseType(const protobuf::FieldDescriptor* field,
+                            bool is_const) {
+  ABSL_DCHECK(field->cpp_type() == protobuf::FieldDescriptor::CPPTYPE_MESSAGE);
+  std::string maybe_const = is_const ? "const " : "";
+  return maybe_const + QualifiedClassName(field->message_type());
+}
+
+std::string MessagePtrConstType(const protobuf::FieldDescriptor* field,
+                                bool is_const) {
+  ABSL_DCHECK(field->cpp_type() == protobuf::FieldDescriptor::CPPTYPE_MESSAGE);
+  std::string maybe_const = is_const ? "const " : "";
+  return "::protos::Ptr<" + maybe_const +
+         QualifiedClassName(field->message_type()) + ">";
+}
+
+std::string MessageCProxyType(const protobuf::FieldDescriptor* field,
+                              bool is_const) {
+  ABSL_DCHECK(field->cpp_type() == protobuf::FieldDescriptor::CPPTYPE_MESSAGE);
+  std::string maybe_const = is_const ? "const " : "";
+  return maybe_const + QualifiedInternalClassName(field->message_type()) +
+         "CProxy";
+}
+
+std::string MessageProxyType(const protobuf::FieldDescriptor* field,
+                             bool is_const) {
+  ABSL_DCHECK(field->cpp_type() == protobuf::FieldDescriptor::CPPTYPE_MESSAGE);
+  std::string maybe_const = is_const ? "const " : "";
+  return maybe_const + QualifiedInternalClassName(field->message_type()) +
+         "Proxy";
+}
+
+}  // namespace protos_generator
diff --git a/protos_generator/names.h b/protos_generator/names.h
new file mode 100644
index 0000000..efa01ce
--- /dev/null
+++ b/protos_generator/names.h
@@ -0,0 +1,73 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2023 Google LLC.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google LLC nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef UPB_PROTOS_GENERATOR_NAMES_H_
+#define UPB_PROTOS_GENERATOR_NAMES_H_
+
+#include <string>
+
+#include "google/protobuf/descriptor.pb.h"
+#include "protos_generator/output.h"
+
+namespace protos_generator {
+
+namespace protobuf = ::google::protobuf;
+
+inline constexpr absl::string_view kNoPackageNamePrefix = "protos_";
+
+std::string ClassName(const protobuf::Descriptor* descriptor);
+std::string QualifiedClassName(const protobuf::Descriptor* descriptor);
+std::string QualifiedInternalClassName(const protobuf::Descriptor* descriptor);
+
+std::string CppSourceFilename(const google::protobuf::FileDescriptor* file);
+std::string ForwardingHeaderFilename(const google::protobuf::FileDescriptor* file);
+std::string UpbCFilename(const google::protobuf::FileDescriptor* file);
+std::string CppHeaderFilename(const google::protobuf::FileDescriptor* file);
+
+void WriteStartNamespace(const protobuf::FileDescriptor* file, Output& output);
+void WriteEndNamespace(const protobuf::FileDescriptor* file, Output& output);
+
+std::string CppConstType(const protobuf::FieldDescriptor* field);
+std::string CppTypeParameterName(const protobuf::FieldDescriptor* field);
+
+std::string MessageBaseType(const protobuf::FieldDescriptor* field,
+                            bool is_const);
+// Generate protos::Ptr<const Model> to be used in accessors as public
+// signatures.
+std::string MessagePtrConstType(const protobuf::FieldDescriptor* field,
+                                bool is_const);
+std::string MessageCProxyType(const protobuf::FieldDescriptor* field,
+                              bool is_const);
+std::string MessageProxyType(const protobuf::FieldDescriptor* field,
+                             bool is_const);
+
+}  // namespace protos_generator
+
+#endif  // UPB_PROTOS_GENERATOR_NAMES_H_
diff --git a/protos_generator/output.cc b/protos_generator/output.cc
new file mode 100644
index 0000000..72cd4e5
--- /dev/null
+++ b/protos_generator/output.cc
@@ -0,0 +1,92 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2023 Google LLC.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google LLC nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "protos_generator/output.h"
+
+#include <string>
+
+#include "absl/strings/str_replace.h"
+
+namespace protos_generator {
+namespace {
+
+namespace protobuf = ::google::protobuf;
+
+}  // namespace
+
+std::string StripExtension(absl::string_view fname) {
+  size_t lastdot = fname.find_last_of('.');
+  if (lastdot == std::string::npos) {
+    return std::string(fname);
+  }
+  return std::string(fname.substr(0, lastdot));
+}
+
+std::string ToCIdent(absl::string_view str) {
+  return absl::StrReplaceAll(str, {{".", "_"}, {"/", "_"}, {"-", "_"}});
+}
+
+std::string ToPreproc(absl::string_view str) {
+  return absl::AsciiStrToUpper(ToCIdent(str));
+}
+
+void EmitFileWarning(const protobuf::FileDescriptor* file, Output& output) {
+  output(
+      R"cc(
+        /* This file was generated by protos_generator (the upb C++ compiler) "
+        from the input
+         * file:
+         *
+         *     $0
+         *
+         * Do not edit -- your changes will be discarded when the file is
+         * regenerated. */
+      )cc",
+      file->name());
+  output("\n");
+}
+
+std::string MessageName(const protobuf::Descriptor* descriptor) {
+  return ToCIdent(descriptor->full_name());
+}
+
+std::string FileLayoutName(const google::protobuf::FileDescriptor* file) {
+  return ToCIdent(file->name()) + "_upb_file_layout";
+}
+
+std::string CHeaderFilename(const google::protobuf::FileDescriptor* file) {
+  return StripExtension(file->name()) + ".upb.h";
+}
+
+std::string CSourceFilename(const google::protobuf::FileDescriptor* file) {
+  return StripExtension(file->name()) + ".upb.c";
+}
+
+}  // namespace protos_generator
diff --git a/protos_generator/output.h b/protos_generator/output.h
new file mode 100644
index 0000000..53f0720
--- /dev/null
+++ b/protos_generator/output.h
@@ -0,0 +1,174 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2023 Google LLC.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google LLC nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef UPB_PROTOS_GENERATOR_OUTPUT_H
+#define UPB_PROTOS_GENERATOR_OUTPUT_H
+
+#include <vector>
+
+#include "absl/log/absl_log.h"
+#include "absl/strings/str_replace.h"
+#include "absl/strings/substitute.h"
+#include "google/protobuf/descriptor.h"
+#include "google/protobuf/io/zero_copy_stream.h"
+
+namespace protos_generator {
+
+class Output {
+ public:
+  Output(google::protobuf::io::ZeroCopyOutputStream* stream) : stream_(stream) {}
+  ~Output() { stream_->BackUp((int)buffer_size_); }
+
+  template <class... Arg>
+  void operator()(absl::string_view format, const Arg&... arg) {
+    Write(absl::Substitute(format, arg...));
+  }
+
+  // Indentation size in characters.
+  static constexpr size_t kIndentationSize = 2;
+
+  void Indent() { Indent(kIndentationSize); }
+  void Indent(size_t size) { indent_ += size; }
+
+  void Outdent() { Outdent(kIndentationSize); }
+  void Outdent(size_t size) {
+    if (indent_ < size) {
+      ABSL_LOG(FATAL) << "mismatched Output indent/unindent calls";
+    }
+    indent_ -= size;
+  }
+
+ private:
+  void Write(absl::string_view data) {
+    std::string stripped;
+    if (absl::StartsWith(data, "\n ")) {
+      size_t indent = data.substr(1).find_first_not_of(' ');
+      if (indent > indent_) {
+        indent -= indent_;
+      }
+      if (indent != absl::string_view::npos) {
+        // Remove indentation from all lines.
+        auto line_prefix = data.substr(0, indent + 1);
+        // The final line has an extra newline and is indented two less, eg.
+        //    R"cc(
+        //      UPB_INLINE $0 $1_$2(const $1 *msg) {
+        //        return $1_has_$2(msg) ? *UPB_PTR_AT(msg, $3, $0) : $4;
+        //      }
+        //    )cc",
+        std::string last_line_prefix = std::string(line_prefix);
+        last_line_prefix.resize(last_line_prefix.size() - 2);
+        data.remove_prefix(line_prefix.size());
+        stripped = absl::StrReplaceAll(
+            data, {{line_prefix, "\n"}, {last_line_prefix, "\n"}});
+        data = stripped;
+      }
+    } else {
+      WriteIndent();
+    }
+    WriteRaw(data);
+  }
+
+  void WriteRaw(absl::string_view data) {
+    while (!data.empty()) {
+      RefreshOutput();
+      size_t to_write = std::min(data.size(), buffer_size_);
+      memcpy(output_buffer_, data.data(), to_write);
+      data.remove_prefix(to_write);
+      output_buffer_ += to_write;
+      buffer_size_ -= to_write;
+    }
+  }
+
+  void WriteIndent() {
+    if (indent_ == 0) {
+      return;
+    }
+    size_t size = indent_;
+    while (size > buffer_size_) {
+      if (buffer_size_ > 0) {
+        memset(output_buffer_, ' ', buffer_size_);
+      }
+      size -= buffer_size_;
+      buffer_size_ = 0;
+      RefreshOutput();
+    }
+    memset(output_buffer_, ' ', size);
+    output_buffer_ += size;
+    buffer_size_ -= size;
+  }
+
+  void RefreshOutput() {
+    while (buffer_size_ == 0) {
+      void* void_buffer;
+      int size;
+      if (!stream_->Next(&void_buffer, &size)) {
+        fprintf(stderr, "upbc: Failed to write to to output\n");
+        abort();
+      }
+      output_buffer_ = static_cast<char*>(void_buffer);
+      buffer_size_ = size;
+    }
+  }
+
+  google::protobuf::io::ZeroCopyOutputStream* stream_;
+  char* output_buffer_ = nullptr;
+  size_t buffer_size_ = 0;
+  // Current indentation size in characters.
+  size_t indent_ = 0;
+  friend class OutputIndenter;
+};
+
+class OutputIndenter {
+ public:
+  OutputIndenter(Output& output)
+      : OutputIndenter(output, Output::kIndentationSize) {}
+  OutputIndenter(Output& output, size_t indent_size)
+      : indent_size_(indent_size), output_(output) {
+    output.Indent(indent_size);
+  }
+  ~OutputIndenter() { output_.Outdent(indent_size_); }
+
+ private:
+  size_t indent_size_;
+  Output& output_;
+};
+
+std::string StripExtension(absl::string_view fname);
+std::string ToCIdent(absl::string_view str);
+std::string ToPreproc(absl::string_view str);
+void EmitFileWarning(const google::protobuf::FileDescriptor* file, Output& output);
+std::string MessageName(const google::protobuf::Descriptor* descriptor);
+std::string FileLayoutName(const google::protobuf::FileDescriptor* file);
+std::string CHeaderFilename(const google::protobuf::FileDescriptor* file);
+std::string CSourceFilename(const google::protobuf::FileDescriptor* file);
+
+}  // namespace protos_generator
+
+#endif  // UPB_PROTOS_GENERATOR_OUTPUT_H
diff --git a/protos_generator/protoc-gen-upb-protos.cc b/protos_generator/protoc-gen-upb-protos.cc
new file mode 100644
index 0000000..58c3712
--- /dev/null
+++ b/protos_generator/protoc-gen-upb-protos.cc
@@ -0,0 +1,282 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2023 Google LLC.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google LLC nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include <memory>
+
+#include "google/protobuf/descriptor.pb.h"
+#include "google/protobuf/compiler/code_generator.h"
+#include "google/protobuf/compiler/plugin.h"
+#include "google/protobuf/descriptor.h"
+#include "protos_generator/gen_enums.h"
+#include "protos_generator/gen_extensions.h"
+#include "protos_generator/gen_messages.h"
+#include "protos_generator/gen_utils.h"
+#include "protos_generator/names.h"
+#include "protos_generator/output.h"
+#include "upbc/file_layout.h"
+
+namespace protos_generator {
+namespace {
+
+namespace protoc = ::google::protobuf::compiler;
+namespace protobuf = ::google::protobuf;
+using FileDescriptor = ::google::protobuf::FileDescriptor;
+
+void WriteSource(const protobuf::FileDescriptor* file, Output& output,
+                 bool fasttable_enabled);
+void WriteHeader(const protobuf::FileDescriptor* file, Output& output);
+void WriteForwardingHeader(const protobuf::FileDescriptor* file,
+                           Output& output);
+void WriteMessageImplementations(const protobuf::FileDescriptor* file,
+                                 Output& output);
+void WriteTypedefForwardingHeader(
+    const protobuf::FileDescriptor* file,
+    const std::vector<const protobuf::Descriptor*>& file_messages,
+    Output& output);
+void WriteHeaderMessageForwardDecls(
+    const protobuf::FileDescriptor* file,
+    Output& output);
+
+class Generator : public protoc::CodeGenerator {
+ public:
+  ~Generator() override {}
+  bool Generate(const protobuf::FileDescriptor* file,
+                const std::string& parameter, protoc::GeneratorContext* context,
+                std::string* error) const override;
+  uint64_t GetSupportedFeatures() const override {
+    return FEATURE_PROTO3_OPTIONAL;
+  }
+};
+
+bool Generator::Generate(const protobuf::FileDescriptor* file,
+                         const std::string& parameter,
+                         protoc::GeneratorContext* context,
+                         std::string* error) const {
+  bool fasttable_enabled = false;
+  std::vector<std::pair<std::string, std::string>> params;
+  google::protobuf::compiler::ParseGeneratorParameter(parameter, &params);
+
+  for (const auto& pair : params) {
+    if (pair.first == "fasttable") {
+      fasttable_enabled = true;
+    } else {
+      *error = "Unknown parameter: " + pair.first;
+      return false;
+    }
+  }
+
+  // Write model.upb.fwd.h
+  Output forwarding_header_output(
+      context->Open(ForwardingHeaderFilename(file)));
+  WriteForwardingHeader(file, forwarding_header_output);
+  // Write model.upb.proto.h
+  Output header_output(context->Open(CppHeaderFilename(file)));
+  WriteHeader(file, header_output);
+  // Write model.upb.proto.cc
+  Output cc_output(context->Open(CppSourceFilename(file)));
+  WriteSource(file, cc_output, fasttable_enabled);
+  return true;
+}
+
+// The forwarding header defines Access/Proxy/CProxy for message classes
+// used to include when referencing dependencies to prevent transitive
+// dependency headers from being included.
+void WriteForwardingHeader(const protobuf::FileDescriptor* file,
+                           Output& output) {
+  EmitFileWarning(file, output);
+  output(
+      R"cc(
+#ifndef $0_UPB_FWD_H_
+#define $0_UPB_FWD_H_
+      )cc",
+      ToPreproc(file->name()));
+  output("\n");
+  for (int i = 0; i < file->public_dependency_count(); ++i) {
+    output("#include \"$0\"\n",
+           ForwardingHeaderFilename(file->public_dependency(i)));
+  }
+  if (file->public_dependency_count() > 0) {
+    output("\n");
+  }
+  const std::vector<const protobuf::Descriptor*> this_file_messages =
+      SortedMessages(file);
+  WriteTypedefForwardingHeader(file, this_file_messages, output);
+  output("#endif  /* $0_UPB_FWD_H_ */\n", ToPreproc(file->name()));
+}
+
+void WriteHeader(const protobuf::FileDescriptor* file, Output& output) {
+  EmitFileWarning(file, output);
+  output(
+      R"cc(
+#ifndef $0_UPB_PROTO_H_
+#define $0_UPB_PROTO_H_
+
+#include "protos/protos.h"
+#include "protos/protos_internal.h"
+#include "protos/repeated_field.h"
+
+#include "absl/strings/string_view.h"
+#include "absl/status/statusor.h"
+      )cc",
+      ToPreproc(file->name()));
+
+  // Import headers for proto public dependencies.
+  for (int i = 0; i < file->public_dependency_count(); i++) {
+    if (i == 0) {
+      output("// Public Imports.\n");
+    }
+    output("#include \"$0\"\n", CppHeaderFilename(file->public_dependency(i)));
+    if (i == file->public_dependency_count() - 1) {
+      output("\n");
+    }
+  }
+
+  output("#include \"upb/port/def.inc\"\n");
+
+  const std::vector<const protobuf::Descriptor*> this_file_messages =
+      SortedMessages(file);
+  const std::vector<const protobuf::FieldDescriptor*> this_file_exts =
+      SortedExtensions(file);
+
+  if (!this_file_messages.empty()) {
+    output("\n");
+  }
+
+  WriteHeaderMessageForwardDecls(file, output);
+  WriteStartNamespace(file, output);
+
+  std::vector<const protobuf::EnumDescriptor*> this_file_enums =
+      SortedEnums(file);
+
+  // Write Class and Enums.
+  WriteEnumDeclarations(this_file_enums, output);
+  output("\n");
+
+  for (auto message : this_file_messages) {
+    WriteMessageClassDeclarations(message, this_file_exts, this_file_enums,
+                                  output);
+  }
+  output("\n");
+
+  WriteExtensionIdentifiersHeader(this_file_exts, output);
+  output("\n");
+
+  WriteEndNamespace(file, output);
+
+  output("\n#include \"upb/port/undef.inc\"\n\n");
+  // End of "C" section.
+
+  output("#endif  /* $0_UPB_PROTO_H_ */\n", ToPreproc(file->name()));
+}
+
+// Writes a .upb.cc source file.
+void WriteSource(const protobuf::FileDescriptor* file, Output& output,
+                 bool fasttable_enabled) {
+  EmitFileWarning(file, output);
+
+  output(
+      R"cc(
+#include <stddef.h>
+#include "absl/strings/string_view.h"
+#include "protos/protos.h"
+#include "$0"
+      )cc",
+      CppHeaderFilename(file));
+
+  for (int i = 0; i < file->dependency_count(); i++) {
+    output("#include \"$0\"\n", CppHeaderFilename(file->dependency(i)));
+  }
+  output("#include \"upb/port/def.inc\"\n");
+
+  WriteStartNamespace(file, output);
+  WriteMessageImplementations(file, output);
+  const std::vector<const protobuf::FieldDescriptor*> this_file_exts =
+      SortedExtensions(file);
+  WriteExtensionIdentifiers(this_file_exts, output);
+  WriteEndNamespace(file, output);
+
+  output("#include \"upb/port/undef.inc\"\n\n");
+}
+
+void WriteMessageImplementations(const protobuf::FileDescriptor* file,
+                                 Output& output) {
+  const std::vector<const protobuf::FieldDescriptor*> file_exts =
+      SortedExtensions(file);
+  const std::vector<const protobuf::Descriptor*> this_file_messages =
+      SortedMessages(file);
+  for (auto message : this_file_messages) {
+    WriteMessageImplementation(message, file_exts, output);
+  }
+}
+
+void WriteTypedefForwardingHeader(
+    const protobuf::FileDescriptor* file,
+    const std::vector<const protobuf::Descriptor*>& file_messages,
+    Output& output) {
+  WriteStartNamespace(file, output);
+
+  // Forward-declare types defined in this file.
+  for (auto message : file_messages) {
+    output(
+        R"cc(
+          class $0;
+          namespace internal {
+          class $0Access;
+          class $0Proxy;
+          class $0CProxy;
+          }  // namespace internal
+        )cc",
+        ClassName(message));
+  }
+  output("\n");
+  WriteEndNamespace(file, output);
+}
+
+/// Writes includes for upb C minitables and fwd.h for transitive typedefs.
+void WriteHeaderMessageForwardDecls(
+    const protobuf::FileDescriptor* file,
+    Output& output) {
+  // Import forward-declaration of types defined in this file.
+  output("#include \"$0\"\n", UpbCFilename(file));
+  output("#include \"$0\"\n", ForwardingHeaderFilename(file));
+  // Import forward-declaration of types in dependencies.
+  for (int i = 0; i < file->dependency_count(); ++i) {
+    output("#include \"$0\"\n", ForwardingHeaderFilename(file->dependency(i)));
+  }
+  output("\n");
+}
+
+}  // namespace
+}  // namespace protos_generator
+
+int main(int argc, char** argv) {
+  protos_generator::Generator generator_cc;
+  return google::protobuf::compiler::PluginMain(argc, argv, &generator_cc);
+}
diff --git a/protos_generator/tests/BUILD b/protos_generator/tests/BUILD
new file mode 100644
index 0000000..e5c203f
--- /dev/null
+++ b/protos_generator/tests/BUILD
@@ -0,0 +1,140 @@
+# Copyright (c) 2009-2021, Google LLC
+# All rights reserved.
+#
+# Use of this source code is governed by a BSD-style
+# license that can be found in the LICENSE file or at
+# https://developers.google.com/open-source/licenses/bsd
+
+load(
+    "//bazel:build_defs.bzl",
+    "UPB_DEFAULT_CPPOPTS",
+)
+load(
+    "//bazel:upb_proto_library.bzl",
+    "upb_proto_library",
+)
+load(
+    "//protos/bazel:upb_cc_proto_library.bzl",
+    "upb_cc_proto_library",
+)
+load(
+    "@rules_cc//cc:defs.bzl",
+    "cc_proto_library",
+)
+
+# begin:google_only
+# package(default_applicable_licenses = ["//upb:license"])
+# end:google_only
+
+licenses(["notice"])
+
+proto_library(
+    name = "test_model_proto",
+    srcs = [
+        "child_model.proto",
+        "test_enum.proto",
+        "test_extension.proto",
+        "test_model.proto",
+    ],
+)
+
+proto_library(
+    name = "no_package_proto",
+    srcs = [
+        "no_package.proto",
+    ],
+)
+
+proto_library(
+    name = "naming_conflict_proto",
+    srcs = [
+        "naming_conflict.proto",
+    ],
+)
+
+proto_library(
+    name = "no_package_enum_user_proto",
+    srcs = [
+        "no_package_enum_user.proto",
+    ],
+    deps = [":no_package_proto"],
+)
+
+upb_proto_library(
+    name = "test_model_upb_proto",
+    visibility = [
+        "//protos:__pkg__",
+    ],
+    deps = [":test_model_proto"],
+)
+
+upb_cc_proto_library(
+    name = "test_model_upb_cc_proto",
+    visibility = ["//protos:__pkg__"],
+    deps = [":test_model_proto"],
+)
+
+upb_cc_proto_library(
+    name = "naming_conflict_upb_cc_proto",
+    visibility = [
+        "//visibility:private",  # Only private by automation, not intent. Owner may accept CLs adding visibility. See go/scheuklappen#explicit-private.
+    ],
+    deps = [":naming_conflict_proto"],
+)
+
+upb_cc_proto_library(
+    name = "no_package_upb_cc_proto",
+    deps = [
+        ":no_package_proto",
+    ],
+)
+
+upb_cc_proto_library(
+    name = "no_package_enum_user_upb_cc_proto",
+    deps = [
+        ":no_package_enum_user_proto",
+    ],
+)
+
+cc_proto_library(
+    name = "test_model_cc_proto",
+    deps = [":test_model_proto"],
+)
+
+# begin:google_only
+# proto_library(
+#     name = "legacy_name_proto",
+#     srcs = [
+#         "legacy-name.proto",
+#     ],
+# )
+#
+# upb_cc_proto_library(
+#     name = "legacy_name_test_proto",
+#     visibility = [
+#         "//visibility:private",  # Only private by automation, not intent. Owner may accept CLs adding visibility. See go/scheuklappen#explicit-private.
+#     ],
+#     deps = [":legacy_name_proto"],
+# )
+# end:google_only
+
+cc_test(
+    name = "test_generated_cc_code",
+    srcs = ["test_generated.cc"],
+    copts = UPB_DEFAULT_CPPOPTS,
+    deps = [
+        # begin:google_only
+#         ":legacy_name_test_proto",
+        # end:google_only
+        ":no_package_upb_cc_proto",
+        ":test_model_upb_cc_proto",
+        ":test_model_upb_proto",
+        ":naming_conflict_upb_cc_proto",
+        "@com_google_googletest//:gtest_main",
+        "@com_google_absl//absl/status:statusor",
+        "@com_google_absl//absl/strings",
+        "//protos",
+        "//upb:mem",
+        "//protos:repeated_field",
+    ],
+)
diff --git a/protos_generator/tests/child_model.proto b/protos_generator/tests/child_model.proto
new file mode 100644
index 0000000..c7af6f1
--- /dev/null
+++ b/protos_generator/tests/child_model.proto
@@ -0,0 +1,49 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2023 Google LLC.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google LLC nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+syntax = "proto3";
+
+package protos_generator.test;
+
+import public "protos_generator/tests/test_enum.proto";
+
+message ChildModel1 {
+  optional bool child_b1 = 44;
+  optional string child_str1 = 56;
+}
+
+message ChildModel3 {
+  string sub_key = 1;
+  bool bool1 = 2;
+  int32 i32 = 3;
+  optional string opt_str = 4;
+  optional bool opt_bool = 5;
+  optional int32 opt_i32 = 6;
+}
diff --git a/protos_generator/tests/legacy-name.proto b/protos_generator/tests/legacy-name.proto
new file mode 100644
index 0000000..0f256ad
--- /dev/null
+++ b/protos_generator/tests/legacy-name.proto
@@ -0,0 +1,40 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2023 Google LLC.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google LLC nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+syntax = "proto3";
+
+package protos_generator.test;
+
+// option java_multiple_files = true;
+
+enum LegacyEnum {
+  PHASE_DEFAULT = 0;
+  PHASE_BUSY = 1;
+}
diff --git a/protos_generator/tests/naming_conflict.proto b/protos_generator/tests/naming_conflict.proto
new file mode 100644
index 0000000..414f7c6
--- /dev/null
+++ b/protos_generator/tests/naming_conflict.proto
@@ -0,0 +1,38 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2023 Google LLC.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google LLC nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+syntax = "proto3";
+
+package protos_generator.test;
+
+message HasChildCount {
+  optional HasChildCount has_child_count = 1;
+  optional int32 child_count = 2;
+}
diff --git a/protos_generator/tests/no_package.proto b/protos_generator/tests/no_package.proto
new file mode 100644
index 0000000..37d6df8
--- /dev/null
+++ b/protos_generator/tests/no_package.proto
@@ -0,0 +1,48 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2023 Google LLC.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google LLC nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+syntax = "proto2";
+
+// option java_multiple_files = true;
+
+enum EnumWithNoPackage {
+  CELSIUS = 1;
+  FAHRENHEIT = 2;
+}
+
+message MessageWithEnumUpbTest {
+  enum EnumWithNoPackageInMessage {
+    UNKNOWN = 0;
+    AB_1 = 1;
+    CD_2 = 2;
+    EF_3 = 3;
+    GH_4 = 4;
+  }
+}
diff --git a/protos_generator/tests/no_package_enum_user.proto b/protos_generator/tests/no_package_enum_user.proto
new file mode 100644
index 0000000..ebf8c67
--- /dev/null
+++ b/protos_generator/tests/no_package_enum_user.proto
@@ -0,0 +1,41 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2023 Google LLC.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google LLC nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+syntax = "proto2";
+
+package protos_generator.tests;
+
+import "protos_generator/tests/no_package.proto";
+
+// option java_multiple_files = true;
+
+message MyMessage {
+  optional MessageWithEnumUpbTest my_type = 1;
+}
diff --git a/protos_generator/tests/test_enum.proto b/protos_generator/tests/test_enum.proto
new file mode 100644
index 0000000..ed41c3c
--- /dev/null
+++ b/protos_generator/tests/test_enum.proto
@@ -0,0 +1,40 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2023 Google LLC.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google LLC nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+syntax = "proto3";
+
+package protos_generator.test;
+
+enum TestEnum {
+  DEVICE_UNKNOWN = 0;
+  DEVICE_KEYBOARD = 1;
+  DEVICE_MOUSE = 2;
+  DEVICE_MONITOR = 3;
+}
diff --git a/protos_generator/tests/test_extension.proto b/protos_generator/tests/test_extension.proto
new file mode 100644
index 0000000..3a1a3c6
--- /dev/null
+++ b/protos_generator/tests/test_extension.proto
@@ -0,0 +1,42 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2023 Google LLC.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google LLC nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+syntax = "proto2";
+
+package protos_generator.test.someotherpackage;
+
+import "protos_generator/tests/test_model.proto";
+
+// Define extension that is extending proto outside this package with a type
+// defined in different file.
+
+extend TestModel {
+  optional ThemeExtension styling = 13001;
+}
diff --git a/protos_generator/tests/test_generated.cc b/protos_generator/tests/test_generated.cc
new file mode 100644
index 0000000..1206b6a
--- /dev/null
+++ b/protos_generator/tests/test_generated.cc
@@ -0,0 +1,1158 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2023 Google LLC.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google LLC nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include <limits>
+#include <memory>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include "absl/status/statusor.h"
+#include "absl/strings/string_view.h"
+#include "protos/protos.h"
+#include "protos/repeated_field.h"
+#include "protos/repeated_field_iterator.h"
+#include "protos_generator/tests/child_model.upb.proto.h"
+#include "protos_generator/tests/no_package.upb.proto.h"
+#include "protos_generator/tests/test_model.upb.proto.h"
+#include "upb/mem/arena.h"
+
+namespace {
+
+using ::protos_generator::test::protos::ChildModel1;
+using ::protos_generator::test::protos::other_ext;
+using ::protos_generator::test::protos::RED;
+using ::protos_generator::test::protos::TestEnum;
+using ::protos_generator::test::protos::TestModel;
+using ::protos_generator::test::protos::TestModel_Category;
+using ::protos_generator::test::protos::TestModel_Category_IMAGES;
+using ::protos_generator::test::protos::TestModel_Category_NEWS;
+using ::protos_generator::test::protos::TestModel_Category_VIDEO;
+using ::protos_generator::test::protos::theme;
+using ::protos_generator::test::protos::ThemeExtension;
+using ::testing::ElementsAre;
+using ::testing::HasSubstr;
+
+// C++17 port of C++20 `requires`
+template <typename... T, typename F>
+constexpr bool Requires(F) {
+  return std::is_invocable_v<F, T...>;
+}
+
+TEST(CppGeneratedCode, Constructor) { TestModel test_model; }
+
+TEST(CppGeneratedCode, MessageEnum) { EXPECT_EQ(5, TestModel_Category_IMAGES); }
+
+TEST(CppGeneratedCode, ImportedEnum) { EXPECT_EQ(3, TestEnum::DEVICE_MONITOR); }
+
+TEST(CppGeneratedCode, Enum) { EXPECT_EQ(1, RED); }
+
+TEST(CppGeneratedCode, EnumNoPackage) { EXPECT_EQ(1, ::protos_CELSIUS); }
+
+TEST(CppGeneratedCode, MessageEnumType) {
+  TestModel_Category category1 = TestModel_Category_IMAGES;
+  TestModel::Category category2 = TestModel::IMAGES;
+  EXPECT_EQ(category1, category2);
+}
+
+TEST(CppGeneratedCode, MessageEnumValue) {
+  EXPECT_EQ(TestModel_Category_IMAGES, TestModel::IMAGES);
+}
+
+TEST(CppGeneratedCode, ArenaConstructor) {
+  ::protos::Arena arena;
+  auto testModel = ::protos::CreateMessage<TestModel>(arena);
+  EXPECT_EQ(false, testModel.has_b1());
+}
+
+TEST(CppGeneratedCode, Booleans) {
+  ::protos::Arena arena;
+  auto testModel = ::protos::CreateMessage<TestModel>(arena);
+  EXPECT_FALSE(testModel.b1());
+  testModel.set_b1(true);
+  EXPECT_TRUE(testModel.b1());
+  testModel.set_b1(false);
+  EXPECT_FALSE(testModel.b1());
+  testModel.set_b1(true);
+  EXPECT_TRUE(testModel.b1());
+  testModel.clear_b1();
+  EXPECT_FALSE(testModel.has_b1());
+}
+
+TEST(CppGeneratedCode, ScalarInt32) {
+  ::protos::Arena arena;
+  auto testModel = ::protos::CreateMessage<TestModel>(arena);
+  // Test int32 defaults.
+  EXPECT_EQ(testModel.value(), 0);
+  EXPECT_FALSE(testModel.has_value());
+  // Floating point defautls.
+  EXPECT_EQ(std::numeric_limits<float>::infinity(),
+            testModel.float_value_with_default());
+  EXPECT_EQ(-std::numeric_limits<double>::infinity(),
+            testModel.double_value_with_default());
+
+  // Set value.
+  testModel.set_value(5);
+  EXPECT_TRUE(testModel.has_value());
+  EXPECT_EQ(testModel.value(), 5);
+  // Change value.
+  testModel.set_value(10);
+  EXPECT_TRUE(testModel.has_value());
+  EXPECT_EQ(testModel.value(), 10);
+  // Clear value.
+  testModel.clear_value();
+  EXPECT_FALSE(testModel.has_value());
+  EXPECT_EQ(testModel.value(), 0);
+}
+
+const char kTestStr1[] = "abcdefg";
+const char kTestStr2[] = "just another test string";
+
+TEST(CppGeneratedCode, Strings) {
+  TestModel testModel;
+  testModel.set_str1(kTestStr1);
+  testModel.set_str2(kTestStr2);
+  EXPECT_EQ(testModel.str1(), kTestStr1);
+  EXPECT_EQ(testModel.str2(), kTestStr2);
+  EXPECT_TRUE(testModel.has_str1());
+  EXPECT_TRUE(testModel.has_str2());
+
+  testModel.clear_str1();
+  EXPECT_FALSE(testModel.has_str1());
+  EXPECT_TRUE(testModel.has_str2());
+}
+
+TEST(CppGeneratedCode, ScalarUInt32) {
+  ::protos::Arena arena;
+  auto testModel = ::protos::CreateMessage<TestModel>(arena);
+  // Test defaults.
+  EXPECT_EQ(testModel.optional_uint32(), 0);
+  EXPECT_FALSE(testModel.has_optional_uint32());
+  // Set value.
+  testModel.set_optional_uint32(0xA0001000);
+  EXPECT_TRUE(testModel.has_optional_uint32());
+  EXPECT_EQ(testModel.optional_uint32(), 0xA0001000);
+  // Change value.
+  testModel.set_optional_uint32(0x70002000);
+  EXPECT_TRUE(testModel.has_optional_uint32());
+  EXPECT_EQ(testModel.optional_uint32(), 0x70002000);
+  // Clear value.
+  testModel.clear_optional_uint32();
+  EXPECT_FALSE(testModel.has_optional_uint32());
+  EXPECT_EQ(testModel.optional_uint32(), 0);
+}
+
+TEST(CppGeneratedCode, ScalarInt64) {
+  ::protos::Arena arena;
+  auto testModel = ::protos::CreateMessage<TestModel>(arena);
+  // Test defaults.
+  EXPECT_EQ(testModel.optional_int64(), 0);
+  EXPECT_FALSE(testModel.has_optional_int64());
+  // Set value.
+  testModel.set_optional_int64(0xFF00CCDDA0001000);
+  EXPECT_TRUE(testModel.has_optional_int64());
+  EXPECT_EQ(testModel.optional_int64(), 0xFF00CCDDA0001000);
+  // Change value.
+  testModel.set_optional_int64(0xFF00CCDD70002000);
+  EXPECT_TRUE(testModel.has_optional_int64());
+  EXPECT_EQ(testModel.optional_int64(), 0xFF00CCDD70002000);
+  // Clear value.
+  testModel.clear_optional_int64();
+  EXPECT_FALSE(testModel.has_optional_int64());
+  EXPECT_EQ(testModel.optional_int64(), 0);
+  // Set after clear.
+  testModel.set_optional_int64(0xFF00CCDDA0001000);
+  EXPECT_TRUE(testModel.has_optional_int64());
+  EXPECT_EQ(testModel.optional_int64(), 0xFF00CCDDA0001000);
+}
+
+TEST(CppGeneratedCode, ScalarFloat) {
+  ::protos::Arena arena;
+  auto testModel = ::protos::CreateMessage<TestModel>(arena);
+  // Test defaults.
+  EXPECT_EQ(testModel.optional_float(), 0.0f);
+  EXPECT_FALSE(testModel.has_optional_float());
+  EXPECT_EQ(std::numeric_limits<float>::infinity(),
+            testModel.float_value_with_default());
+  EXPECT_EQ(-std::numeric_limits<double>::infinity(),
+            testModel.double_value_with_default());
+  // Set value.
+  testModel.set_optional_float(3.14159265f);
+  EXPECT_TRUE(testModel.has_optional_float());
+  EXPECT_NEAR(testModel.optional_float(), 3.14159265f, 1e-9f);
+  // Change value.
+  testModel.set_optional_float(-2.0f);
+  EXPECT_TRUE(testModel.has_optional_float());
+  EXPECT_NEAR(testModel.optional_float(), -2, 1e-9f);
+  // Clear value.
+  testModel.clear_optional_float();
+  EXPECT_FALSE(testModel.has_optional_float());
+  EXPECT_EQ(testModel.optional_float(), 0.0f);
+  // Set after clear.
+  testModel.set_optional_float(3.14159265f);
+  EXPECT_TRUE(testModel.has_optional_float());
+  EXPECT_NEAR(testModel.optional_float(), 3.14159265f, 1e-9f);
+}
+
+TEST(CppGeneratedCode, ScalarDouble) {
+  ::protos::Arena arena;
+  auto testModel = ::protos::CreateMessage<TestModel>(arena);
+  // Test defaults.
+  EXPECT_EQ(testModel.optional_double(), 0.0);
+  EXPECT_FALSE(testModel.has_optional_double());
+  // Set value.
+  testModel.set_optional_double(3.141592653589793);
+  EXPECT_TRUE(testModel.has_optional_double());
+  EXPECT_NEAR(testModel.optional_double(), 3.141592653589793, 1e-16f);
+  // Change value.
+  testModel.set_optional_double(-1.0);
+  EXPECT_TRUE(testModel.has_optional_double());
+  EXPECT_NEAR(testModel.optional_double(), -1.0, 1e-16f);
+  // Clear value.
+  testModel.clear_optional_double();
+  EXPECT_FALSE(testModel.has_optional_double());
+  EXPECT_EQ(testModel.optional_double(), 0.0f);
+  // Set after clear.
+  testModel.set_optional_double(3.141592653589793);
+  EXPECT_TRUE(testModel.has_optional_double());
+  EXPECT_NEAR(testModel.optional_double(), 3.141592653589793, 1e-16f);
+}
+
+TEST(CppGeneratedCode, Enums) {
+  ::protos::Arena arena;
+  auto testModel = ::protos::CreateMessage<TestModel>(arena);
+
+  // Check enum default value.
+  EXPECT_EQ(TestModel_Category_IMAGES, 5);
+
+  // Test defaults.
+  EXPECT_FALSE(testModel.has_category());
+  EXPECT_EQ(testModel.category(), TestModel_Category_IMAGES);
+  // Set value.
+  testModel.set_category(TestModel_Category_NEWS);
+  EXPECT_TRUE(testModel.has_category());
+  EXPECT_EQ(testModel.category(), TestModel_Category_NEWS);
+  // Change value.
+  testModel.set_category(TestModel_Category_VIDEO);
+  EXPECT_TRUE(testModel.has_category());
+  EXPECT_EQ(testModel.category(), TestModel_Category_VIDEO);
+  // Clear value.
+  testModel.clear_category();
+  EXPECT_FALSE(testModel.has_category());
+  EXPECT_EQ(testModel.category(), TestModel_Category_IMAGES);
+  // Set after clear.
+  testModel.set_category(TestModel_Category_VIDEO);
+  EXPECT_TRUE(testModel.has_category());
+  EXPECT_EQ(testModel.category(), TestModel_Category_VIDEO);
+}
+
+TEST(CppGeneratedCode, FieldWithDefaultValue) {
+  ::protos::Arena arena;
+  auto testModel = ::protos::CreateMessage<TestModel>(arena);
+
+  EXPECT_FALSE(testModel.has_int_value_with_default());
+  EXPECT_EQ(testModel.int_value_with_default(), 65);
+  testModel.set_int_value_with_default(10);
+  EXPECT_EQ(testModel.int_value_with_default(), 10);
+
+  EXPECT_FALSE(testModel.has_string_value_with_default());
+  EXPECT_EQ(testModel.string_value_with_default(), "hello");
+  testModel.set_string_value_with_default("new string");
+  EXPECT_EQ(testModel.string_value_with_default(), "new string");
+}
+
+TEST(CppGeneratedCode, OneOfFields) {
+  ::protos::Arena arena;
+  auto test_model = ::protos::CreateMessage<TestModel>(arena);
+
+  EXPECT_FALSE(test_model.has_oneof_member1());
+  EXPECT_FALSE(test_model.has_oneof_member2());
+  EXPECT_EQ(TestModel::CHILD_ONEOF1_NOT_SET, test_model.child_oneof1_case());
+
+  test_model.set_oneof_member1("one of string");
+  EXPECT_TRUE(test_model.has_oneof_member1());
+  EXPECT_FALSE(test_model.has_oneof_member2());
+  EXPECT_EQ(test_model.oneof_member1(), "one of string");
+  EXPECT_EQ(TestModel::kOneofMember1, test_model.child_oneof1_case());
+
+  test_model.set_oneof_member2(true);
+  EXPECT_FALSE(test_model.has_oneof_member1());
+  EXPECT_TRUE(test_model.has_oneof_member2());
+  EXPECT_EQ(test_model.oneof_member2(), true);
+  EXPECT_EQ(TestModel::kOneofMember2, test_model.child_oneof1_case());
+
+  test_model.clear_oneof_member2();
+  EXPECT_FALSE(test_model.has_oneof_member1());
+  EXPECT_FALSE(test_model.has_oneof_member2());
+  EXPECT_EQ(test_model.oneof_member1(), "");
+  EXPECT_EQ(test_model.oneof_member2(), false);
+  EXPECT_EQ(TestModel::CHILD_ONEOF1_NOT_SET, test_model.child_oneof1_case());
+}
+
+TEST(CppGeneratedCode, Messages) {
+  ::protos::Arena arena;
+  auto test_model = ::protos::CreateMessage<TestModel>(arena);
+  EXPECT_EQ(false, test_model.has_child_model_1());
+  auto child_model = test_model.child_model_1();
+  EXPECT_EQ(false, child_model->has_child_b1());
+  EXPECT_EQ(false, child_model->child_b1());
+  auto mutable_child = test_model.mutable_child_model_1();
+  mutable_child->set_child_b1(true);
+  EXPECT_EQ(true, mutable_child->has_child_b1());
+  EXPECT_EQ(true, mutable_child->child_b1());
+  // The View should not change due to mutation since it
+  // is default_instance.
+  EXPECT_EQ(false, child_model->has_child_b1());
+  // Readonly View should now show change.
+  child_model = test_model.child_model_1();
+  EXPECT_EQ(true, child_model->has_child_b1());
+  EXPECT_EQ(true, child_model->child_b1());
+  // Clear message field.
+  EXPECT_EQ(true, test_model.has_child_model_1());
+  test_model.clear_child_model_1();
+  EXPECT_EQ(false, test_model.has_child_model_1());
+}
+
+TEST(CppGeneratedCode, NestedMessages) {
+  ::protos::Arena arena;
+  auto test_model = ::protos::CreateMessage<TestModel>(arena);
+  auto nested_child = test_model.nested_child_1();
+  EXPECT_EQ(0, nested_child->nested_child_name().size());
+  auto mutable_nested_child = test_model.mutable_nested_child_1();
+  EXPECT_EQ(false, mutable_nested_child->has_nested_child_name());
+  mutable_nested_child->set_nested_child_name(kTestStr1);
+  EXPECT_EQ(true, mutable_nested_child->has_nested_child_name());
+}
+
+TEST(CppGeneratedCode, RepeatedMessages) {
+  ::protos::Arena arena;
+  auto test_model = ::protos::CreateMessage<TestModel>(arena);
+  EXPECT_EQ(0, test_model.child_models_size());
+  // Should be able to clear repeated field when empty.
+  test_model.mutable_child_models()->clear();
+  EXPECT_EQ(0, test_model.child_models_size());
+  // Add 2 children.
+  auto new_child = test_model.add_child_models();
+  EXPECT_EQ(true, new_child.ok());
+  new_child.value()->set_child_str1(kTestStr1);
+  new_child = test_model.add_child_models();
+  EXPECT_EQ(true, new_child.ok());
+  new_child.value()->set_child_str1(kTestStr2);
+  EXPECT_EQ(2, test_model.child_models_size());
+  // Mutable access.
+  auto mutable_first = test_model.mutable_child_models(0);
+  EXPECT_EQ(mutable_first->child_str1(), kTestStr1);
+  mutable_first->set_child_str1("change1");
+  auto mutable_second = test_model.mutable_child_models(1);
+  EXPECT_EQ(mutable_second->child_str1(), kTestStr2);
+  mutable_second->set_child_str1("change2");
+  // Check mutations using views.
+  auto view_first = test_model.child_models(0);
+  EXPECT_EQ(view_first->child_str1(), "change1");
+  auto view_second = test_model.child_models(1);
+  EXPECT_EQ(view_second->child_str1(), "change2");
+}
+
+TEST(CppGeneratedCode, RepeatedScalar) {
+  ::protos::Arena arena;
+  auto test_model = ::protos::CreateMessage<TestModel>(arena);
+  EXPECT_EQ(0, test_model.value_array_size());
+  // Should be able to clear repeated field when empty.
+  test_model.mutable_value_array()->clear();
+  EXPECT_EQ(0, test_model.value_array_size());
+  // Add 2 children.
+  EXPECT_EQ(true, test_model.add_value_array(5));
+  EXPECT_EQ(true, test_model.add_value_array(6));
+  EXPECT_EQ(2, test_model.value_array_size());
+  EXPECT_EQ(5, test_model.value_array(0));
+  EXPECT_EQ(6, test_model.value_array(1));
+  EXPECT_EQ(true, test_model.resize_value_array(3));
+  EXPECT_EQ(3, test_model.value_array_size());
+  test_model.set_value_array(2, 7);
+  EXPECT_EQ(5, test_model.value_array(0));
+  EXPECT_EQ(6, test_model.value_array(1));
+  EXPECT_EQ(7, test_model.value_array(2));
+}
+
+TEST(CppGeneratedCode, RepeatedFieldClear) {
+  ::protos::Arena arena;
+  auto test_model = ::protos::CreateMessage<TestModel>(arena);
+  test_model.mutable_value_array()->push_back(5);
+  test_model.mutable_value_array()->push_back(16);
+  test_model.mutable_value_array()->push_back(27);
+  ASSERT_EQ(test_model.mutable_value_array()->size(), 3);
+  test_model.mutable_value_array()->clear();
+  EXPECT_EQ(test_model.mutable_value_array()->size(), 0);
+}
+
+TEST(CppGeneratedCode, RepeatedFieldProxyForScalars) {
+  ::protos::Arena arena;
+  auto test_model = ::protos::CreateMessage<TestModel>(arena);
+  EXPECT_EQ(0, test_model.value_array().size());
+  EXPECT_EQ(0, test_model.mutable_value_array()->size());
+
+  test_model.mutable_value_array()->push_back(5);
+  test_model.mutable_value_array()->push_back(16);
+  test_model.mutable_value_array()->push_back(27);
+
+  ASSERT_EQ(test_model.mutable_value_array()->size(), 3);
+  EXPECT_EQ((*test_model.mutable_value_array())[0], 5);
+  EXPECT_EQ((*test_model.mutable_value_array())[1], 16);
+  EXPECT_EQ((*test_model.mutable_value_array())[2], 27);
+
+  const auto value_array = test_model.value_array();
+  ASSERT_EQ(value_array.size(), 3);
+  EXPECT_EQ(value_array[0], 5);
+  EXPECT_EQ(value_array[1], 16);
+  EXPECT_EQ(value_array[2], 27);
+
+  EXPECT_THAT(value_array, ElementsAre(5, 16, 27));
+
+  EXPECT_THAT(std::vector(value_array.begin(), value_array.end()),
+              ElementsAre(5, 16, 27));
+  EXPECT_THAT(std::vector(value_array.cbegin(), value_array.cend()),
+              ElementsAre(5, 16, 27));
+  EXPECT_THAT(std::vector(value_array.rbegin(), value_array.rend()),
+              ElementsAre(27, 16, 5));
+  EXPECT_THAT(std::vector(value_array.crbegin(), value_array.crend()),
+              ElementsAre(27, 16, 5));
+}
+
+TEST(CppGeneratedCode, RepeatedScalarIterator) {
+  ::protos::Arena arena;
+  auto test_model = ::protos::CreateMessage<TestModel>(arena);
+  test_model.mutable_value_array()->push_back(5);
+  test_model.mutable_value_array()->push_back(16);
+  test_model.mutable_value_array()->push_back(27);
+  int sum = 0;
+  // Access by value.
+  const ::protos::RepeatedField<int32_t>::CProxy rep1 =
+      test_model.value_array();
+  for (auto i : rep1) {
+    sum += i;
+  }
+  EXPECT_EQ(sum, 5 + 16 + 27);
+  // Access by const reference.
+  sum = 0;
+  for (const int& i : *test_model.mutable_value_array()) {
+    sum += i;
+  }
+  EXPECT_EQ(sum, 5 + 16 + 27);
+  // Access by forwarding reference.
+  sum = 0;
+  for (auto&& i : *test_model.mutable_value_array()) {
+    sum += i;
+  }
+  EXPECT_EQ(sum, 5 + 16 + 27);
+  // Test iterator operators.
+  auto begin = test_model.value_array().begin();
+  auto end = test_model.value_array().end();
+  sum = 0;
+  for (auto it = begin; it != end; ++it) {
+    sum += *it;
+  }
+  EXPECT_EQ(sum, 5 + 16 + 27);
+  auto it = begin;
+  ++it;
+  EXPECT_TRUE(begin < it);
+  EXPECT_TRUE(begin <= it);
+  it = end;
+  EXPECT_TRUE(it == end);
+  EXPECT_TRUE(it > begin);
+  EXPECT_TRUE(it >= begin);
+  EXPECT_TRUE(it != begin);
+  // difference type
+  it = end;
+  --it;
+  --it;
+  EXPECT_EQ(end - it, 2);
+  it = begin;
+  EXPECT_EQ(it[0], 5);
+  EXPECT_EQ(it[1], 16);
+  EXPECT_EQ(it[2], 27);
+  // ValueProxy.
+  sum = 0;
+  for (::protos::RepeatedField<int32_t>::ValueCProxy c :
+       test_model.value_array()) {
+    sum += c;
+  }
+  EXPECT_EQ(sum, 5 + 16 + 27);
+  sum = 0;
+  for (::protos::RepeatedField<int32_t>::ValueProxy c :
+       *test_model.mutable_value_array()) {
+    sum += c;
+  }
+  EXPECT_EQ(sum, 5 + 16 + 27);
+}
+
+TEST(CppGeneratedCode, RepeatedFieldProxyForStrings) {
+  ::protos::Arena arena;
+  auto test_model = ::protos::CreateMessage<TestModel>(arena);
+  EXPECT_EQ(0, test_model.repeated_string().size());
+  EXPECT_EQ(0, test_model.mutable_repeated_string()->size());
+
+  test_model.mutable_repeated_string()->push_back("a");
+  test_model.mutable_repeated_string()->push_back("b");
+  test_model.mutable_repeated_string()->push_back("c");
+
+  ASSERT_EQ(test_model.repeated_string().size(), 3);
+  EXPECT_EQ(test_model.repeated_string()[0], "a");
+  EXPECT_EQ(test_model.repeated_string()[1], "b");
+  EXPECT_EQ(test_model.repeated_string()[2], "c");
+
+  EXPECT_THAT(test_model.repeated_string(), ElementsAre("a", "b", "c"));
+  EXPECT_THAT(*test_model.mutable_repeated_string(),
+              ElementsAre("a", "b", "c"));
+
+  ASSERT_EQ(test_model.mutable_repeated_string()->size(), 3);
+  EXPECT_EQ((*test_model.mutable_repeated_string())[0], "a");
+  EXPECT_EQ((*test_model.mutable_repeated_string())[1], "b");
+  EXPECT_EQ((*test_model.mutable_repeated_string())[2], "c");
+
+  // The const accessor can't be used to modify the element
+  EXPECT_FALSE((std::is_assignable<decltype(test_model.repeated_string()[1]),
+                                   absl::string_view>::value));
+  // But the mutable one is fine.
+  (*test_model.mutable_repeated_string())[1] = "other";
+  EXPECT_THAT(test_model.repeated_string(), ElementsAre("a", "other", "c"));
+
+  test_model.mutable_repeated_string()->clear();
+  EXPECT_EQ(test_model.mutable_repeated_string()->size(), 0);
+}
+
+TEST(CppGeneratedCode, RepeatedFieldProxyForMessages) {
+  ::protos::Arena arena;
+  auto test_model = ::protos::CreateMessage<TestModel>(arena);
+  EXPECT_EQ(0, test_model.child_models().size());
+  ChildModel1 child1;
+  child1.set_child_str1(kTestStr1);
+  test_model.mutable_child_models()->push_back(child1);
+  ChildModel1 child2;
+  child2.set_child_str1(kTestStr2);
+  test_model.mutable_child_models()->push_back(std::move(child2));
+
+  int i = 0;
+  for (auto child : test_model.child_models()) {
+    EXPECT_FALSE(Requires<decltype(child)>(
+        [](auto&& x) -> decltype(x.set_child_str1("")) {}));
+    if (i++ == 0) {
+      EXPECT_EQ(child.child_str1(), kTestStr1);
+    } else {
+      EXPECT_EQ(child.child_str1(), kTestStr2);
+    }
+  }
+
+  i = 0;
+  for (auto child : *test_model.mutable_child_models()) {
+    if (i++ == 0) {
+      EXPECT_EQ(child.child_str1(), kTestStr1);
+    } else {
+      EXPECT_EQ(child.child_str1(), kTestStr2);
+    }
+  }
+
+  using testing::_;
+  EXPECT_THAT(test_model.child_models(), ElementsAre(_, _));
+
+  EXPECT_EQ(test_model.child_models().size(), 2);
+  EXPECT_EQ(test_model.child_models()[0].child_str1(), kTestStr1);
+  EXPECT_EQ(test_model.child_models()[1].child_str1(), kTestStr2);
+  EXPECT_EQ((*test_model.mutable_child_models())[0].child_str1(), kTestStr1);
+  EXPECT_EQ((*test_model.mutable_child_models())[1].child_str1(), kTestStr2);
+  (*test_model.mutable_child_models())[0].set_child_str1("change1");
+  EXPECT_EQ((*test_model.mutable_child_models())[0].child_str1(), "change1");
+  test_model.mutable_child_models()->clear();
+  EXPECT_EQ(test_model.mutable_child_models()->size(), 0);
+}
+
+TEST(CppGeneratedCode, RepeatedFieldProxyForMessagesIndexOperator) {
+  ::protos::Arena arena;
+  auto test_model = ::protos::CreateMessage<TestModel>(arena);
+  EXPECT_EQ(0, test_model.child_models().size());
+  ChildModel1 child1;
+  child1.set_child_str1(kTestStr1);
+  test_model.mutable_child_models()->push_back(child1);
+  ChildModel1 child2;
+
+  child2.set_child_str1(kTestStr2);
+  test_model.mutable_child_models()->push_back(std::move(child2));
+  ASSERT_EQ(test_model.child_models().size(), 2);
+
+  // test_model.child_models()[0].set_child_str1("change1");
+  (*test_model.mutable_child_models())[0].set_child_str1("change1");
+  EXPECT_EQ((*test_model.mutable_child_models())[0].child_str1(), "change1");
+}
+
+TEST(CppGeneratedCode, RepeatedStrings) {
+  ::protos::Arena arena;
+  auto test_model = ::protos::CreateMessage<TestModel>(arena);
+  EXPECT_EQ(0, test_model.repeated_string_size());
+  // Should be able to clear repeated field when empty.
+  test_model.mutable_repeated_string()->clear();
+  EXPECT_EQ(0, test_model.repeated_string_size());
+  // Add 2 children.
+  EXPECT_EQ(true, test_model.add_repeated_string("Hello"));
+  EXPECT_EQ(true, test_model.add_repeated_string("World"));
+  EXPECT_EQ(2, test_model.repeated_string_size());
+  EXPECT_EQ("Hello", test_model.repeated_string(0));
+  EXPECT_EQ("World", test_model.repeated_string(1));
+  EXPECT_EQ(true, test_model.resize_repeated_string(3));
+  EXPECT_EQ(3, test_model.repeated_string_size());
+  test_model.set_repeated_string(2, "Test");
+  EXPECT_EQ("Hello", test_model.repeated_string(0));
+  EXPECT_EQ("World", test_model.repeated_string(1));
+  EXPECT_EQ("Test", test_model.repeated_string(2));
+}
+
+TEST(CppGeneratedCode, MessageMapInt32KeyMessageValue) {
+  const int key_test_value = 3;
+  ::protos::Arena arena;
+  ::protos::Arena child_arena;
+  auto test_model = ::protos::CreateMessage<TestModel>(arena);
+  EXPECT_EQ(0, test_model.child_map_size());
+  test_model.clear_child_map();
+  EXPECT_EQ(0, test_model.child_map_size());
+  auto child_model1 = ::protos::CreateMessage<ChildModel1>(child_arena);
+  child_model1.set_child_str1("abc");
+  test_model.set_child_map(key_test_value, child_model1);
+  auto map_result = test_model.get_child_map(key_test_value);
+  EXPECT_EQ(true, map_result.ok());
+  EXPECT_EQ("abc", map_result.value()->child_str1());
+  // Now mutate original child model to verify that value semantics are
+  // preserved.
+  child_model1.set_child_str1("abc V2");
+  EXPECT_EQ("abc", map_result.value()->child_str1());
+  test_model.delete_child_map(key_test_value);
+  auto map_result_after_delete = test_model.get_child_map(key_test_value);
+  EXPECT_EQ(false, map_result_after_delete.ok());
+}
+
+TEST(CppGeneratedCode, MessageMapStringKeyAndStringValue) {
+  ::protos::Arena arena;
+  auto test_model = ::protos::CreateMessage<TestModel>(arena);
+  EXPECT_EQ(0, test_model.str_to_str_map_size());
+  test_model.clear_str_to_str_map();
+  EXPECT_EQ(0, test_model.str_to_str_map_size());
+  test_model.set_str_to_str_map("first", "abc");
+  test_model.set_str_to_str_map("second", "def");
+  auto result = test_model.get_str_to_str_map("second");
+  EXPECT_EQ(true, result.ok());
+  EXPECT_EQ("def", result.value());
+  test_model.delete_str_to_str_map("first");
+  auto result_after_delete = test_model.get_str_to_str_map("first");
+  EXPECT_EQ(false, result_after_delete.ok());
+}
+
+TEST(CppGeneratedCode, MessageMapStringKeyAndInt32Value) {
+  ::protos::Arena arena;
+  auto test_model = ::protos::CreateMessage<TestModel>(arena);
+  EXPECT_EQ(0, test_model.str_to_int_map_size());
+  test_model.clear_str_to_int_map();
+  EXPECT_EQ(0, test_model.str_to_int_map_size());
+  test_model.set_str_to_int_map("first", 10);
+  EXPECT_EQ(1, test_model.str_to_int_map_size());
+  test_model.set_str_to_int_map("second", 20);
+  EXPECT_EQ(2, test_model.str_to_int_map_size());
+  auto result = test_model.get_str_to_int_map("second");
+  EXPECT_EQ(true, result.ok());
+  EXPECT_EQ(20, result.value());
+  test_model.delete_str_to_int_map("first");
+  auto result_after_delete = test_model.get_str_to_int_map("first");
+  EXPECT_EQ(false, result_after_delete.ok());
+}
+
+TEST(CppGeneratedCode, HasExtension) {
+  TestModel model;
+  EXPECT_EQ(false, ::protos::HasExtension(&model, theme));
+}
+
+TEST(CppGeneratedCode, HasExtensionPtr) {
+  TestModel model;
+  EXPECT_EQ(false, ::protos::HasExtension(model.recursive_child(), theme));
+}
+
+TEST(CppGeneratedCode, ClearExtensionWithEmptyExtension) {
+  TestModel model;
+  EXPECT_EQ(false, ::protos::HasExtension(&model, theme));
+  ::protos::ClearExtension(&model, theme);
+  EXPECT_EQ(false, ::protos::HasExtension(&model, theme));
+}
+
+TEST(CppGeneratedCode, ClearExtensionWithEmptyExtensionPtr) {
+  TestModel model;
+  ::protos::Ptr<TestModel> recursive_child = model.mutable_recursive_child();
+  ::protos::ClearExtension(recursive_child, theme);
+  EXPECT_EQ(false, ::protos::HasExtension(recursive_child, theme));
+}
+
+TEST(CppGeneratedCode, SetExtension) {
+  TestModel model;
+  void* prior_message;
+  {
+    // Use a nested scope to make sure the arenas are fused correctly.
+    ThemeExtension extension1;
+    extension1.set_ext_name("Hello World");
+    prior_message = ::protos::internal::GetInternalMsg(&extension1);
+    EXPECT_EQ(false, ::protos::HasExtension(&model, theme));
+    EXPECT_EQ(
+        true,
+        ::protos::SetExtension(&model, theme, std::move(extension1)).ok());
+  }
+  EXPECT_EQ(true, ::protos::HasExtension(&model, theme));
+  auto ext = ::protos::GetExtension(&model, theme);
+  EXPECT_TRUE(ext.ok());
+  EXPECT_EQ(::protos::internal::GetInternalMsg(*ext), prior_message);
+}
+
+TEST(CppGeneratedCode, SetExtensionFusingFailureShouldCopy) {
+  // Use an initial block to disallow fusing.
+  char initial_block[1000];
+  protos::Arena arena(initial_block, sizeof(initial_block));
+
+  protos::Ptr<TestModel> model = protos::CreateMessage<TestModel>(arena);
+
+  ThemeExtension extension1;
+  extension1.set_ext_name("Hello World");
+  ASSERT_FALSE(
+      upb_Arena_Fuse(arena.ptr(), ::protos::internal::GetArena(&extension1)));
+  EXPECT_FALSE(::protos::HasExtension(model, theme));
+  auto status = ::protos::SetExtension(model, theme, std::move(extension1));
+  EXPECT_TRUE(status.ok());
+  EXPECT_TRUE(::protos::HasExtension(model, theme));
+  EXPECT_TRUE(::protos::GetExtension(model, theme).ok());
+}
+
+TEST(CppGeneratedCode, SetExtensionShouldClone) {
+  TestModel model;
+  ThemeExtension extension1;
+  extension1.set_ext_name("Hello World");
+  EXPECT_EQ(false, ::protos::HasExtension(&model, theme));
+  EXPECT_EQ(true, ::protos::SetExtension(&model, theme, extension1).ok());
+  extension1.set_ext_name("Goodbye");
+  EXPECT_EQ(true, ::protos::HasExtension(&model, theme));
+  auto ext = ::protos::GetExtension(&model, theme);
+  EXPECT_TRUE(ext.ok());
+  EXPECT_EQ((*ext)->ext_name(), "Hello World");
+}
+
+TEST(CppGeneratedCode, SetExtensionShouldCloneConst) {
+  TestModel model;
+  ThemeExtension extension1;
+  extension1.set_ext_name("Hello World");
+  EXPECT_EQ(false, ::protos::HasExtension(&model, theme));
+  EXPECT_EQ(
+      true,
+      ::protos::SetExtension(&model, theme, std::as_const(extension1)).ok());
+  extension1.set_ext_name("Goodbye");
+  EXPECT_EQ(true, ::protos::HasExtension(&model, theme));
+  auto ext = ::protos::GetExtension(&model, theme);
+  EXPECT_TRUE(ext.ok());
+  EXPECT_EQ((*ext)->ext_name(), "Hello World");
+}
+
+TEST(CppGeneratedCode, SetExtensionOnMutableChild) {
+  TestModel model;
+  ThemeExtension extension1;
+  extension1.set_ext_name("Hello World");
+  EXPECT_EQ(false,
+            ::protos::HasExtension(model.mutable_recursive_child(), theme));
+  EXPECT_EQ(true, ::protos::SetExtension(model.mutable_recursive_child(), theme,
+                                         extension1)
+                      .ok());
+  EXPECT_EQ(true,
+            ::protos::HasExtension(model.mutable_recursive_child(), theme));
+}
+
+TEST(CppGeneratedCode, GetExtension) {
+  TestModel model;
+  ThemeExtension extension1;
+  extension1.set_ext_name("Hello World");
+  EXPECT_EQ(false, ::protos::HasExtension(&model, theme));
+  EXPECT_EQ(true, ::protos::SetExtension(&model, theme, extension1).ok());
+  EXPECT_EQ("Hello World",
+            ::protos::GetExtension(&model, theme).value()->ext_name());
+}
+
+TEST(CppGeneratedCode, GetExtensionOnMutableChild) {
+  TestModel model;
+  ThemeExtension extension1;
+  extension1.set_ext_name("Hello World");
+  ::protos::Ptr<TestModel> mutable_recursive_child =
+      model.mutable_recursive_child();
+  EXPECT_EQ(false, ::protos::HasExtension(mutable_recursive_child, theme));
+  EXPECT_EQ(
+      true,
+      ::protos::SetExtension(mutable_recursive_child, theme, extension1).ok());
+  EXPECT_EQ("Hello World",
+            ::protos::GetExtension(mutable_recursive_child, theme)
+                .value()
+                ->ext_name());
+}
+
+TEST(CppGeneratedCode, GetExtensionOnImmutableChild) {
+  TestModel model;
+  ThemeExtension extension1;
+  extension1.set_ext_name("Hello World");
+  ::protos::Ptr<TestModel> mutable_recursive_child =
+      model.mutable_recursive_child();
+  EXPECT_EQ(false, ::protos::HasExtension(mutable_recursive_child, theme));
+  EXPECT_EQ(
+      true,
+      ::protos::SetExtension(mutable_recursive_child, theme, extension1).ok());
+  ::protos::Ptr<const TestModel> recursive_child = model.recursive_child();
+  EXPECT_EQ("Hello World",
+            ::protos::GetExtension(recursive_child, theme).value()->ext_name());
+}
+
+TEST(CppGeneratedCode, SerializeUsingArena) {
+  TestModel model;
+  model.set_str1("Hello World");
+  ::upb::Arena arena;
+  absl::StatusOr<absl::string_view> bytes = ::protos::Serialize(&model, arena);
+  EXPECT_EQ(true, bytes.ok());
+  TestModel parsed_model = ::protos::Parse<TestModel>(bytes.value()).value();
+  EXPECT_EQ("Hello World", parsed_model.str1());
+}
+
+TEST(CppGeneratedCode, SerializeProxyUsingArena) {
+  ::upb::Arena message_arena;
+  TestModel::Proxy model_proxy =
+      ::protos::CreateMessage<TestModel>(message_arena);
+  model_proxy.set_str1("Hello World");
+  ::upb::Arena arena;
+  absl::StatusOr<absl::string_view> bytes =
+      ::protos::Serialize(&model_proxy, arena);
+  EXPECT_EQ(true, bytes.ok());
+  TestModel parsed_model = ::protos::Parse<TestModel>(bytes.value()).value();
+  EXPECT_EQ("Hello World", parsed_model.str1());
+}
+
+TEST(CppGeneratedCode, SerializeNestedMessageUsingArena) {
+  TestModel model;
+  model.mutable_recursive_child()->set_str1("Hello World");
+  ::upb::Arena arena;
+  ::protos::Ptr<const TestModel> child = model.recursive_child();
+  absl::StatusOr<absl::string_view> bytes = ::protos::Serialize(child, arena);
+  EXPECT_EQ(true, bytes.ok());
+  TestModel parsed_model = ::protos::Parse<TestModel>(bytes.value()).value();
+  EXPECT_EQ("Hello World", parsed_model.str1());
+}
+
+TEST(CppGeneratedCode, Parse) {
+  TestModel model;
+  model.set_str1("Test123");
+  ThemeExtension extension1;
+  extension1.set_ext_name("Hello World");
+  EXPECT_EQ(true, ::protos::SetExtension(&model, theme, extension1).ok());
+  ::upb::Arena arena;
+  auto bytes = ::protos::Serialize(&model, arena);
+  EXPECT_EQ(true, bytes.ok());
+  TestModel parsed_model = ::protos::Parse<TestModel>(bytes.value()).value();
+  EXPECT_EQ("Test123", parsed_model.str1());
+  EXPECT_EQ(true, ::protos::GetExtension(&parsed_model, theme).ok());
+}
+
+TEST(CppGeneratedCode, ParseIntoPtrToModel) {
+  TestModel model;
+  model.set_str1("Test123");
+  ThemeExtension extension1;
+  extension1.set_ext_name("Hello World");
+  EXPECT_EQ(true, ::protos::SetExtension(&model, theme, extension1).ok());
+  ::upb::Arena arena;
+  auto bytes = ::protos::Serialize(&model, arena);
+  EXPECT_EQ(true, bytes.ok());
+  ::protos::Ptr<TestModel> parsed_model =
+      ::protos::CreateMessage<TestModel>(arena);
+  EXPECT_TRUE(::protos::Parse(parsed_model, bytes.value()));
+  EXPECT_EQ("Test123", parsed_model->str1());
+  // Should return an extension even if we don't pass ExtensionRegistry
+  // by promoting unknown.
+  EXPECT_EQ(true, ::protos::GetExtension(parsed_model, theme).ok());
+}
+
+TEST(CppGeneratedCode, ParseWithExtensionRegistry) {
+  TestModel model;
+  model.set_str1("Test123");
+  ThemeExtension extension1;
+  extension1.set_ext_name("Hello World");
+  EXPECT_EQ(true, ::protos::SetExtension(&model, theme, extension1).ok());
+  EXPECT_EQ(true, ::protos::SetExtension(
+                      &model, ThemeExtension::theme_extension, extension1)
+                      .ok());
+  ::upb::Arena arena;
+  auto bytes = ::protos::Serialize(&model, arena);
+  EXPECT_EQ(true, bytes.ok());
+  ::protos::ExtensionRegistry extensions(
+      {&theme, &other_ext, &ThemeExtension::theme_extension}, arena);
+  TestModel parsed_model =
+      ::protos::Parse<TestModel>(bytes.value(), extensions).value();
+  EXPECT_EQ("Test123", parsed_model.str1());
+  EXPECT_EQ(true, ::protos::GetExtension(&parsed_model, theme).ok());
+  EXPECT_EQ(true, ::protos::GetExtension(&parsed_model,
+                                         ThemeExtension::theme_extension)
+                      .ok());
+  EXPECT_EQ("Hello World", ::protos::GetExtension(
+                               &parsed_model, ThemeExtension::theme_extension)
+                               .value()
+                               ->ext_name());
+}
+
+TEST(CppGeneratedCode, NameCollisions) {
+  TestModel model;
+  model.set_template_("test");
+  EXPECT_EQ("test", model.template_());
+  model.set_arena__("test");
+  EXPECT_EQ("test", model.arena__());
+}
+
+TEST(CppGeneratedCode, SharedPointer) {
+  std::shared_ptr<TestModel> model = std::make_shared<TestModel>();
+  ::upb::Arena arena;
+  auto bytes = protos::Serialize(model.get(), arena);
+  EXPECT_TRUE(protos::Parse(model.get(), bytes.value()));
+}
+
+TEST(CppGeneratedCode, UniquePointer) {
+  auto model = std::make_unique<TestModel>();
+  ::upb::Arena arena;
+  auto bytes = protos::Serialize(model.get(), arena);
+  EXPECT_TRUE(protos::Parse(model.get(), bytes.value()));
+}
+
+TEST(CppGeneratedCode, Assignment) {
+  TestModel model;
+  model.set_category(5);
+  model.mutable_child_model_1()->set_child_str1("text in child");
+  TestModel model2 = model;
+  EXPECT_EQ(5, model2.category());
+  EXPECT_EQ(model2.child_model_1()->child_str1(), "text in child");
+}
+
+TEST(CppGeneratedCode, PtrAssignment) {
+  TestModel model;
+  model.mutable_child_model_1()->set_child_str1("text in child");
+  ChildModel1 child_from_const_ptr = *model.child_model_1();
+  EXPECT_EQ(child_from_const_ptr.child_str1(), "text in child");
+  ChildModel1 child_from_ptr = *model.mutable_child_model_1();
+  EXPECT_EQ(child_from_ptr.child_str1(), "text in child");
+}
+
+TEST(CppGeneratedCode, CopyConstructor) {
+  TestModel model;
+  model.set_category(6);
+  TestModel model2(model);
+  EXPECT_EQ(6, model2.category());
+}
+
+TEST(CppGeneratedCode, PtrConstructor) {
+  TestModel model;
+  model.mutable_child_model_1()->set_child_str1("text in child");
+  ChildModel1 child_from_ptr(*model.mutable_child_model_1());
+  EXPECT_EQ(child_from_ptr.child_str1(), "text in child");
+  ChildModel1 child_from_const_ptr(*model.child_model_1());
+  EXPECT_EQ(child_from_const_ptr.child_str1(), "text in child");
+}
+
+TEST(CppGeneratedCode, MutableToProxy) {
+  TestModel model;
+  ::protos::Ptr<ChildModel1> child = model.mutable_child_model_1();
+  (void)child;
+}
+
+TEST(CppGeneratedCode, ProxyToCProxy) {
+  TestModel model;
+  ::protos::Ptr<ChildModel1> child = model.mutable_child_model_1();
+  ::protos::Ptr<const ChildModel1> child2 = child;
+  (void)child2;
+}
+
+TEST(CppGeneratedCode, MutableAccessorsAreHiddenInCProxy) {
+  TestModel model;
+  ::protos::Ptr<TestModel> proxy = &model;
+  ::protos::Ptr<const TestModel> cproxy = proxy;
+
+  const auto test_const_accessors = [](auto p) {
+    // We don't want to run it, just check it compiles.
+    if (false) {
+      (void)p->has_str1();
+      (void)p->str1();
+      (void)p->has_value();
+      (void)p->value();
+      (void)p->has_oneof_member1();
+      (void)p->oneof_member1();
+      (void)p->value_array();
+      (void)p->value_array_size();
+      (void)p->value_array(1);
+      (void)p->has_nested_child_1();
+      (void)p->nested_child_1();
+      (void)p->child_models();
+      (void)p->child_models_size();
+      (void)p->child_models(1);
+      (void)p->child_map_size();
+      (void)p->get_child_map(1);
+    }
+  };
+
+  test_const_accessors(proxy);
+  test_const_accessors(cproxy);
+
+  const auto test_mutable_accessors = [](auto p, bool expected) {
+    const auto r = [&](auto l) { return Requires<decltype(p)>(l) == expected; };
+    EXPECT_TRUE(r([](auto p) -> decltype(p->set_str1("")) {}));
+    EXPECT_TRUE(r([](auto p) -> decltype(p->clear_str1()) {}));
+    EXPECT_TRUE(r([](auto p) -> decltype(p->set_value(1)) {}));
+    EXPECT_TRUE(r([](auto p) -> decltype(p->clear_value()) {}));
+    EXPECT_TRUE(r([](auto p) -> decltype(p->set_oneof_member1("")) {}));
+    EXPECT_TRUE(r([](auto p) -> decltype(p->clear_oneof_member1()) {}));
+    EXPECT_TRUE(r([](auto p) -> decltype(p->mutable_nested_child_1()) {}));
+    EXPECT_TRUE(r([](auto p) -> decltype(p->clear_nested_child_1()) {}));
+    EXPECT_TRUE(r([](auto p) -> decltype(p->add_value_array(1)) {}));
+    EXPECT_TRUE(r([](auto p) -> decltype(p->mutable_value_array()) {}));
+    EXPECT_TRUE(r([](auto p) -> decltype(p->resize_value_array(1)) {}));
+    EXPECT_TRUE(r([](auto p) -> decltype(p->set_value_array(1, 1)) {}));
+    EXPECT_TRUE(r([](auto p) -> decltype(p->add_child_models()) {}));
+    EXPECT_TRUE(r([](auto p) -> decltype(p->mutable_child_models(1)) {}));
+    EXPECT_TRUE(r([](auto p) -> decltype(p->clear_child_map()) {}));
+    EXPECT_TRUE(r([](auto p) -> decltype(p->delete_child_map(1)) {}));
+    EXPECT_TRUE(r(
+        [](auto p) -> decltype(p->set_child_map(1, *p->get_child_map(1))) {}));
+  };
+  test_mutable_accessors(proxy, true);
+  test_mutable_accessors(cproxy, false);
+}
+
+bool ProxyToCProxyMethod(::protos::Ptr<const ChildModel1> child) {
+  return child->child_str1() == "text in child";
+}
+
+TEST(CppGeneratedCode, PassProxyToCProxy) {
+  TestModel model;
+  model.mutable_child_model_1()->set_child_str1("text in child");
+  EXPECT_TRUE(ProxyToCProxyMethod(model.mutable_child_model_1()));
+}
+
+TEST(CppGeneratedCode, PtrImplicitConversion) {
+  TestModel model;
+  model.set_int64(5);
+  ::protos::Ptr<TestModel> model_ptr = &model;
+  EXPECT_EQ(model_ptr->int64(), 5);
+}
+
+TEST(CppGeneratedCode, ClearSubMessage) {
+  // Fill model.
+  TestModel model;
+  model.set_int64(5);
+  auto new_child = model.mutable_child_model_1();
+  new_child->set_child_str1("text in child");
+  ThemeExtension extension1;
+  extension1.set_ext_name("name in extension");
+  EXPECT_TRUE(::protos::SetExtension(&model, theme, extension1).ok());
+  EXPECT_TRUE(model.mutable_child_model_1()->has_child_str1());
+  // Clear using Ptr<T>
+  ::protos::ClearMessage(model.mutable_child_model_1());
+  EXPECT_FALSE(model.mutable_child_model_1()->has_child_str1());
+}
+
+TEST(CppGeneratedCode, ClearMessage) {
+  // Fill model.
+  TestModel model;
+  model.set_int64(5);
+  model.set_str2("Hello");
+  auto new_child = model.add_child_models();
+  ASSERT_TRUE(new_child.ok());
+  new_child.value()->set_child_str1("text in child");
+  ThemeExtension extension1;
+  extension1.set_ext_name("name in extension");
+  EXPECT_TRUE(::protos::SetExtension(&model, theme, extension1).ok());
+  // Clear using T*
+  ::protos::ClearMessage(&model);
+  // Verify that scalars, repeated fields and extensions are cleared.
+  EXPECT_FALSE(model.has_int64());
+  EXPECT_FALSE(model.has_str2());
+  EXPECT_TRUE(model.child_models().empty());
+  EXPECT_FALSE(::protos::HasExtension(&model, theme));
+}
+
+TEST(CppGeneratedCode, DeepCopy) {
+  // Fill model.
+  TestModel model;
+  model.set_int64(5);
+  model.set_str2("Hello");
+  auto new_child = model.add_child_models();
+  ASSERT_TRUE(new_child.ok());
+  new_child.value()->set_child_str1("text in child");
+  ThemeExtension extension1;
+  extension1.set_ext_name("name in extension");
+  EXPECT_TRUE(::protos::SetExtension(&model, theme, extension1).ok());
+  TestModel target;
+  target.set_b1(true);
+  ::protos::DeepCopy(&model, &target);
+  EXPECT_FALSE(target.b1()) << "Target was not cleared before copying content ";
+  EXPECT_EQ(target.str2(), "Hello");
+  EXPECT_TRUE(::protos::HasExtension(&target, theme));
+}
+
+TEST(CppGeneratedCode, HasExtensionAndRegistry) {
+  // Fill model.
+  TestModel source;
+  source.set_int64(5);
+  source.set_str2("Hello");
+  auto new_child = source.add_child_models();
+  ASSERT_TRUE(new_child.ok());
+  new_child.value()->set_child_str1("text in child");
+  ThemeExtension extension1;
+  extension1.set_ext_name("name in extension");
+  ASSERT_TRUE(::protos::SetExtension(&source, theme, extension1).ok());
+
+  // Now that we have a source model with extension data, serialize.
+  ::protos::Arena arena;
+  std::string data = std::string(::protos::Serialize(&source, arena).value());
+
+  // Test with ExtensionRegistry
+  ::protos::ExtensionRegistry extensions({&theme}, arena);
+  TestModel parsed_model = ::protos::Parse<TestModel>(data, extensions).value();
+  EXPECT_TRUE(::protos::HasExtension(&parsed_model, theme));
+}
+
+// TODO : Add BUILD rule to test failures below.
+#ifdef TEST_CLEAR_MESSAGE_FAILURE
+TEST(CppGeneratedCode, ClearConstMessageShouldFail) {
+  // Fill model.
+  TestModel model;
+  model.set_int64(5);
+  model.set_str2("Hello");
+  // Only mutable_ can be cleared not Ptr<const T>.
+  ::protos::ClearMessage(model.child_model_1());
+}
+#endif
+
+}  // namespace
diff --git a/protos_generator/tests/test_model.proto b/protos_generator/tests/test_model.proto
new file mode 100644
index 0000000..e133624
--- /dev/null
+++ b/protos_generator/tests/test_model.proto
@@ -0,0 +1,184 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2023 Google LLC.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google LLC nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+syntax = "proto2";
+
+package protos_generator.test;
+
+import "protos_generator/tests/child_model.proto";
+
+message TestModelContainer {
+  repeated TestModel models = 1;
+  optional ChildModel3 proto_3_child = 2;
+}
+
+message TestModel {
+  optional int32 value = 1;
+  repeated int32 value_array = 2;  // _UPB_MODE_ARRAY
+  repeated int32 value_packed_array = 3
+      [packed = true];  // _UPB_MODE_ARRAY | _UPB_MODE_IS_PACKED
+  repeated int32 value_deprec = 4 [deprecated = true];
+  optional string str1 = 115;
+  optional bool b1 = 9;
+  optional bool b2 = 10;
+  optional string str2 = 50;
+  optional string str3 = 11;
+  optional float optional_float = 14;
+  optional double optional_double = 15;
+  optional int64 optional_int64 = 16;
+  optional uint32 optional_uint32 = 17;
+  optional uint64 optional_uint64 = 18;
+  optional sint32 optional_sint32 = 19;
+  optional sint64 optional_sint64 = 20;
+  optional fixed32 optional_fixed32 = 21;
+  optional fixed64 optional_fixed64 = 22;
+  optional sfixed32 optional_sfixed32 = 23;
+  optional sfixed64 optional_sfixed64 = 24;
+  repeated int64 repeated_int64 = 25;
+  repeated uint64 repeated_uint64 = 26;
+  repeated fixed64 repeated_fixed64 = 27;
+  repeated sfixed64 repeated_sfixed64 = 28;
+  repeated bool repeated_bool = 29;
+  repeated string repeated_string = 35;
+  optional bytes optional_bytes = 36;
+  message NestedChild {
+    optional string nested_child_name = 211;
+  }
+  optional NestedChild nested_child_1 = 212;
+  optional ChildModel1 child_model_1 = 222;
+  repeated ChildModel1 child_models = 223;
+  optional ChildModel1 bar = 224;
+  oneof child_oneof1 {
+    string oneof_member1 = 98;
+    bool oneof_member2 = 99;
+  }
+  optional int32 int_value_with_default = 31
+      [default = 65];  // Not supported yet
+  optional string string_value_with_default = 32
+      [default = "hello"];  // Not supported yet
+  optional float float_value_with_default = 33 [default = inf];
+  optional float double_value_with_default = 34 [default = -inf];
+
+  map<int32, ChildModel1> child_map = 225;
+  optional TestModel recursive_child = 226;
+  map<string, ChildModel1> child_str_map = 227;
+  map<string, int32> str_to_int_map = 228;
+  map<string, string> str_to_str_map = 229;
+
+  extend TestAnnotation {
+    optional OtherExtension in_message_ext = 15000;
+  }
+
+  enum Category {
+    IMAGES = 5;
+    NEWS = 6;
+    VIDEO = 7;
+    RADIO = 8 [deprecated = true];
+  }
+  optional Category category = 37;
+
+  // keyword collisions (double, template, ...)
+  oneof type {
+    string string = 230;
+    int64 int64 = 231;
+    double double = 232;
+  }
+  optional string template = 233;
+  optional string msg = 234;
+  optional string arena = 235;
+
+  // Tests publicly imported enum.
+  optional TestEnum imported_enum = 238;
+
+  optional string phase = 239;
+  optional bool clear_phase = 240;
+
+  optional string doc_id = 241;
+  optional bool set_doc_id = 242;
+
+  extensions 10000 to max;
+}
+
+// Old version with fewer fields to test backward/forward compatibility.
+message TestModelContainerV1 {
+  repeated TestModelV1 models = 1;
+}
+
+message TestModelV1 {
+  optional int32 value = 1;
+  repeated int32 value2 = 2;
+  repeated int32 value3 = 3 [packed = true];
+  repeated int32 value4 = 4 [deprecated = true];
+  optional bool b1 = 9;
+  optional bool b2 = 10;
+  optional string str2 = 50;
+}
+
+enum PrimaryColors {
+  RED = 1;
+  GREEN = 2;
+  BLUE = 3;
+}
+
+// TestModel extension.
+message ThemeExtension {
+  extend TestModel {
+    optional ThemeExtension theme_extension = 12003;
+  }
+  optional string ext_name = 1;
+  optional bool ext_bool = 2;
+}
+
+extend TestModel {
+  optional ThemeExtension theme = 12001;
+}
+
+message OtherExtension {
+  optional string ext2_name = 1;
+}
+
+extend TestModel {
+  optional OtherExtension other_ext = 12002;
+}
+
+message TestAnnotation {
+  extensions 10000 to max;
+}
+
+message TestMessageHasEnum {
+  optional EnumDeclaredAfterMessage enum_declared_after_message = 1;
+}
+
+enum EnumDeclaredAfterMessage {
+  ZERO = 0;
+  ONE = 1;
+  TWO = 2;
+  THREE = 3;
+}