| // Protocol Buffers - Google's data interchange format |
| // Copyright 2023 Google LLC. All rights reserved. |
| // |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file or at |
| // https://developers.google.com/open-source/licenses/bsd |
| |
| #ifndef PROTOBUF_HPB_REPEATED_FIELD_ITERATOR_H_ |
| #define PROTOBUF_HPB_REPEATED_FIELD_ITERATOR_H_ |
| |
| #include <cstddef> |
| #include <cstring> |
| #include <iterator> |
| #include <type_traits> |
| |
| #include "absl/strings/string_view.h" |
| #include "google/protobuf/hpb/backend/upb/interop.h" |
| #include "google/protobuf/hpb/hpb.h" |
| #include "upb/base/string_view.h" |
| #include "upb/mem/arena.h" |
| #include "upb/message/array.h" |
| #include "upb/message/message.h" |
| |
| namespace hpb { |
| namespace internal { |
| |
| // TODO: Implement std iterator for messages |
| template <typename T> |
| class RepeatedFieldScalarProxy; |
| template <typename T> |
| class RepeatedFieldStringProxy; |
| |
| struct IteratorTestPeer; |
| |
| template <typename T> |
| class Iterator; |
| |
| template <typename PolicyT> |
| class ReferenceProxy; |
| |
| template <typename PolicyT> |
| class InjectedRelationalsImpl { |
| using RP = ReferenceProxy<PolicyT>; |
| using V = typename PolicyT::value_type; |
| friend bool operator==(RP a, V b) { return static_cast<V>(a) == b; } |
| friend bool operator==(V a, RP b) { return a == static_cast<V>(b); } |
| friend bool operator==(RP a, RP b) { |
| return static_cast<V>(a) == static_cast<V>(b); |
| } |
| friend bool operator!=(RP a, V b) { return static_cast<V>(a) != b; } |
| friend bool operator!=(V a, RP b) { return a != static_cast<V>(b); } |
| friend bool operator!=(RP a, RP b) { |
| return static_cast<V>(a) != static_cast<V>(b); |
| } |
| friend bool operator<(RP a, V b) { return static_cast<V>(a) < b; } |
| friend bool operator<(V a, RP b) { return a < static_cast<V>(b); } |
| friend bool operator<(RP a, RP b) { |
| return static_cast<V>(a) < static_cast<V>(b); |
| } |
| friend bool operator<=(RP a, V b) { return static_cast<V>(a) <= b; } |
| friend bool operator<=(V a, RP b) { return a <= static_cast<V>(b); } |
| friend bool operator<=(RP a, RP b) { |
| return static_cast<V>(a) <= static_cast<V>(b); |
| } |
| friend bool operator>(RP a, V b) { return static_cast<V>(a) > b; } |
| friend bool operator>(V a, RP b) { return a > static_cast<V>(b); } |
| friend bool operator>(RP a, RP b) { |
| return static_cast<V>(a) > static_cast<V>(b); |
| } |
| friend bool operator>=(RP a, V b) { return static_cast<V>(a) >= b; } |
| friend bool operator>=(V a, RP b) { return a >= static_cast<V>(b); } |
| friend bool operator>=(RP a, RP b) { |
| return static_cast<V>(a) >= static_cast<V>(b); |
| } |
| }; |
| class NoInjectedRelationalsImpl {}; |
| |
| // We need to inject relationals for the string references because the |
| // relationals for string_view are templates and won't allow for implicit |
| // conversions from ReferenceProxy to string_view before deduction. |
| template <typename PolicyT> |
| using InjectedRelationals = std::conditional_t< |
| std::is_same_v<std::remove_const_t<typename PolicyT::value_type>, |
| absl::string_view>, |
| InjectedRelationalsImpl<PolicyT>, NoInjectedRelationalsImpl>; |
| |
| template <typename PolicyT> |
| class ReferenceProxy : InjectedRelationals<PolicyT> { |
| using value_type = typename PolicyT::value_type; |
| |
| public: |
| ReferenceProxy(const ReferenceProxy&) = default; |
| ReferenceProxy& operator=(const ReferenceProxy& other) { |
| // Assign through the references |
| // TODO: Make this better for strings to avoid the copy. |
| it_.Set(other.it_.Get()); |
| return *this; |
| } |
| friend void swap(ReferenceProxy a, ReferenceProxy b) { a.it_.swap(b.it_); } |
| |
| operator value_type() const { return it_.Get(); } |
| void operator=(const value_type& value) const { it_.Set(value); } |
| void operator=(value_type&& value) const { it_.Set(std::move(value)); } |
| Iterator<PolicyT> operator&() const { return Iterator<PolicyT>(it_); } |
| |
| private: |
| friend IteratorTestPeer; |
| friend ReferenceProxy<typename PolicyT::AddConst>; |
| friend Iterator<PolicyT>; |
| |
| explicit ReferenceProxy(typename PolicyT::Payload elem) : it_(elem) {} |
| typename PolicyT::Payload it_; |
| }; |
| |
| template <template <typename> class PolicyTemplate, typename T> |
| class ReferenceProxy<PolicyTemplate<const T>> |
| : InjectedRelationals<PolicyTemplate<const T>> { |
| using PolicyT = PolicyTemplate<const T>; |
| using value_type = typename PolicyT::value_type; |
| |
| public: |
| ReferenceProxy(ReferenceProxy<PolicyTemplate<T>> p) : it_(p.it_) {} |
| ReferenceProxy(const ReferenceProxy&) = default; |
| ReferenceProxy& operator=(const ReferenceProxy&) = delete; |
| |
| operator value_type() const { return it_.Get(); } |
| Iterator<PolicyT> operator&() const { return Iterator<PolicyT>(it_); } |
| |
| private: |
| friend IteratorTestPeer; |
| friend Iterator<PolicyT>; |
| |
| explicit ReferenceProxy(typename PolicyT::Payload elem) : it_(elem) {} |
| typename PolicyT::Payload it_; |
| }; |
| |
| template <typename PolicyT> |
| class Iterator { |
| public: |
| using iterator_category = std::random_access_iterator_tag; |
| using value_type = std::remove_const_t<typename PolicyT::value_type>; |
| using difference_type = std::ptrdiff_t; |
| using pointer = Iterator; |
| using reference = |
| std::conditional_t<PolicyT::kUseReferenceProxy, ReferenceProxy<PolicyT>, |
| typename PolicyT::value_type>; |
| |
| constexpr Iterator() noexcept : it_(nullptr) {} |
| Iterator(const Iterator& other) = default; |
| Iterator& operator=(const Iterator& other) = default; |
| template < |
| typename P = PolicyT, |
| typename = std::enable_if_t<std::is_const<typename P::value_type>::value>> |
| Iterator(const Iterator<typename P::RemoveConst>& other) : it_(other.it_) {} |
| |
| constexpr reference operator*() const noexcept { |
| if constexpr (PolicyT::kUseReferenceProxy) { |
| return reference(it_); |
| } else { |
| return it_.Get(); |
| } |
| } |
| // No operator-> needed because T is a scalar. |
| |
| private: |
| // Hide the internal type. |
| using iterator = Iterator; |
| |
| public: |
| // {inc,dec}rementable |
| constexpr iterator& operator++() noexcept { |
| it_.AddOffset(1); |
| return *this; |
| } |
| constexpr iterator operator++(int) noexcept { |
| auto copy = *this; |
| ++*this; |
| return copy; |
| } |
| constexpr iterator& operator--() noexcept { |
| it_.AddOffset(-1); |
| return *this; |
| } |
| constexpr iterator operator--(int) noexcept { |
| auto copy = *this; |
| --*this; |
| return copy; |
| } |
| |
| // equality_comparable |
| friend constexpr bool operator==(const iterator& x, |
| const iterator& y) noexcept { |
| return x.it_.Index() == y.it_.Index(); |
| } |
| friend constexpr bool operator!=(const iterator& x, |
| const iterator& y) noexcept { |
| return !(x == y); |
| } |
| |
| // less_than_comparable |
| friend constexpr bool operator<(const iterator& x, |
| const iterator& y) noexcept { |
| return x.it_.Index() < y.it_.Index(); |
| } |
| friend constexpr bool operator<=(const iterator& x, |
| const iterator& y) noexcept { |
| return !(y < x); |
| } |
| friend constexpr bool operator>(const iterator& x, |
| const iterator& y) noexcept { |
| return y < x; |
| } |
| friend constexpr bool operator>=(const iterator& x, |
| const iterator& y) noexcept { |
| return !(x < y); |
| } |
| |
| constexpr iterator& operator+=(difference_type d) noexcept { |
| it_.AddOffset(d); |
| return *this; |
| } |
| constexpr iterator operator+(difference_type d) const noexcept { |
| auto copy = *this; |
| copy += d; |
| return copy; |
| } |
| friend constexpr iterator operator+(const difference_type d, |
| iterator it) noexcept { |
| return it + d; |
| } |
| |
| constexpr iterator& operator-=(difference_type d) noexcept { |
| it_.AddOffset(-d); |
| return *this; |
| } |
| constexpr iterator operator-(difference_type d) const noexcept { |
| auto copy = *this; |
| copy -= d; |
| return copy; |
| } |
| |
| // indexable |
| constexpr reference operator[](difference_type d) const noexcept { |
| auto copy = *this; |
| copy += d; |
| return *copy; |
| } |
| |
| // random access iterator |
| friend constexpr difference_type operator-(iterator x, iterator y) noexcept { |
| return x.it_.Index() - y.it_.Index(); |
| } |
| |
| private: |
| friend IteratorTestPeer; |
| friend ReferenceProxy<PolicyT>; |
| friend Iterator<typename PolicyT::AddConst>; |
| template <typename U> |
| friend class RepeatedFieldScalarProxy; |
| template <typename U> |
| friend class RepeatedFieldStringProxy; |
| template <typename U> |
| friend class RepeatedFieldProxy; |
| |
| // Create from internal::RepeatedFieldScalarProxy. |
| explicit Iterator(typename PolicyT::Payload it) noexcept : it_(it) {} |
| |
| // The internal iterator. |
| typename PolicyT::Payload it_; |
| }; |
| |
| template <typename T> |
| struct ScalarIteratorPolicy { |
| static constexpr bool kUseReferenceProxy = true; |
| using value_type = T; |
| using RemoveConst = ScalarIteratorPolicy<std::remove_const_t<T>>; |
| using AddConst = ScalarIteratorPolicy<const T>; |
| |
| struct Payload { |
| T* value; |
| void AddOffset(ptrdiff_t offset) { value += offset; } |
| T Get() const { return *value; } |
| void Set(T new_value) const { *value = new_value; } |
| T* Index() const { return value; } |
| |
| void swap(Payload& other) { |
| using std::swap; |
| swap(*value, *other.value); |
| } |
| |
| operator typename ScalarIteratorPolicy<const T>::Payload() const { |
| return {value}; |
| } |
| }; |
| }; |
| |
| template <typename T> |
| struct StringIteratorPolicy { |
| static constexpr bool kUseReferenceProxy = true; |
| using value_type = T; |
| using RemoveConst = StringIteratorPolicy<std::remove_const_t<T>>; |
| using AddConst = StringIteratorPolicy<const T>; |
| |
| struct Payload { |
| using Array = |
| std::conditional_t<std::is_const_v<T>, const upb_Array, upb_Array>; |
| Array* arr; |
| upb_Arena* arena; |
| size_t index; |
| |
| void AddOffset(ptrdiff_t offset) { index += offset; } |
| absl::string_view Get() const { |
| upb_MessageValue message_value = upb_Array_Get(arr, index); |
| return absl::string_view(message_value.str_val.data, |
| message_value.str_val.size); |
| } |
| void Set(absl::string_view new_value) const { |
| char* data = |
| static_cast<char*>(upb_Arena_Malloc(arena, new_value.size())); |
| memcpy(data, new_value.data(), new_value.size()); |
| upb_MessageValue message_value; |
| message_value.str_val = |
| upb_StringView_FromDataAndSize(data, new_value.size()); |
| upb_Array_Set(arr, index, message_value); |
| } |
| size_t Index() const { return index; } |
| |
| void swap(Payload& other) { |
| upb_MessageValue a = upb_Array_Get(this->arr, this->index); |
| upb_MessageValue b = upb_Array_Get(other.arr, other.index); |
| upb_Array_Set(this->arr, this->index, b); |
| upb_Array_Set(other.arr, other.index, a); |
| } |
| |
| operator typename StringIteratorPolicy<const T>::Payload() const { |
| return {arr, arena, index}; |
| } |
| }; |
| }; |
| |
| template <typename T> |
| struct MessageIteratorPolicy { |
| static constexpr bool kUseReferenceProxy = false; |
| using value_type = std::conditional_t<std::is_const_v<T>, typename T::CProxy, |
| typename T::Proxy>; |
| using RemoveConst = MessageIteratorPolicy<std::remove_const_t<T>>; |
| using AddConst = MessageIteratorPolicy<const T>; |
| |
| struct Payload { |
| using Array = |
| std::conditional_t<std::is_const_v<T>, const upb_Array, upb_Array>; |
| upb_Message** arr; |
| upb_Arena* arena; |
| |
| void AddOffset(ptrdiff_t offset) { arr += offset; } |
| auto Get() const { |
| if constexpr (std::is_const_v<T>) { |
| return ::hpb::interop::upb::MakeCHandle< |
| typename std::remove_const_t<T>>(*arr, arena); |
| } else { |
| return hpb::interop::upb::MakeHandle<T>(*arr, arena); |
| } |
| } |
| auto Index() const { return arr; } |
| }; |
| }; |
| |
| } // namespace internal |
| } // namespace hpb |
| |
| #endif // PROTOBUF_HPB_REPEATED_FIELD_ITERATOR_H_ |