[Upb C++] Implement RepeatedField

PiperOrigin-RevId: 538944010
diff --git a/protos/BUILD b/protos/BUILD
index 604ceff..be62cb7 100644
--- a/protos/BUILD
+++ b/protos/BUILD
@@ -39,9 +39,34 @@
 licenses(["notice"])
 
 cc_library(
+    name = "repeated_field",
+    hdrs = [
+        "repeated_field.h",
+        "repeated_field_iterator.h",
+    ],
+    copts = UPB_DEFAULT_CPPOPTS,
+    visibility = ["//visibility:public"],
+    deps = [
+        ":protos",
+        ":protos_traits",
+        "//:collections_internal",
+        "//:message_copy",
+        "//:mini_table",
+        "//:port",
+        "//:upb",
+        "@com_google_absl//absl/base:core_headers",
+        "@com_google_absl//absl/strings",
+    ],
+)
+
+cc_library(
     name = "protos",
-    srcs = ["protos.cc"],
-    hdrs = ["protos.h"],
+    srcs = [
+        "protos.cc",
+    ],
+    hdrs = [
+        "protos.h",
+    ],
     copts = UPB_DEFAULT_CPPOPTS,
     visibility = ["//visibility:public"],
     deps = [
@@ -50,10 +75,21 @@
         "//:upb",
         "@com_google_absl//absl/status",
         "@com_google_absl//absl/status:statusor",
+        "@com_google_absl//absl/strings",
         "@com_google_absl//absl/strings:str_format",
     ],
 )
 
+# Internally used type traits.
+cc_library(
+    name = "protos_traits",
+    hdrs = [
+        "protos_traits.h",
+    ],
+    copts = UPB_DEFAULT_CPPOPTS,
+    visibility = ["//visibility:private"],
+)
+
 cc_library(
     name = "protos_internal",
     hdrs = ["protos_internal.h"],
@@ -80,6 +116,7 @@
     deps = [
         ":protos",
         ":protos_internal",
+        ":repeated_field",
     ],
 )
 
diff --git a/protos/bazel/upb_cc_proto_library.bzl b/protos/bazel/upb_cc_proto_library.bzl
index 8fad95a..0c65018 100644
--- a/protos/bazel/upb_cc_proto_library.bzl
+++ b/protos/bazel/upb_cc_proto_library.bzl
@@ -286,6 +286,7 @@
                 "@com_google_absl//absl/strings",
                 "@com_google_absl//absl/status:statusor",
                 "//protos",
+                "//protos:repeated_field",
             ],
         ),
     }),
diff --git a/protos/protos_traits.h b/protos/protos_traits.h
new file mode 100644
index 0000000..720f2c6
--- /dev/null
+++ b/protos/protos_traits.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2009-2023, Google LLC
+ * All rights reserved.
+ *
+ * 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 Google LLC 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_PROTOS_TRAITS_H_
+#define THIRD_PARTY_UPB_PROTOS_PROTOS_TRAITS_H_
+
+#include <type_traits>
+
+namespace protos::internal {
+
+template <typename T, typename T2>
+using add_const_if_T_is_const =
+    std::conditional_t<std::is_const_v<T>, const T2, T2>;
+
+}  // namespace protos::internal
+
+#endif  // THIRD_PARTY_UPB_PROTOS_PROTOS_TRAITS_H_
diff --git a/protos/repeated_field.h b/protos/repeated_field.h
new file mode 100644
index 0000000..a040e50
--- /dev/null
+++ b/protos/repeated_field.h
@@ -0,0 +1,303 @@
+/*
+ * Copyright (c) 2009-2023, Google LLC
+ * All rights reserved.
+ *
+ * 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 Google LLC 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_REPEATED_FIELD_H_
+#define UPB_PROTOS_REPEATED_FIELD_H_
+
+#include <cstddef>
+#include <iterator>
+#include <type_traits>
+
+#include "absl/base/attributes.h"
+#include "absl/strings/string_view.h"
+#include "protos/protos.h"
+#include "protos/protos_traits.h"
+#include "protos/repeated_field_iterator.h"
+#include "upb/base/string_view.h"
+#include "upb/collections/array.h"
+#include "upb/collections/array_internal.h"
+#include "upb/mem/arena.h"
+#include "upb/message/copy.h"
+#include "upb/mini_table/types.h"
+
+// Must be last:
+#include "upb/port/def.inc"
+
+namespace protos {
+
+namespace internal {
+
+// Shared implementation of repeated fields for absl::string_view and
+// message types for mutable and immutable variants.
+//
+// Immutable (const accessor), constructs this class with a nullptr upb_Array*
+// when the underlying array in the message is empty.
+//
+// Mutable accessors on the other hand, will allocate a new empty non-null
+// upb_Array* for the message when the RepeatedFieldProxy is constructed.
+template <class T>
+class RepeatedFieldProxyBase {
+  using Array = add_const_if_T_is_const<T, upb_Array>;
+
+ public:
+  explicit RepeatedFieldProxyBase(Array* arr) : arr_(arr) {}
+
+  size_t size() const { return arr_ != nullptr ? upb_Array_Size(arr_) : 0; }
+
+  bool empty() const { return size() == 0; }
+
+ protected:
+  // Returns upb_Array string member.
+  inline absl::string_view GetString(size_t n) const;
+
+  // Returns upb_Array message member.
+  inline upb_Message* GetMessage(size_t n) const;
+
+  Array* arr_;
+};
+
+template <class T>
+inline absl::string_view RepeatedFieldProxyBase<T>::GetString(size_t n) const {
+  upb_MessageValue message_value = upb_Array_Get(arr_, n);
+  return absl::string_view(message_value.str_val.data,
+                           message_value.str_val.size);
+}
+
+template <class T>
+upb_Message* RepeatedFieldProxyBase<T>::GetMessage(size_t n) const {
+  auto** messages =
+      static_cast<upb_Message**>(upb_Array_MutableDataPtr(this->arr_));
+  return messages[n];
+}
+
+template <class T>
+class RepeatedFieldProxyMutableBase : public RepeatedFieldProxyBase<T> {
+ public:
+  RepeatedFieldProxyMutableBase(upb_Array* arr, upb_Arena* arena)
+      : RepeatedFieldProxyBase<T>(arr), arena_(arena) {}
+
+  void clear() { upb_Array_Resize(this->arr_, 0, arena_); }
+
+ protected:
+  upb_Arena* arena_;
+};
+
+// RepeatedField proxy for repeated messages.
+template <class T>
+class RepeatedFieldProxy
+    : public std::conditional_t<std::is_const_v<T>, RepeatedFieldProxyBase<T>,
+                                RepeatedFieldProxyMutableBase<T>> {
+  static_assert(!std::is_same_v<T, absl::string_view>, "");
+  static_assert(!std::is_same_v<T, const absl::string_view>, "");
+  static_assert(!std::is_arithmetic_v<T>, "");
+  static constexpr bool kIsConst = std::is_const_v<T>;
+
+ public:
+  explicit RepeatedFieldProxy(const upb_Array* arr)
+      : RepeatedFieldProxyBase<T>(arr) {}
+  RepeatedFieldProxy(upb_Array* arr, upb_Arena* arena)
+      : RepeatedFieldProxyMutableBase<T>(arr, arena) {}
+  // Constructor used by ::protos::Ptr.
+  RepeatedFieldProxy(const RepeatedFieldProxy&) = default;
+
+  // T::CProxy [] operator specialization.
+  typename T::CProxy operator[](size_t n) const {
+    upb_MessageValue message_value = upb_Array_Get(this->arr_, n);
+    return ::protos::internal::CreateMessage<typename std::remove_const_t<T>>(
+        (upb_Message*)message_value.msg_val);
+  }
+
+  // TODO(b:/280069986) : Audit/Finalize based on Iterator Design.
+  // T::Proxy [] operator specialization.
+  template <int&... DeductionBlocker, bool b = !kIsConst,
+            typename = std::enable_if_t<b>>
+  typename T::Proxy operator[](size_t n) {
+    return ::protos::internal::CreateMessageProxy<T>(this->GetMessage(n),
+                                                     this->arena_);
+  }
+
+  // Mutable message reference specialization.
+  template <int&... DeductionBlocker, bool b = !kIsConst,
+            typename = std::enable_if_t<b>>
+  void push_back(const T& t) {
+    upb_MessageValue message_value;
+    message_value.msg_val =
+        upb_Message_DeepClone(GetInternalMsg(t), T::minitable(), this->arena_);
+    upb_Array_Append(this->arr_, message_value, this->arena_);
+  }
+
+  // Mutable message add using move.
+  template <int&... DeductionBlocker, bool b = !kIsConst,
+            typename = std::enable_if_t<b>>
+  void push_back(T&& msg) {
+    upb_MessageValue message_value;
+    message_value.msg_val = GetInternalMsg(msg);
+    upb_Arena_Fuse(GetArena(msg), this->arena_);
+    upb_Array_Append(this->arr_, message_value, this->arena_);
+    T moved_msg = std::move(msg);
+  }
+
+ private:
+  friend class ::protos::Ptr<T>;
+};
+
+// RepeatedField proxy for repeated strings.
+template <class T>
+class RepeatedFieldStringProxy
+    : public std::conditional_t<std::is_const_v<T>, RepeatedFieldProxyBase<T>,
+                                RepeatedFieldProxyMutableBase<T>> {
+  static_assert(std::is_same_v<T, absl::string_view> ||
+                    std::is_same_v<T, const absl::string_view>,
+                "");
+  static constexpr bool kIsConst = std::is_const_v<T>;
+
+ public:
+  // Immutable constructor.
+  explicit RepeatedFieldStringProxy(const upb_Array* arr)
+      : RepeatedFieldProxyBase<T>(arr) {}
+  // Mutable constructor.
+  RepeatedFieldStringProxy(upb_Array* arr, upb_Arena* arena)
+      : RepeatedFieldProxyMutableBase<T>(arr, arena) {}
+  // Constructor used by ::protos::Ptr.
+  RepeatedFieldStringProxy(const RepeatedFieldStringProxy&) = default;
+
+  T operator[](size_t n) const { return this->GetString(n); }
+
+  template <int&... DeductionBlocker, bool b = !kIsConst,
+            typename = std::enable_if_t<b>>
+  void push_back(T t) {
+    upb_MessageValue message_value;
+    // Copy string to arena.
+    UPB_ASSERT(this->arena_);
+    char* data = (char*)upb_Arena_Malloc(this->arena_, t.size());
+    UPB_ASSERT(data);
+    memcpy(data, t.data(), t.size());
+    message_value.str_val = upb_StringView_FromDataAndSize(data, t.size());
+    upb_Array_Append(this->arr_, message_value, this->arena_);
+  }
+};
+
+// RepeatedField proxy for repeated scalar types.
+template <typename T>
+class RepeatedFieldScalarProxy
+    : public std::conditional_t<std::is_const_v<T>, RepeatedFieldProxyBase<T>,
+                                RepeatedFieldProxyMutableBase<T>> {
+  static_assert(std::is_arithmetic_v<T>, "");
+  static constexpr bool kIsConst = std::is_const_v<T>;
+
+ public:
+  explicit RepeatedFieldScalarProxy(const upb_Array* arr)
+      : RepeatedFieldProxyBase<T>(arr) {}
+  RepeatedFieldScalarProxy(upb_Array* arr, upb_Arena* arena)
+      : RepeatedFieldProxyMutableBase<T>(arr, arena) {}
+  // Constructor used by ::protos::Ptr.
+  RepeatedFieldScalarProxy(const RepeatedFieldScalarProxy&) = default;
+
+  T operator[](size_t n) const {
+    upb_MessageValue message_value = upb_Array_Get(this->arr_, n);
+    typename std::remove_const_t<T> val;
+    memcpy(&val, &message_value, sizeof(T));
+    return val;
+  }
+
+  template <int&... DeductionBlocker, bool b = !kIsConst,
+            typename = std::enable_if_t<b>>
+  void push_back(T t) {
+    upb_MessageValue message_value;
+    memcpy(&message_value, &t, sizeof(T));
+    upb_Array_Append(this->arr_, message_value, this->arena_);
+  }
+
+  // Iterator support.
+  using iterator = internal::RepeatedScalarIterator<T>;
+
+  iterator begin() const { return iterator(unsafe_array()); }
+  iterator cbegin() const { return begin(); }
+  iterator end() const { return iterator(unsafe_array() + this->size()); }
+  iterator cend() const { return end(); }
+
+  // Reverse iterator support.
+  using const_reverse_iterator = std::reverse_iterator<iterator>;
+  using reverse_iterator = std::reverse_iterator<iterator>;
+  reverse_iterator rbegin() { return reverse_iterator(end()); }
+  const_reverse_iterator rbegin() const {
+    return const_reverse_iterator(end());
+  }
+  reverse_iterator rend() { return reverse_iterator(begin()); }
+  const_reverse_iterator rend() const {
+    return const_reverse_iterator(begin());
+  }
+
+ private:
+  T* unsafe_array() const {
+    if (kIsConst) {
+      const void* unsafe_ptr = ::upb_Array_DataPtr(this->arr_);
+      return static_cast<T*>(const_cast<void*>(unsafe_ptr));
+    }
+    if (!kIsConst) {
+      void* unsafe_ptr =
+          ::upb_Array_MutableDataPtr(const_cast<upb_Array*>(this->arr_));
+      return static_cast<T*>(unsafe_ptr);
+    }
+  }
+};
+
+}  // namespace internal
+
+template <typename T>
+class RepeatedField {
+  static constexpr bool kIsString = std::is_same_v<T, absl::string_view>;
+  static constexpr bool kIsScalar = std::is_arithmetic_v<T>;
+
+ public:
+  using Proxy = std::conditional_t<
+      kIsScalar, internal::RepeatedFieldScalarProxy<T>,
+      std::conditional_t<kIsString, internal::RepeatedFieldStringProxy<T>,
+                         internal::RepeatedFieldProxy<T>>>;
+  using CProxy = std::conditional_t<
+      kIsScalar, internal::RepeatedFieldScalarProxy<const T>,
+      std::conditional_t<kIsString, internal::RepeatedFieldStringProxy<const T>,
+                         internal::RepeatedFieldProxy<const T>>>;
+  // TODO(b/286451125): T supports incomplete type from fwd.h forwarding headers
+  // We would like to reference T::CProxy. Validate forwarding header design.
+  using ValueProxy = std::conditional_t<
+      kIsScalar, T,
+      std::conditional_t<kIsString, absl::string_view, ::protos::Ptr<T>>>;
+  using ValueCProxy = std::conditional_t<
+      kIsScalar, const T,
+      std::conditional_t<kIsString, absl::string_view, ::protos::Ptr<const T>>>;
+  using Access = std::conditional_t<
+      kIsScalar, internal::RepeatedFieldScalarProxy<T>,
+      std::conditional_t<kIsString, internal::RepeatedFieldStringProxy<T>,
+                         internal::RepeatedFieldProxy<T>>>;
+};
+
+}  // namespace protos
+
+#include "upb/port/undef.inc"
+
+#endif  // UPB_PROTOS_REPEATED_FIELD_H_
diff --git a/protos/repeated_field_iterator.h b/protos/repeated_field_iterator.h
new file mode 100644
index 0000000..8d9f04e
--- /dev/null
+++ b/protos/repeated_field_iterator.h
@@ -0,0 +1,158 @@
+/*
+ * Copyright (c) 2009-2023, Google LLC
+ * All rights reserved.
+ *
+ * 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 Google LLC 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_REPEATED_FIELD_ITERATOR_H_
+#define UPB_PROTOS_REPEATED_FIELD_ITERATOR_H_
+
+#include <cstddef>
+#include <iterator>
+#include <type_traits>
+
+#include "absl/strings/string_view.h"
+#include "protos/protos.h"
+#include "upb/base/string_view.h"
+#include "upb/collections/array.h"
+#include "upb/mem/arena.h"
+#include "upb/message/copy.h"
+#include "upb/mini_table/types.h"
+
+// Must be last:
+#include "upb/port/def.inc"
+
+namespace protos {
+
+namespace internal {
+
+// TODO(b/279086429): Implement std iterator for strings and messages
+template <typename T>
+class RepeatedFieldScalarProxy;
+
+template <typename T>
+class RepeatedScalarIterator {
+ public:
+  using iterator_category = std::random_access_iterator_tag;
+  using value_type = typename std::remove_const<T>::type;
+  using difference_type = std::ptrdiff_t;
+  using pointer = T*;
+  using reference = T&;
+
+  constexpr RepeatedScalarIterator() noexcept : it_(nullptr) {}
+  RepeatedScalarIterator(const RepeatedScalarIterator& other) = default;
+  RepeatedScalarIterator& operator=(const RepeatedScalarIterator& other) =
+      default;
+
+  // deref TODO(b/286450722) Change to use a proxy.
+  constexpr reference operator*() const noexcept { return *it_; }
+  constexpr pointer operator->() const noexcept { return it_; }
+
+ private:
+  // Hide the internal type.
+  using iterator = RepeatedScalarIterator;
+
+ public:
+  // {inc,dec}rementable
+  constexpr iterator& operator++() noexcept {
+    ++it_;
+    return *this;
+  }
+  constexpr iterator operator++(int) noexcept { return iterator(it_++); }
+  constexpr iterator& operator--() noexcept {
+    --it_;
+    return *this;
+  }
+  constexpr iterator operator--(int) noexcept { return iterator(it_--); }
+
+  // equality_comparable
+  friend constexpr bool operator==(const iterator x,
+                                   const iterator y) noexcept {
+    return x.it_ == y.it_;
+  }
+  friend constexpr bool operator!=(const iterator x,
+                                   const iterator y) noexcept {
+    return x.it_ != y.it_;
+  }
+
+  // less_than_comparable
+  friend constexpr bool operator<(const iterator x, const iterator y) noexcept {
+    return x.it_ < y.it_;
+  }
+  friend constexpr bool operator<=(const iterator x,
+                                   const iterator y) noexcept {
+    return x.it_ <= y.it_;
+  }
+  friend constexpr bool operator>(const iterator x, const iterator y) noexcept {
+    return x.it_ > y.it_;
+  }
+  friend constexpr bool operator>=(const iterator x,
+                                   const iterator y) noexcept {
+    return x.it_ >= y.it_;
+  }
+
+  constexpr iterator& operator+=(difference_type d) noexcept {
+    it_ += d;
+    return *this;
+  }
+  constexpr iterator operator+(difference_type d) const noexcept {
+    return iterator(it_ + d);
+  }
+  friend constexpr iterator operator+(const difference_type d,
+                                      iterator it) noexcept {
+    return it + d;
+  }
+
+  constexpr iterator& operator-=(difference_type d) noexcept {
+    it_ -= d;
+    return *this;
+  }
+  constexpr iterator operator-(difference_type d) const noexcept {
+    return iterator(it_ - d);
+  }
+
+  // indexable
+  constexpr reference operator[](difference_type d) const noexcept {
+    return it_[d];
+  }
+
+  // random access iterator
+  friend constexpr difference_type operator-(iterator x, iterator y) noexcept {
+    return x.it_ - y.it_;
+  }
+
+ private:
+  friend class RepeatedFieldScalarProxy<T>;
+
+  // Create from internal::RepeatedFieldScalarProxy.
+  explicit RepeatedScalarIterator(T* it) noexcept : it_(it) {}
+
+  // The internal iterator.
+  T* it_;
+};
+
+}  // namespace internal
+
+}  // namespace protos
+
+#endif  // UPB_PROTOS_REPEATED_FIELD_ITERATOR_H_
diff --git a/protos_generator/gen_messages.cc b/protos_generator/gen_messages.cc
index 8d81fbd..3fb5d4d 100644
--- a/protos_generator/gen_messages.cc
+++ b/protos_generator/gen_messages.cc
@@ -89,25 +89,24 @@
   }
 
   // Forward declaration of Proto Class for GCC handling of free friend method.
-  output("class $0;", ClassName(descriptor));
-  output("namespace internal {\n");
+  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");
+  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");
+  output("}  // namespace internal\n\n");
 }
 
 void WriteModelAccessDeclaration(const protobuf::Descriptor* descriptor,
                                  Output& output) {
   output(
       R"cc(
-
         class $0Access {
          public:
           $0Access() {}
@@ -189,8 +188,6 @@
   WriteUsingEnumsInHeader(descriptor, file_enums, output);
   WriteDefaultInstanceHeader(descriptor, output);
   WriteExtensionIdentifiersInClassHeader(descriptor, file_exts, output);
-  output.Indent();
-  output.Indent();
   if (descriptor->extension_range_count()) {
     // for typetrait checking
     output("using ExtendableType = $0;\n", ClassName(descriptor));
@@ -200,11 +197,16 @@
   // 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:
         $0(upb_Message* msg, upb_Arena* arena) : $0Access() {
           msg_ = ($1*)msg;
@@ -264,6 +266,7 @@
         friend $0::Proxy(::protos::CreateMessage<$0>(::protos::Arena& arena));
         friend $0::Proxy(::protos::internal::CreateMessageProxy<$0>(
             upb_Message*, upb_Arena*));
+        friend class RepeatedFieldProxy;
         friend class $0CProxy;
         friend class $0Access;
         friend class ::protos::Ptr<$0>;
@@ -307,6 +310,7 @@
         private:
         $0CProxy(void* msg) : internal::$0Access(($1*)msg, nullptr){};
         friend $0::CProxy(::protos::internal::CreateMessage<$0>(upb_Message* msg));
+        friend class RepeatedFieldProxy;
         friend class ::protos::Ptr<$0>;
         friend class ::protos::Ptr<const $0>;
         static void Rebind($0CProxy& lhs, const $0CProxy& rhs) {
diff --git a/protos_generator/gen_repeated_fields.cc b/protos_generator/gen_repeated_fields.cc
index 2564334..48d77c1 100644
--- a/protos_generator/gen_repeated_fields.cc
+++ b/protos_generator/gen_repeated_fields.cc
@@ -30,6 +30,7 @@
 #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"
@@ -39,6 +40,7 @@
 #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;
@@ -53,14 +55,13 @@
         R"cc(
           using $0Access::$1;
           using $0Access::$1_size;
-          using $0Access::mutable_$1;
         )cc",
         class_name, resolved_field_name);
     if (!read_only) {
       output(
           R"cc(
             using $0Access::add_$1;
-            using $0Access::clear_$1;
+            using $0Access::mutable_$1;
           )cc",
           class_name, resolved_field_name);
     }
@@ -75,7 +76,7 @@
       output(
           R"cc(
             using $0Access::add_$1;
-            using $0Access::clear_$1;
+            using $0Access::mutable_$1;
             using $0Access::resize_$1;
             using $0Access::set_$1;
           )cc",
@@ -96,8 +97,6 @@
           $0_$2(msg_, &len);
           return len;
         }
-
-        inline void clear_$1() { $0_clear_$2(msg_); }
       )cc",
       MessageName(desc), resolved_field_name, resolved_upbc_name);
 
@@ -105,16 +104,34 @@
     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),
-        MessagePtrConstType(field, /* const */ true), resolved_field_name,
-        resolved_upbc_name);
+        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);
@@ -170,6 +187,27 @@
       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);
+        };
+        ::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,
@@ -215,6 +253,27 @@
       )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);
+        };
+        ::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,
@@ -258,6 +317,27 @@
       )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);
+        };
+        ::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/protoc-gen-upb-protos.cc b/protos_generator/protoc-gen-upb-protos.cc
index bc39d3e..0355b78 100644
--- a/protos_generator/protoc-gen-upb-protos.cc
+++ b/protos_generator/protoc-gen-upb-protos.cc
@@ -130,6 +130,7 @@
 
 #include "protos/protos.h"
 #include "protos/protos_internal.h"
+#include "protos/repeated_field.h"
 #include "upb/upb.hpp"
 
 #include "absl/strings/string_view.h"
diff --git a/protos_generator/tests/BUILD b/protos_generator/tests/BUILD
index efdc4d7..82cbf55 100644
--- a/protos_generator/tests/BUILD
+++ b/protos_generator/tests/BUILD
@@ -149,5 +149,6 @@
         "@com_google_googletest//:gtest_main",
         "//:upb",
         "//protos",
+        "//protos:repeated_field",
     ],
 )
diff --git a/protos_generator/tests/test_generated.cc b/protos_generator/tests/test_generated.cc
index 49dcae7..b2866b6 100644
--- a/protos_generator/tests/test_generated.cc
+++ b/protos_generator/tests/test_generated.cc
@@ -29,6 +29,8 @@
 
 #include "gtest/gtest.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"
@@ -333,29 +335,29 @@
 TEST(CppGeneratedCode, RepeatedMessages) {
   ::protos::Arena arena;
   auto test_model = ::protos::CreateMessage<TestModel>(arena);
-  EXPECT_EQ(0, test_model.child_model_2_size());
+  EXPECT_EQ(0, test_model.child_models_size());
   // Should be able to clear repeated field when empty.
-  test_model.clear_child_model_2();
-  EXPECT_EQ(0, test_model.child_model_2_size());
+  test_model.mutable_child_models()->clear();
+  EXPECT_EQ(0, test_model.child_models_size());
   // Add 2 children.
-  auto new_child = test_model.add_child_model_2();
+  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_model_2();
+  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_model_2_size());
+  EXPECT_EQ(2, test_model.child_models_size());
   // Mutable access.
-  auto mutable_first = test_model.mutable_child_model_2(0);
+  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_model_2(1);
+  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_model_2(0);
+  auto view_first = test_model.child_models(0);
   EXPECT_EQ(view_first->child_str1(), "change1");
-  auto view_second = test_model.child_model_2(1);
+  auto view_second = test_model.child_models(1);
   EXPECT_EQ(view_second->child_str1(), "change2");
 }
 
@@ -364,7 +366,7 @@
   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.clear_value_array();
+  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));
@@ -380,12 +382,173 @@
   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);
+
+  ASSERT_EQ(test_model.value_array().size(), 3);
+  EXPECT_EQ(test_model.value_array()[0], 5);
+  EXPECT_EQ(test_model.value_array()[1], 16);
+  EXPECT_EQ(test_model.value_array()[2], 27);
+}
+
+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");
+
+  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");
+  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));
+  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.clear_repeated_string();
+  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"));
diff --git a/protos_generator/tests/test_model.proto b/protos_generator/tests/test_model.proto
index a675c3f..d372cc1 100644
--- a/protos_generator/tests/test_model.proto
+++ b/protos_generator/tests/test_model.proto
@@ -43,7 +43,7 @@
   }
   optional NestedChild nested_child_1 = 212;
   optional ChildModel1 child_model_1 = 222;
-  repeated ChildModel1 child_model_2 = 223;
+  repeated ChildModel1 child_models = 223;
   optional ChildModel1 bar = 224;
   oneof child_oneof1 {
     string oneof_member1 = 98;
diff --git a/upb/collections/array.c b/upb/collections/array.c
index aeed25e..b122fe7 100644
--- a/upb/collections/array.c
+++ b/upb/collections/array.c
@@ -50,6 +50,12 @@
   return _upb_Array_New(a, 4, _upb_Array_CTypeSizeLg2(type));
 }
 
+const void* upb_Array_DataPtr(const upb_Array* arr) {
+  return _upb_array_ptr((upb_Array*)arr);
+}
+
+void* upb_Array_MutableDataPtr(upb_Array* arr) { return _upb_array_ptr(arr); }
+
 size_t upb_Array_Size(const upb_Array* arr) { return arr->size; }
 
 upb_MessageValue upb_Array_Get(const upb_Array* arr, size_t i) {
diff --git a/upb/collections/array.h b/upb/collections/array.h
index 2e73e7a..5c5b6e4 100644
--- a/upb/collections/array.h
+++ b/upb/collections/array.h
@@ -76,6 +76,12 @@
 // Returns false on allocation failure.
 UPB_API bool upb_Array_Resize(upb_Array* array, size_t size, upb_Arena* arena);
 
+// Returns pointer to array data.
+UPB_API const void* upb_Array_DataPtr(const upb_Array* arr);
+
+// Returns mutable pointer to array data.
+UPB_API void* upb_Array_MutableDataPtr(upb_Array* arr);
+
 #ifdef __cplusplus
 } /* extern "C" */
 #endif