1 // Protocol Buffers - Google's data interchange format
2 // Copyright 2023 Google LLC. All rights reserved.
3 //
4 // 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
7
8 #ifndef PROTOBUF_HPB_REPEATED_FIELD_H_
9 #define PROTOBUF_HPB_REPEATED_FIELD_H_
10
11 #include <assert.h>
12
13 #include <cstddef>
14 #include <iterator>
15 #include <type_traits>
16
17 #include "absl/strings/string_view.h"
18 #include "google/protobuf/hpb/backend/upb/interop.h"
19 #include "google/protobuf/hpb/internal/template_help.h"
20 #include "google/protobuf/hpb/repeated_field_iterator.h"
21 #include "upb/base/string_view.h"
22 #include "upb/mem/arena.h"
23 #include "upb/message/array.h"
24 #include "upb/message/copy.h"
25 #include "upb/message/message.h"
26
27 namespace hpb {
28 namespace 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.
38 template <class T>
39 class RepeatedFieldProxyBase {
40 using Array = hpb::internal::add_const_if_T_is_const<T, upb_Array>;
41
42 public:
RepeatedFieldProxyBase(Array * arr,upb_Arena * arena)43 explicit RepeatedFieldProxyBase(Array* arr, upb_Arena* arena)
44 : arr_(arr), arena_(arena) {}
45
size()46 size_t size() const { return arr_ != nullptr ? upb_Array_Size(arr_) : 0; }
47
empty()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
58 template <class T>
GetMessage(size_t n)59 upb_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
65 template <class T>
66 class RepeatedFieldProxyMutableBase : public RepeatedFieldProxyBase<T> {
67 public:
RepeatedFieldProxyMutableBase(upb_Array * arr,upb_Arena * arena)68 RepeatedFieldProxyMutableBase(upb_Array* arr, upb_Arena* arena)
69 : RepeatedFieldProxyBase<T>(arr, arena) {}
70
clear()71 void clear() { upb_Array_Resize(this->arr_, 0, this->arena_); }
72 };
73
74 // RepeatedField proxy for repeated messages.
75 template <class T>
76 class 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
RepeatedFieldProxy(const upb_Array * arr,upb_Arena * arena)93 explicit RepeatedFieldProxy(const upb_Array* arr, upb_Arena* arena)
94 : RepeatedFieldProxyBase<T>(arr, arena) {}
RepeatedFieldProxy(upb_Array * arr,upb_Arena * arena)95 RepeatedFieldProxy(upb_Array* arr, upb_Arena* arena)
96 : RepeatedFieldProxyMutableBase<T>(arr, arena) {}
97 // Constructor used by ::hpb::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 ::hpb::interop::upb::MakeCHandle<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 hpb::interop::upb::MakeHandle<T>(this->GetMessage(n), this->arena_);
113 }
114
115 // Mutable message reference specialization.
116 template <int&... DeductionBlocker, bool b = !kIsConst,
117 typename = std::enable_if_t<b>>
push_back(const T & t)118 void push_back(const T& t) {
119 upb_MessageValue message_value;
120 message_value.msg_val = upb_Message_DeepClone(
121 ::hpb::internal::PrivateAccess::GetInternalMsg(&t),
122 ::hpb::interop::upb::GetMiniTable(&t), this->arena_);
123 upb_Array_Append(this->arr_, message_value, this->arena_);
124 }
125
126 // Mutable message add using move.
127 template <int&... DeductionBlocker, bool b = !kIsConst,
128 typename = std::enable_if_t<b>>
push_back(T && msg)129 void push_back(T&& msg) {
130 upb_MessageValue message_value;
131 message_value.msg_val =
132 ::hpb::internal::PrivateAccess::GetInternalMsg(&msg);
133 upb_Arena_Fuse(hpb::interop::upb::GetArena(&msg), this->arena_);
134 upb_Array_Append(this->arr_, message_value, this->arena_);
135 T moved_msg = std::move(msg);
136 }
137
begin()138 iterator begin() const {
139 return iterator(
140 {static_cast<upb_Message**>(
141 this->arr_ ? const_cast<void*>(upb_Array_DataPtr(this->arr_))
142 : nullptr),
143 this->arena_});
144 }
end()145 iterator end() const { return begin() + this->size(); }
rbegin()146 reverse_iterator rbegin() const { return reverse_iterator(end()); }
rend()147 reverse_iterator rend() const { return reverse_iterator(begin()); }
148
149 private:
150 friend class ::hpb::Ptr<T>;
151 };
152
153 // RepeatedField proxy for repeated strings.
154 template <class T>
155 class RepeatedFieldStringProxy
156 : public std::conditional_t<std::is_const_v<T>, RepeatedFieldProxyBase<T>,
157 RepeatedFieldProxyMutableBase<T>> {
158 static_assert(std::is_same_v<T, absl::string_view> ||
159 std::is_same_v<T, const absl::string_view>,
160 "");
161 static constexpr bool kIsConst = std::is_const_v<T>;
162
163 public:
164 using value_type = std::remove_const_t<T>;
165 using size_type = size_t;
166 using difference_type = ptrdiff_t;
167 using iterator = internal::Iterator<StringIteratorPolicy<T>>;
168 using reference = typename iterator::reference;
169 using pointer = typename iterator::pointer;
170 using reverse_iterator = std::reverse_iterator<iterator>;
171
172 // Immutable constructor.
RepeatedFieldStringProxy(const upb_Array * arr,upb_Arena * arena)173 explicit RepeatedFieldStringProxy(const upb_Array* arr, upb_Arena* arena)
174 : RepeatedFieldProxyBase<T>(arr, arena) {}
175 // Mutable constructor.
RepeatedFieldStringProxy(upb_Array * arr,upb_Arena * arena)176 RepeatedFieldStringProxy(upb_Array* arr, upb_Arena* arena)
177 : RepeatedFieldProxyMutableBase<T>(arr, arena) {}
178 // Constructor used by ::hpb::Ptr.
179 RepeatedFieldStringProxy(const RepeatedFieldStringProxy&) = default;
180
181 reference operator[](size_t n) const { return begin()[n]; }
182
183 template <int&... DeductionBlocker, bool b = !kIsConst,
184 typename = std::enable_if_t<b>>
push_back(T t)185 void push_back(T t) {
186 upb_MessageValue message_value;
187 // Copy string to arena.
188 assert(this->arena_);
189 char* data = (char*)upb_Arena_Malloc(this->arena_, t.size());
190 assert(data);
191 memcpy(data, t.data(), t.size());
192 message_value.str_val = upb_StringView_FromDataAndSize(data, t.size());
193 upb_Array_Append(this->arr_, message_value, this->arena_);
194 }
195
begin()196 iterator begin() const { return iterator({this->arr_, this->arena_, 0}); }
end()197 iterator end() const {
198 return iterator({this->arr_, this->arena_, this->size()});
199 }
rbegin()200 reverse_iterator rbegin() const { return reverse_iterator(end()); }
rend()201 reverse_iterator rend() const { return reverse_iterator(begin()); }
202 };
203
204 // RepeatedField proxy for repeated scalar types.
205 template <typename T>
206 class RepeatedFieldScalarProxy
207 : public std::conditional_t<std::is_const_v<T>, RepeatedFieldProxyBase<T>,
208 RepeatedFieldProxyMutableBase<T>> {
209 static_assert(std::is_arithmetic_v<T>, "");
210 static constexpr bool kIsConst = std::is_const_v<T>;
211
212 public:
213 using value_type = std::remove_const_t<T>;
214 using size_type = size_t;
215 using difference_type = ptrdiff_t;
216 using iterator = internal::Iterator<ScalarIteratorPolicy<T>>;
217 using reference = typename iterator::reference;
218 using pointer = typename iterator::pointer;
219 using reverse_iterator = std::reverse_iterator<iterator>;
220
RepeatedFieldScalarProxy(const upb_Array * arr,upb_Arena * arena)221 explicit RepeatedFieldScalarProxy(const upb_Array* arr, upb_Arena* arena)
222 : RepeatedFieldProxyBase<T>(arr, arena) {}
RepeatedFieldScalarProxy(upb_Array * arr,upb_Arena * arena)223 RepeatedFieldScalarProxy(upb_Array* arr, upb_Arena* arena)
224 : RepeatedFieldProxyMutableBase<T>(arr, arena) {}
225 // Constructor used by ::hpb::Ptr.
226 RepeatedFieldScalarProxy(const RepeatedFieldScalarProxy&) = default;
227
228 T operator[](size_t n) const {
229 upb_MessageValue message_value = upb_Array_Get(this->arr_, n);
230 typename std::remove_const_t<T> val;
231 memcpy(&val, &message_value, sizeof(T));
232 return val;
233 }
234
235 template <int&... DeductionBlocker, bool b = !kIsConst,
236 typename = std::enable_if_t<b>>
push_back(T t)237 void push_back(T t) {
238 upb_MessageValue message_value;
239 memcpy(&message_value, &t, sizeof(T));
240 upb_Array_Append(this->arr_, message_value, this->arena_);
241 }
242
begin()243 iterator begin() const { return iterator({unsafe_array()}); }
cbegin()244 iterator cbegin() const { return begin(); }
end()245 iterator end() const { return iterator({unsafe_array() + this->size()}); }
cend()246 iterator cend() const { return end(); }
247
248 // Reverse iterator support.
rbegin()249 reverse_iterator rbegin() const { return reverse_iterator(end()); }
rend()250 reverse_iterator rend() const { return reverse_iterator(begin()); }
crbegin()251 reverse_iterator crbegin() const { return reverse_iterator(end()); }
crend()252 reverse_iterator crend() const { return reverse_iterator(begin()); }
253
254 private:
unsafe_array()255 T* unsafe_array() const {
256 if (kIsConst) {
257 const void* unsafe_ptr = ::upb_Array_DataPtr(this->arr_);
258 return static_cast<T*>(const_cast<void*>(unsafe_ptr));
259 }
260 if (!kIsConst) {
261 void* unsafe_ptr =
262 ::upb_Array_MutableDataPtr(const_cast<upb_Array*>(this->arr_));
263 return static_cast<T*>(unsafe_ptr);
264 }
265 }
266 };
267
268 } // namespace internal
269
270 template <typename T>
271 class RepeatedField {
272 static constexpr bool kIsString = std::is_same_v<T, absl::string_view>;
273 static constexpr bool kIsScalar = std::is_arithmetic_v<T>;
274
275 public:
276 using Proxy = std::conditional_t<
277 kIsScalar, internal::RepeatedFieldScalarProxy<T>,
278 std::conditional_t<kIsString, internal::RepeatedFieldStringProxy<T>,
279 internal::RepeatedFieldProxy<T>>>;
280 using CProxy = std::conditional_t<
281 kIsScalar, internal::RepeatedFieldScalarProxy<const T>,
282 std::conditional_t<kIsString, internal::RepeatedFieldStringProxy<const T>,
283 internal::RepeatedFieldProxy<const T>>>;
284 // TODO: T supports incomplete type from fwd.h forwarding headers
285 // We would like to reference T::CProxy. Validate forwarding header design.
286 using ValueProxy = std::conditional_t<
287 kIsScalar, T,
288 std::conditional_t<kIsString, absl::string_view, ::hpb::Ptr<T>>>;
289 using ValueCProxy = std::conditional_t<
290 kIsScalar, const T,
291 std::conditional_t<kIsString, absl::string_view, ::hpb::Ptr<const T>>>;
292 using Access = std::conditional_t<
293 kIsScalar, internal::RepeatedFieldScalarProxy<T>,
294 std::conditional_t<kIsString, internal::RepeatedFieldStringProxy<T>,
295 internal::RepeatedFieldProxy<T>>>;
296 };
297
298 } // namespace hpb
299
300 #endif // PROTOBUF_HPB_REPEATED_FIELD_H_
301