// 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_H_ #define PROTOBUF_HPB_REPEATED_FIELD_H_ #include #include #include #include #include "absl/strings/string_view.h" #include "google/protobuf/hpb/hpb.h" #include "google/protobuf/hpb/repeated_field_iterator.h" #include "google/protobuf/hpb/traits.h" #include "upb/base/string_view.h" #include "upb/mem/arena.h" #include "upb/message/array.h" #include "upb/message/copy.h" #include "upb/message/message.h" namespace protos { namespace internal { // Shared implementation of repeated fields for absl::string_view and // message types for mutable and immutable variants. // // Immutable (const accessor), constructs this class with a nullptr upb_Array* // when the underlying array in the message is empty. // // Mutable accessors on the other hand, will allocate a new empty non-null // upb_Array* for the message when the RepeatedFieldProxy is constructed. template class RepeatedFieldProxyBase { using Array = hpb::internal::add_const_if_T_is_const; public: explicit RepeatedFieldProxyBase(Array* arr, upb_Arena* arena) : arr_(arr), arena_(arena) {} size_t size() const { return arr_ != nullptr ? upb_Array_Size(arr_) : 0; } bool empty() const { return size() == 0; } protected: // Returns upb_Array message member. inline upb_Message* GetMessage(size_t n) const; Array* arr_; upb_Arena* arena_; }; template upb_Message* RepeatedFieldProxyBase::GetMessage(size_t n) const { auto** messages = static_cast(upb_Array_MutableDataPtr(this->arr_)); return messages[n]; } template class RepeatedFieldProxyMutableBase : public RepeatedFieldProxyBase { public: RepeatedFieldProxyMutableBase(upb_Array* arr, upb_Arena* arena) : RepeatedFieldProxyBase(arr, arena) {} void clear() { upb_Array_Resize(this->arr_, 0, this->arena_); } }; // RepeatedField proxy for repeated messages. template class RepeatedFieldProxy : public std::conditional_t, RepeatedFieldProxyBase, RepeatedFieldProxyMutableBase> { static_assert(!std::is_same_v, ""); static_assert(!std::is_same_v, ""); static_assert(!std::is_arithmetic_v, ""); static constexpr bool kIsConst = std::is_const_v; public: using value_type = std::remove_const_t; using size_type = size_t; using difference_type = ptrdiff_t; using iterator = internal::Iterator>; using reference = typename iterator::reference; using pointer = typename iterator::pointer; using reverse_iterator = std::reverse_iterator; explicit RepeatedFieldProxy(const upb_Array* arr, upb_Arena* arena) : RepeatedFieldProxyBase(arr, arena) {} RepeatedFieldProxy(upb_Array* arr, upb_Arena* arena) : RepeatedFieldProxyMutableBase(arr, arena) {} // Constructor used by ::hpb::Ptr. RepeatedFieldProxy(const RepeatedFieldProxy&) = default; // T::CProxy [] operator specialization. typename T::CProxy operator[](size_t n) const { upb_MessageValue message_value = upb_Array_Get(this->arr_, n); return ::protos::internal::CreateMessage>( (upb_Message*)message_value.msg_val, this->arena_); } // TODO : Audit/Finalize based on Iterator Design. // T::Proxy [] operator specialization. template > typename T::Proxy operator[](size_t n) { return ::protos::internal::CreateMessageProxy(this->GetMessage(n), this->arena_); } // Mutable message reference specialization. template > void push_back(const T& t) { upb_MessageValue message_value; message_value.msg_val = upb_Message_DeepClone( PrivateAccess::GetInternalMsg(&t), ::protos::internal::GetMiniTable(&t), this->arena_); upb_Array_Append(this->arr_, message_value, this->arena_); } // Mutable message add using move. template > void push_back(T&& msg) { upb_MessageValue message_value; message_value.msg_val = PrivateAccess::GetInternalMsg(&msg); upb_Arena_Fuse(GetArena(&msg), this->arena_); upb_Array_Append(this->arr_, message_value, this->arena_); T moved_msg = std::move(msg); } iterator begin() const { return iterator( {static_cast( this->arr_ ? const_cast(upb_Array_DataPtr(this->arr_)) : nullptr), this->arena_}); } iterator end() const { return begin() + this->size(); } reverse_iterator rbegin() const { return reverse_iterator(end()); } reverse_iterator rend() const { return reverse_iterator(begin()); } private: friend class ::hpb::Ptr; }; // RepeatedField proxy for repeated strings. template class RepeatedFieldStringProxy : public std::conditional_t, RepeatedFieldProxyBase, RepeatedFieldProxyMutableBase> { static_assert(std::is_same_v || std::is_same_v, ""); static constexpr bool kIsConst = std::is_const_v; public: using value_type = std::remove_const_t; using size_type = size_t; using difference_type = ptrdiff_t; using iterator = internal::Iterator>; using reference = typename iterator::reference; using pointer = typename iterator::pointer; using reverse_iterator = std::reverse_iterator; // Immutable constructor. explicit RepeatedFieldStringProxy(const upb_Array* arr, upb_Arena* arena) : RepeatedFieldProxyBase(arr, arena) {} // Mutable constructor. RepeatedFieldStringProxy(upb_Array* arr, upb_Arena* arena) : RepeatedFieldProxyMutableBase(arr, arena) {} // Constructor used by ::hpb::Ptr. RepeatedFieldStringProxy(const RepeatedFieldStringProxy&) = default; reference operator[](size_t n) const { return begin()[n]; } template > void push_back(T t) { upb_MessageValue message_value; // Copy string to arena. assert(this->arena_); char* data = (char*)upb_Arena_Malloc(this->arena_, t.size()); assert(data); memcpy(data, t.data(), t.size()); message_value.str_val = upb_StringView_FromDataAndSize(data, t.size()); upb_Array_Append(this->arr_, message_value, this->arena_); } iterator begin() const { return iterator({this->arr_, this->arena_, 0}); } iterator end() const { return iterator({this->arr_, this->arena_, this->size()}); } reverse_iterator rbegin() const { return reverse_iterator(end()); } reverse_iterator rend() const { return reverse_iterator(begin()); } }; // RepeatedField proxy for repeated scalar types. template class RepeatedFieldScalarProxy : public std::conditional_t, RepeatedFieldProxyBase, RepeatedFieldProxyMutableBase> { static_assert(std::is_arithmetic_v, ""); static constexpr bool kIsConst = std::is_const_v; public: using value_type = std::remove_const_t; using size_type = size_t; using difference_type = ptrdiff_t; using iterator = internal::Iterator>; using reference = typename iterator::reference; using pointer = typename iterator::pointer; using reverse_iterator = std::reverse_iterator; explicit RepeatedFieldScalarProxy(const upb_Array* arr, upb_Arena* arena) : RepeatedFieldProxyBase(arr, arena) {} RepeatedFieldScalarProxy(upb_Array* arr, upb_Arena* arena) : RepeatedFieldProxyMutableBase(arr, arena) {} // Constructor used by ::hpb::Ptr. RepeatedFieldScalarProxy(const RepeatedFieldScalarProxy&) = default; T operator[](size_t n) const { upb_MessageValue message_value = upb_Array_Get(this->arr_, n); typename std::remove_const_t val; memcpy(&val, &message_value, sizeof(T)); return val; } template > void push_back(T t) { upb_MessageValue message_value; memcpy(&message_value, &t, sizeof(T)); upb_Array_Append(this->arr_, message_value, this->arena_); } iterator begin() const { return iterator({unsafe_array()}); } iterator cbegin() const { return begin(); } iterator end() const { return iterator({unsafe_array() + this->size()}); } iterator cend() const { return end(); } // Reverse iterator support. reverse_iterator rbegin() const { return reverse_iterator(end()); } reverse_iterator rend() const { return reverse_iterator(begin()); } reverse_iterator crbegin() const { return reverse_iterator(end()); } reverse_iterator crend() const { return reverse_iterator(begin()); } private: T* unsafe_array() const { if (kIsConst) { const void* unsafe_ptr = ::upb_Array_DataPtr(this->arr_); return static_cast(const_cast(unsafe_ptr)); } if (!kIsConst) { void* unsafe_ptr = ::upb_Array_MutableDataPtr(const_cast(this->arr_)); return static_cast(unsafe_ptr); } } }; } // namespace internal template class RepeatedField { static constexpr bool kIsString = std::is_same_v; static constexpr bool kIsScalar = std::is_arithmetic_v; public: using Proxy = std::conditional_t< kIsScalar, internal::RepeatedFieldScalarProxy, std::conditional_t, internal::RepeatedFieldProxy>>; using CProxy = std::conditional_t< kIsScalar, internal::RepeatedFieldScalarProxy, std::conditional_t, internal::RepeatedFieldProxy>>; // TODO: T supports incomplete type from fwd.h forwarding headers // We would like to reference T::CProxy. Validate forwarding header design. using ValueProxy = std::conditional_t< kIsScalar, T, std::conditional_t>>; using ValueCProxy = std::conditional_t< kIsScalar, const T, std::conditional_t>>; using Access = std::conditional_t< kIsScalar, internal::RepeatedFieldScalarProxy, std::conditional_t, internal::RepeatedFieldProxy>>; }; } // namespace protos #endif // PROTOBUF_HPB_REPEATED_FIELD_H_