• 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_ITERATOR_H_
9 #define PROTOBUF_HPB_REPEATED_FIELD_ITERATOR_H_
10 
11 #include <cstddef>
12 #include <cstring>
13 #include <iterator>
14 #include <type_traits>
15 
16 #include "absl/strings/string_view.h"
17 #include "google/protobuf/hpb/backend/upb/interop.h"
18 #include "google/protobuf/hpb/hpb.h"
19 #include "upb/base/string_view.h"
20 #include "upb/mem/arena.h"
21 #include "upb/message/array.h"
22 #include "upb/message/message.h"
23 
24 namespace hpb {
25 namespace internal {
26 
27 // TODO: Implement std iterator for messages
28 template <typename T>
29 class RepeatedFieldScalarProxy;
30 template <typename T>
31 class RepeatedFieldStringProxy;
32 
33 struct IteratorTestPeer;
34 
35 template <typename T>
36 class Iterator;
37 
38 template <typename PolicyT>
39 class ReferenceProxy;
40 
41 template <typename PolicyT>
42 class InjectedRelationalsImpl {
43   using RP = ReferenceProxy<PolicyT>;
44   using V = typename PolicyT::value_type;
45   friend bool operator==(RP a, V b) { return static_cast<V>(a) == b; }
46   friend bool operator==(V a, RP b) { return a == static_cast<V>(b); }
47   friend bool operator==(RP a, RP b) {
48     return static_cast<V>(a) == static_cast<V>(b);
49   }
50   friend bool operator!=(RP a, V b) { return static_cast<V>(a) != b; }
51   friend bool operator!=(V a, RP b) { return a != static_cast<V>(b); }
52   friend bool operator!=(RP a, RP b) {
53     return static_cast<V>(a) != static_cast<V>(b);
54   }
55   friend bool operator<(RP a, V b) { return static_cast<V>(a) < b; }
56   friend bool operator<(V a, RP b) { return a < static_cast<V>(b); }
57   friend bool operator<(RP a, RP b) {
58     return static_cast<V>(a) < static_cast<V>(b);
59   }
60   friend bool operator<=(RP a, V b) { return static_cast<V>(a) <= b; }
61   friend bool operator<=(V a, RP b) { return a <= static_cast<V>(b); }
62   friend bool operator<=(RP a, RP b) {
63     return static_cast<V>(a) <= static_cast<V>(b);
64   }
65   friend bool operator>(RP a, V b) { return static_cast<V>(a) > b; }
66   friend bool operator>(V a, RP b) { return a > static_cast<V>(b); }
67   friend bool operator>(RP a, RP b) {
68     return static_cast<V>(a) > static_cast<V>(b);
69   }
70   friend bool operator>=(RP a, V b) { return static_cast<V>(a) >= b; }
71   friend bool operator>=(V a, RP b) { return a >= static_cast<V>(b); }
72   friend bool operator>=(RP a, RP b) {
73     return static_cast<V>(a) >= static_cast<V>(b);
74   }
75 };
76 class NoInjectedRelationalsImpl {};
77 
78 // We need to inject relationals for the string references because the
79 // relationals for string_view are templates and won't allow for implicit
80 // conversions from ReferenceProxy to string_view before deduction.
81 template <typename PolicyT>
82 using InjectedRelationals = std::conditional_t<
83     std::is_same_v<std::remove_const_t<typename PolicyT::value_type>,
84                    absl::string_view>,
85     InjectedRelationalsImpl<PolicyT>, NoInjectedRelationalsImpl>;
86 
87 template <typename PolicyT>
88 class ReferenceProxy : InjectedRelationals<PolicyT> {
89   using value_type = typename PolicyT::value_type;
90 
91  public:
92   ReferenceProxy(const ReferenceProxy&) = default;
93   ReferenceProxy& operator=(const ReferenceProxy& other) {
94     // Assign through the references
95     // TODO: Make this better for strings to avoid the copy.
96     it_.Set(other.it_.Get());
97     return *this;
98   }
swap(ReferenceProxy a,ReferenceProxy b)99   friend void swap(ReferenceProxy a, ReferenceProxy b) { a.it_.swap(b.it_); }
100 
value_type()101   operator value_type() const { return it_.Get(); }
102   void operator=(const value_type& value) const { it_.Set(value); }
103   void operator=(value_type&& value) const { it_.Set(std::move(value)); }
104   Iterator<PolicyT> operator&() const { return Iterator<PolicyT>(it_); }
105 
106  private:
107   friend IteratorTestPeer;
108   friend ReferenceProxy<typename PolicyT::AddConst>;
109   friend Iterator<PolicyT>;
110 
ReferenceProxy(typename PolicyT::Payload elem)111   explicit ReferenceProxy(typename PolicyT::Payload elem) : it_(elem) {}
112   typename PolicyT::Payload it_;
113 };
114 
115 template <template <typename> class PolicyTemplate, typename T>
116 class ReferenceProxy<PolicyTemplate<const T>>
117     : InjectedRelationals<PolicyTemplate<const T>> {
118   using PolicyT = PolicyTemplate<const T>;
119   using value_type = typename PolicyT::value_type;
120 
121  public:
ReferenceProxy(ReferenceProxy<PolicyTemplate<T>> p)122   ReferenceProxy(ReferenceProxy<PolicyTemplate<T>> p) : it_(p.it_) {}
123   ReferenceProxy(const ReferenceProxy&) = default;
124   ReferenceProxy& operator=(const ReferenceProxy&) = delete;
125 
value_type()126   operator value_type() const { return it_.Get(); }
127   Iterator<PolicyT> operator&() const { return Iterator<PolicyT>(it_); }
128 
129  private:
130   friend IteratorTestPeer;
131   friend Iterator<PolicyT>;
132 
ReferenceProxy(typename PolicyT::Payload elem)133   explicit ReferenceProxy(typename PolicyT::Payload elem) : it_(elem) {}
134   typename PolicyT::Payload it_;
135 };
136 
137 template <typename PolicyT>
138 class Iterator {
139  public:
140   using iterator_category = std::random_access_iterator_tag;
141   using value_type = std::remove_const_t<typename PolicyT::value_type>;
142   using difference_type = std::ptrdiff_t;
143   using pointer = Iterator;
144   using reference =
145       std::conditional_t<PolicyT::kUseReferenceProxy, ReferenceProxy<PolicyT>,
146                          typename PolicyT::value_type>;
147 
Iterator()148   constexpr Iterator() noexcept : it_(nullptr) {}
149   Iterator(const Iterator& other) = default;
150   Iterator& operator=(const Iterator& other) = default;
151   template <
152       typename P = PolicyT,
153       typename = std::enable_if_t<std::is_const<typename P::value_type>::value>>
Iterator(const Iterator<typename P::RemoveConst> & other)154   Iterator(const Iterator<typename P::RemoveConst>& other) : it_(other.it_) {}
155 
156   constexpr reference operator*() const noexcept {
157     if constexpr (PolicyT::kUseReferenceProxy) {
158       return reference(it_);
159     } else {
160       return it_.Get();
161     }
162   }
163   // No operator-> needed because T is a scalar.
164 
165  private:
166   // Hide the internal type.
167   using iterator = Iterator;
168 
169  public:
170   // {inc,dec}rementable
171   constexpr iterator& operator++() noexcept {
172     it_.AddOffset(1);
173     return *this;
174   }
175   constexpr iterator operator++(int) noexcept {
176     auto copy = *this;
177     ++*this;
178     return copy;
179   }
180   constexpr iterator& operator--() noexcept {
181     it_.AddOffset(-1);
182     return *this;
183   }
184   constexpr iterator operator--(int) noexcept {
185     auto copy = *this;
186     --*this;
187     return copy;
188   }
189 
190   // equality_comparable
191   friend constexpr bool operator==(const iterator& x,
192                                    const iterator& y) noexcept {
193     return x.it_.Index() == y.it_.Index();
194   }
195   friend constexpr bool operator!=(const iterator& x,
196                                    const iterator& y) noexcept {
197     return !(x == y);
198   }
199 
200   // less_than_comparable
201   friend constexpr bool operator<(const iterator& x,
202                                   const iterator& y) noexcept {
203     return x.it_.Index() < y.it_.Index();
204   }
205   friend constexpr bool operator<=(const iterator& x,
206                                    const iterator& y) noexcept {
207     return !(y < x);
208   }
209   friend constexpr bool operator>(const iterator& x,
210                                   const iterator& y) noexcept {
211     return y < x;
212   }
213   friend constexpr bool operator>=(const iterator& x,
214                                    const iterator& y) noexcept {
215     return !(x < y);
216   }
217 
218   constexpr iterator& operator+=(difference_type d) noexcept {
219     it_.AddOffset(d);
220     return *this;
221   }
222   constexpr iterator operator+(difference_type d) const noexcept {
223     auto copy = *this;
224     copy += d;
225     return copy;
226   }
227   friend constexpr iterator operator+(const difference_type d,
228                                       iterator it) noexcept {
229     return it + d;
230   }
231 
232   constexpr iterator& operator-=(difference_type d) noexcept {
233     it_.AddOffset(-d);
234     return *this;
235   }
236   constexpr iterator operator-(difference_type d) const noexcept {
237     auto copy = *this;
238     copy -= d;
239     return copy;
240   }
241 
242   // indexable
243   constexpr reference operator[](difference_type d) const noexcept {
244     auto copy = *this;
245     copy += d;
246     return *copy;
247   }
248 
249   // random access iterator
250   friend constexpr difference_type operator-(iterator x, iterator y) noexcept {
251     return x.it_.Index() - y.it_.Index();
252   }
253 
254  private:
255   friend IteratorTestPeer;
256   friend ReferenceProxy<PolicyT>;
257   friend Iterator<typename PolicyT::AddConst>;
258   template <typename U>
259   friend class RepeatedFieldScalarProxy;
260   template <typename U>
261   friend class RepeatedFieldStringProxy;
262   template <typename U>
263   friend class RepeatedFieldProxy;
264 
265   // Create from internal::RepeatedFieldScalarProxy.
Iterator(typename PolicyT::Payload it)266   explicit Iterator(typename PolicyT::Payload it) noexcept : it_(it) {}
267 
268   // The internal iterator.
269   typename PolicyT::Payload it_;
270 };
271 
272 template <typename T>
273 struct ScalarIteratorPolicy {
274   static constexpr bool kUseReferenceProxy = true;
275   using value_type = T;
276   using RemoveConst = ScalarIteratorPolicy<std::remove_const_t<T>>;
277   using AddConst = ScalarIteratorPolicy<const T>;
278 
279   struct Payload {
280     T* value;
AddOffsetScalarIteratorPolicy::Payload281     void AddOffset(ptrdiff_t offset) { value += offset; }
GetScalarIteratorPolicy::Payload282     T Get() const { return *value; }
SetScalarIteratorPolicy::Payload283     void Set(T new_value) const { *value = new_value; }
IndexScalarIteratorPolicy::Payload284     T* Index() const { return value; }
285 
swapScalarIteratorPolicy::Payload286     void swap(Payload& other) {
287       using std::swap;
288       swap(*value, *other.value);
289     }
290 
PayloadScalarIteratorPolicy::Payload291     operator typename ScalarIteratorPolicy<const T>::Payload() const {
292       return {value};
293     }
294   };
295 };
296 
297 template <typename T>
298 struct StringIteratorPolicy {
299   static constexpr bool kUseReferenceProxy = true;
300   using value_type = T;
301   using RemoveConst = StringIteratorPolicy<std::remove_const_t<T>>;
302   using AddConst = StringIteratorPolicy<const T>;
303 
304   struct Payload {
305     using Array =
306         std::conditional_t<std::is_const_v<T>, const upb_Array, upb_Array>;
307     Array* arr;
308     upb_Arena* arena;
309     size_t index;
310 
AddOffsetStringIteratorPolicy::Payload311     void AddOffset(ptrdiff_t offset) { index += offset; }
GetStringIteratorPolicy::Payload312     absl::string_view Get() const {
313       upb_MessageValue message_value = upb_Array_Get(arr, index);
314       return absl::string_view(message_value.str_val.data,
315                                message_value.str_val.size);
316     }
SetStringIteratorPolicy::Payload317     void Set(absl::string_view new_value) const {
318       char* data =
319           static_cast<char*>(upb_Arena_Malloc(arena, new_value.size()));
320       memcpy(data, new_value.data(), new_value.size());
321       upb_MessageValue message_value;
322       message_value.str_val =
323           upb_StringView_FromDataAndSize(data, new_value.size());
324       upb_Array_Set(arr, index, message_value);
325     }
IndexStringIteratorPolicy::Payload326     size_t Index() const { return index; }
327 
swapStringIteratorPolicy::Payload328     void swap(Payload& other) {
329       upb_MessageValue a = upb_Array_Get(this->arr, this->index);
330       upb_MessageValue b = upb_Array_Get(other.arr, other.index);
331       upb_Array_Set(this->arr, this->index, b);
332       upb_Array_Set(other.arr, other.index, a);
333     }
334 
PayloadStringIteratorPolicy::Payload335     operator typename StringIteratorPolicy<const T>::Payload() const {
336       return {arr, arena, index};
337     }
338   };
339 };
340 
341 template <typename T>
342 struct MessageIteratorPolicy {
343   static constexpr bool kUseReferenceProxy = false;
344   using value_type = std::conditional_t<std::is_const_v<T>, typename T::CProxy,
345                                         typename T::Proxy>;
346   using RemoveConst = MessageIteratorPolicy<std::remove_const_t<T>>;
347   using AddConst = MessageIteratorPolicy<const T>;
348 
349   struct Payload {
350     using Array =
351         std::conditional_t<std::is_const_v<T>, const upb_Array, upb_Array>;
352     upb_Message** arr;
353     upb_Arena* arena;
354 
AddOffsetMessageIteratorPolicy::Payload355     void AddOffset(ptrdiff_t offset) { arr += offset; }
GetMessageIteratorPolicy::Payload356     auto Get() const {
357       if constexpr (std::is_const_v<T>) {
358         return ::hpb::interop::upb::MakeCHandle<
359             typename std::remove_const_t<T>>(*arr, arena);
360       } else {
361         return hpb::interop::upb::MakeHandle<T>(*arr, arena);
362       }
363     }
IndexMessageIteratorPolicy::Payload364     auto Index() const { return arr; }
365   };
366 };
367 
368 }  // namespace internal
369 }  // namespace hpb
370 
371 #endif  // PROTOBUF_HPB_REPEATED_FIELD_ITERATOR_H_
372