• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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