[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