blob: 6d37b569fa2bf186442cf695ff330595d5f625c3 [file] [log] [blame]
Adam Cozzette501ecec2023-09-26 14:36:20 -07001// Protocol Buffers - Google's data interchange format
2// Copyright 2023 Google LLC. All rights reserved.
Adam Cozzette501ecec2023-09-26 14:36:20 -07003//
Protobuf Team Bot0fab7732023-11-20 13:38:15 -08004// Use of this source code is governed by a BSD-style
5// license that can be found in the LICENSE file or at
6// https://developers.google.com/open-source/licenses/bsd
Adam Cozzette501ecec2023-09-26 14:36:20 -07007
8#ifndef UPB_PROTOS_REPEATED_FIELD_H_
9#define UPB_PROTOS_REPEATED_FIELD_H_
10
Eric Salo7c5e18b2023-11-27 11:14:23 -080011#include <assert.h>
12
Adam Cozzette501ecec2023-09-26 14:36:20 -070013#include <cstddef>
14#include <iterator>
15#include <type_traits>
16
17#include "absl/strings/string_view.h"
18#include "protos/protos.h"
19#include "protos/protos_traits.h"
20#include "protos/repeated_field_iterator.h"
21#include "upb/base/string_view.h"
Adam Cozzette501ecec2023-09-26 14:36:20 -070022#include "upb/mem/arena.h"
Eric Salo07fba1d2023-09-29 14:50:56 -070023#include "upb/message/array.h"
Adam Cozzette501ecec2023-09-26 14:36:20 -070024#include "upb/message/copy.h"
25#include "upb/message/types.h"
26
Adam Cozzette501ecec2023-09-26 14:36:20 -070027namespace protos {
Adam Cozzette501ecec2023-09-26 14:36:20 -070028namespace internal {
29
30// Shared implementation of repeated fields for absl::string_view and
31// message types for mutable and immutable variants.
32//
33// Immutable (const accessor), constructs this class with a nullptr upb_Array*
34// when the underlying array in the message is empty.
35//
36// Mutable accessors on the other hand, will allocate a new empty non-null
37// upb_Array* for the message when the RepeatedFieldProxy is constructed.
38template <class T>
39class RepeatedFieldProxyBase {
40 using Array = add_const_if_T_is_const<T, upb_Array>;
41
42 public:
43 explicit RepeatedFieldProxyBase(Array* arr, upb_Arena* arena)
44 : arr_(arr), arena_(arena) {}
45
46 size_t size() const { return arr_ != nullptr ? upb_Array_Size(arr_) : 0; }
47
48 bool empty() const { return size() == 0; }
49
50 protected:
51 // Returns upb_Array message member.
52 inline upb_Message* GetMessage(size_t n) const;
53
54 Array* arr_;
55 upb_Arena* arena_;
56};
57
58template <class T>
59upb_Message* RepeatedFieldProxyBase<T>::GetMessage(size_t n) const {
60 auto** messages =
61 static_cast<upb_Message**>(upb_Array_MutableDataPtr(this->arr_));
62 return messages[n];
63}
64
65template <class T>
66class RepeatedFieldProxyMutableBase : public RepeatedFieldProxyBase<T> {
67 public:
68 RepeatedFieldProxyMutableBase(upb_Array* arr, upb_Arena* arena)
69 : RepeatedFieldProxyBase<T>(arr, arena) {}
70
71 void clear() { upb_Array_Resize(this->arr_, 0, this->arena_); }
72};
73
74// RepeatedField proxy for repeated messages.
75template <class T>
76class RepeatedFieldProxy
77 : public std::conditional_t<std::is_const_v<T>, RepeatedFieldProxyBase<T>,
78 RepeatedFieldProxyMutableBase<T>> {
79 static_assert(!std::is_same_v<T, absl::string_view>, "");
80 static_assert(!std::is_same_v<T, const absl::string_view>, "");
81 static_assert(!std::is_arithmetic_v<T>, "");
82 static constexpr bool kIsConst = std::is_const_v<T>;
83
84 public:
85 using value_type = std::remove_const_t<T>;
86 using size_type = size_t;
87 using difference_type = ptrdiff_t;
88 using iterator = internal::Iterator<MessageIteratorPolicy<T>>;
89 using reference = typename iterator::reference;
90 using pointer = typename iterator::pointer;
91 using reverse_iterator = std::reverse_iterator<iterator>;
92
93 explicit RepeatedFieldProxy(const upb_Array* arr, upb_Arena* arena)
94 : RepeatedFieldProxyBase<T>(arr, arena) {}
95 RepeatedFieldProxy(upb_Array* arr, upb_Arena* arena)
96 : RepeatedFieldProxyMutableBase<T>(arr, arena) {}
97 // Constructor used by ::protos::Ptr.
98 RepeatedFieldProxy(const RepeatedFieldProxy&) = default;
99
100 // T::CProxy [] operator specialization.
101 typename T::CProxy operator[](size_t n) const {
102 upb_MessageValue message_value = upb_Array_Get(this->arr_, n);
103 return ::protos::internal::CreateMessage<typename std::remove_const_t<T>>(
104 (upb_Message*)message_value.msg_val, this->arena_);
105 }
106
107 // TODO : Audit/Finalize based on Iterator Design.
108 // T::Proxy [] operator specialization.
109 template <int&... DeductionBlocker, bool b = !kIsConst,
110 typename = std::enable_if_t<b>>
111 typename T::Proxy operator[](size_t n) {
112 return ::protos::internal::CreateMessageProxy<T>(this->GetMessage(n),
113 this->arena_);
114 }
115
116 // Mutable message reference specialization.
117 template <int&... DeductionBlocker, bool b = !kIsConst,
118 typename = std::enable_if_t<b>>
119 void push_back(const T& t) {
120 upb_MessageValue message_value;
121 message_value.msg_val = upb_Message_DeepClone(
122 PrivateAccess::GetInternalMsg(&t), ::protos::internal::GetMiniTable(&t),
123 this->arena_);
124 upb_Array_Append(this->arr_, message_value, this->arena_);
125 }
126
127 // Mutable message add using move.
128 template <int&... DeductionBlocker, bool b = !kIsConst,
129 typename = std::enable_if_t<b>>
130 void push_back(T&& msg) {
131 upb_MessageValue message_value;
132 message_value.msg_val = PrivateAccess::GetInternalMsg(&msg);
133 upb_Arena_Fuse(GetArena(&msg), this->arena_);
134 upb_Array_Append(this->arr_, message_value, this->arena_);
135 T moved_msg = std::move(msg);
136 }
137
138 iterator begin() const {
139 return iterator({static_cast<upb_Message**>(
140 const_cast<void*>(upb_Array_DataPtr(this->arr_))),
141 this->arena_});
142 }
143 iterator end() const { return begin() + this->size(); }
144 reverse_iterator rbegin() const { return reverse_iterator(end()); }
145 reverse_iterator rend() const { return reverse_iterator(begin()); }
146
147 private:
148 friend class ::protos::Ptr<T>;
149};
150
151// RepeatedField proxy for repeated strings.
152template <class T>
153class RepeatedFieldStringProxy
154 : public std::conditional_t<std::is_const_v<T>, RepeatedFieldProxyBase<T>,
155 RepeatedFieldProxyMutableBase<T>> {
156 static_assert(std::is_same_v<T, absl::string_view> ||
157 std::is_same_v<T, const absl::string_view>,
158 "");
159 static constexpr bool kIsConst = std::is_const_v<T>;
160
161 public:
162 using value_type = std::remove_const_t<T>;
163 using size_type = size_t;
164 using difference_type = ptrdiff_t;
165 using iterator = internal::Iterator<StringIteratorPolicy<T>>;
166 using reference = typename iterator::reference;
167 using pointer = typename iterator::pointer;
168 using reverse_iterator = std::reverse_iterator<iterator>;
169
170 // Immutable constructor.
171 explicit RepeatedFieldStringProxy(const upb_Array* arr, upb_Arena* arena)
172 : RepeatedFieldProxyBase<T>(arr, arena) {}
173 // Mutable constructor.
174 RepeatedFieldStringProxy(upb_Array* arr, upb_Arena* arena)
175 : RepeatedFieldProxyMutableBase<T>(arr, arena) {}
176 // Constructor used by ::protos::Ptr.
177 RepeatedFieldStringProxy(const RepeatedFieldStringProxy&) = default;
178
179 reference operator[](size_t n) const { return begin()[n]; }
180
181 template <int&... DeductionBlocker, bool b = !kIsConst,
182 typename = std::enable_if_t<b>>
183 void push_back(T t) {
184 upb_MessageValue message_value;
185 // Copy string to arena.
Eric Salo7c5e18b2023-11-27 11:14:23 -0800186 assert(this->arena_);
Adam Cozzette501ecec2023-09-26 14:36:20 -0700187 char* data = (char*)upb_Arena_Malloc(this->arena_, t.size());
Eric Salo7c5e18b2023-11-27 11:14:23 -0800188 assert(data);
Adam Cozzette501ecec2023-09-26 14:36:20 -0700189 memcpy(data, t.data(), t.size());
190 message_value.str_val = upb_StringView_FromDataAndSize(data, t.size());
191 upb_Array_Append(this->arr_, message_value, this->arena_);
192 }
193
194 iterator begin() const { return iterator({this->arr_, this->arena_, 0}); }
195 iterator end() const {
196 return iterator({this->arr_, this->arena_, this->size()});
197 }
198 reverse_iterator rbegin() const { return reverse_iterator(end()); }
199 reverse_iterator rend() const { return reverse_iterator(begin()); }
200};
201
202// RepeatedField proxy for repeated scalar types.
203template <typename T>
204class RepeatedFieldScalarProxy
205 : public std::conditional_t<std::is_const_v<T>, RepeatedFieldProxyBase<T>,
206 RepeatedFieldProxyMutableBase<T>> {
207 static_assert(std::is_arithmetic_v<T>, "");
208 static constexpr bool kIsConst = std::is_const_v<T>;
209
210 public:
211 using value_type = std::remove_const_t<T>;
212 using size_type = size_t;
213 using difference_type = ptrdiff_t;
214 using iterator = internal::Iterator<ScalarIteratorPolicy<T>>;
215 using reference = typename iterator::reference;
216 using pointer = typename iterator::pointer;
217 using reverse_iterator = std::reverse_iterator<iterator>;
218
219 explicit RepeatedFieldScalarProxy(const upb_Array* arr, upb_Arena* arena)
220 : RepeatedFieldProxyBase<T>(arr, arena) {}
221 RepeatedFieldScalarProxy(upb_Array* arr, upb_Arena* arena)
222 : RepeatedFieldProxyMutableBase<T>(arr, arena) {}
223 // Constructor used by ::protos::Ptr.
224 RepeatedFieldScalarProxy(const RepeatedFieldScalarProxy&) = default;
225
226 T operator[](size_t n) const {
227 upb_MessageValue message_value = upb_Array_Get(this->arr_, n);
228 typename std::remove_const_t<T> val;
229 memcpy(&val, &message_value, sizeof(T));
230 return val;
231 }
232
233 template <int&... DeductionBlocker, bool b = !kIsConst,
234 typename = std::enable_if_t<b>>
235 void push_back(T t) {
236 upb_MessageValue message_value;
237 memcpy(&message_value, &t, sizeof(T));
238 upb_Array_Append(this->arr_, message_value, this->arena_);
239 }
240
241 iterator begin() const { return iterator({unsafe_array()}); }
242 iterator cbegin() const { return begin(); }
243 iterator end() const { return iterator({unsafe_array() + this->size()}); }
244 iterator cend() const { return end(); }
245
246 // Reverse iterator support.
247 reverse_iterator rbegin() const { return reverse_iterator(end()); }
248 reverse_iterator rend() const { return reverse_iterator(begin()); }
249 reverse_iterator crbegin() const { return reverse_iterator(end()); }
250 reverse_iterator crend() const { return reverse_iterator(begin()); }
251
252 private:
253 T* unsafe_array() const {
254 if (kIsConst) {
255 const void* unsafe_ptr = ::upb_Array_DataPtr(this->arr_);
256 return static_cast<T*>(const_cast<void*>(unsafe_ptr));
257 }
258 if (!kIsConst) {
259 void* unsafe_ptr =
260 ::upb_Array_MutableDataPtr(const_cast<upb_Array*>(this->arr_));
261 return static_cast<T*>(unsafe_ptr);
262 }
263 }
264};
265
266} // namespace internal
267
268template <typename T>
269class RepeatedField {
270 static constexpr bool kIsString = std::is_same_v<T, absl::string_view>;
271 static constexpr bool kIsScalar = std::is_arithmetic_v<T>;
272
273 public:
274 using Proxy = std::conditional_t<
275 kIsScalar, internal::RepeatedFieldScalarProxy<T>,
276 std::conditional_t<kIsString, internal::RepeatedFieldStringProxy<T>,
277 internal::RepeatedFieldProxy<T>>>;
278 using CProxy = std::conditional_t<
279 kIsScalar, internal::RepeatedFieldScalarProxy<const T>,
280 std::conditional_t<kIsString, internal::RepeatedFieldStringProxy<const T>,
281 internal::RepeatedFieldProxy<const T>>>;
282 // TODO: T supports incomplete type from fwd.h forwarding headers
283 // We would like to reference T::CProxy. Validate forwarding header design.
284 using ValueProxy = std::conditional_t<
285 kIsScalar, T,
286 std::conditional_t<kIsString, absl::string_view, ::protos::Ptr<T>>>;
287 using ValueCProxy = std::conditional_t<
288 kIsScalar, const T,
289 std::conditional_t<kIsString, absl::string_view, ::protos::Ptr<const T>>>;
290 using Access = std::conditional_t<
291 kIsScalar, internal::RepeatedFieldScalarProxy<T>,
292 std::conditional_t<kIsString, internal::RepeatedFieldStringProxy<T>,
293 internal::RepeatedFieldProxy<T>>>;
294};
295
296} // namespace protos
297
Adam Cozzette501ecec2023-09-26 14:36:20 -0700298#endif // UPB_PROTOS_REPEATED_FIELD_H_