// Protocol Buffers - Google's data interchange format // Copyright 2008 Google Inc. 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 // Author: kenton@google.com (Kenton Varda) // Based on original Protocol Buffers design by // Sanjay Ghemawat, Jeff Dean, and others. // // RepeatedField and RepeatedPtrField are used by generated protocol message // classes to manipulate repeated fields. These classes are very similar to // STL's vector, but include a number of optimizations found to be useful // specifically in the case of Protocol Buffers. RepeatedPtrField is // particularly different from STL vector as it manages ownership of the // pointers that it contains. // // This header covers RepeatedField. #ifndef GOOGLE_PROTOBUF_REPEATED_FIELD_H__ #define GOOGLE_PROTOBUF_REPEATED_FIELD_H__ #include #include #include #include #include #include #include #include #include #include #include #include "absl/base/attributes.h" #include "absl/base/dynamic_annotations.h" #include "absl/base/macros.h" #include "absl/base/no_destructor.h" #include "absl/base/optimization.h" #include "absl/log/absl_check.h" #include "absl/strings/cord.h" #include "google/protobuf/arena.h" #include "google/protobuf/arena_align.h" #include "google/protobuf/field_with_arena.h" #include "google/protobuf/generated_enum_util.h" #include "google/protobuf/internal_metadata_locator.h" #include "google/protobuf/internal_visibility.h" #include "google/protobuf/message_lite.h" #include "google/protobuf/port.h" #include "google/protobuf/repeated_ptr_field.h" #include "google/protobuf/serial_arena.h" // Must be included last. #include "google/protobuf/port_def.inc" #ifdef SWIG #error "You cannot SWIG proto headers" #endif namespace google { namespace protobuf { class Message; class DynamicMessage; class UnknownField; // For the allowlist class UnknownFieldSet; class DynamicMessage; class Reflection; namespace internal { template class MutableRepeatedFieldProxyImpl; class EpsCopyInputStream; class TcParser; class WireFormat; template constexpr int RepeatedFieldLowerClampLimit() { // The header is padded to be at least `sizeof(T)` when it would be smaller // otherwise. static_assert(sizeof(T) <= kHeapRepHeaderSize, ""); // We want to pad the minimum size to be a power of two bytes, including the // header. // The first allocation is kHeapRepHeaderSize bytes worth of elements for a // total of 2*kHeapRepHeaderSize bytes. For an 8-byte header, we allocate 8 // bool, 2 ints, or 1 int64. return kHeapRepHeaderSize / sizeof(T); } // kRepeatedFieldUpperClampLimit is the lowest signed integer value that // overflows when multiplied by 2 (which is undefined behavior). Sizes above // this will clamp to the maximum int value instead of following exponential // growth when growing a repeated field. #if defined(__cpp_inline_variables) inline constexpr int kRepeatedFieldUpperClampLimit = #else constexpr int kRepeatedFieldUpperClampLimit = #endif (std::numeric_limits::max() / 2) + 1; template class RepeatedIterator; // Decays `Element` to a value type (excluding `absl::Cord` in release builds // to avoid a nontrivial copy). This is useful when forwarding element values // to user-provided callbacks, which prevents leaking a reference to the // backing storage of the repeated field. template using DecayedRepeatedFieldElement = std::conditional_t && !internal::PerformDebugChecks(), const Element&, Element>; // Align to 8 as sanitizers are picky on the alignment of containers to start at // 8 byte offsets even when compiling for 32 bit platforms. class alignas(8) HeapRep { public: explicit HeapRep(uint32_t capacity) : capacity_(capacity) {} // Avoid 'implicitly deleted dtor' warnings on certain compilers. ~HeapRep() = delete; uint32_t capacity() const { return capacity_; } template const Element* elements() const { const char* elements_ptr = reinterpret_cast(this) + SizeOf(); return reinterpret_cast(elements_ptr); } template Element* elements() { char* elements_ptr = reinterpret_cast(this) + SizeOf(); return reinterpret_cast(elements_ptr); } // Returns the size of the HeapRep in bytes. Do not use `sizeof(HeapRep)`, // since that does not account for the padding we insert after the data // members. template static constexpr size_t SizeOf() { // We pad the header to be at least `sizeof(Element)` so that we have // power-of-two sized allocations, which enables Arena optimizations. return std::max(sizeof(HeapRep), sizeof(Element)); } private: uint32_t capacity_; // Unused padding which is always present before the data. By having this // field be explicitly initialized to 0, the compiler can sometimes merge // writes to adjacent memory locations into a single vector store instruction. [[maybe_unused]] const uint32_t unused_ = 0; }; // We use small object optimization (SOO) to store elements inline when possible // for small repeated fields. We do so in order to avoid memory indirections. // Note that SOO is disabled on 32-bit platforms due to alignment limitations. // SOO data is stored in the same space as the size/capacity ints. enum { kSooCapacityBytes = 2 * sizeof(int) }; inline constexpr uint32_t kNotSooBit = 0x1; // The number of bits at the start of the `InternalMetadataResolver` offset to // use for other purposes. Note that this must be <= log2(alignof(void*)). inline constexpr uint32_t kResolverTaggedBits = 1; inline constexpr size_t kSooSizeMask = sizeof(void*); // The number of elements that can be stored in the SOO rep. On 64-bit // platforms, this is 1 for int64_t, 2 for int32_t, 3 for bool, and 0 for // absl::Cord. We return 0 to disable SOO on 32-bit platforms. template constexpr int SooCapacityElements() { // RepeatedPtrField always has SOO capacity of 1. if constexpr (std::is_pointer_v) { return 1; } // Disable SOO for RepeatedFields on 32-bit platforms. if constexpr (sizeof(void*) < 8) return 0; return std::min(kSooCapacityBytes / sizeof(T), kSooSizeMask); } // The in-memory layout of a repeated field. This has three states: empty, SOO, // and heap-allocated. // // In every state, the first 8 bytes hold the internal metadata offset and size. // // In the SOO state, the last 8 bytes hold the data (1 int64_t, 2 int32_t, 8 // bools, etc.). // // In the heap-allocated state, the last 8 bytes hold a pointer to the // heap-allocated buffer of elements. class SooRep { public: constexpr SooRep() = default; explicit constexpr SooRep(InternalMetadataOffset offset) : resolver_(offset) {} bool is_soo() const { return (resolver_.Tag() & kNotSooBit) == 0; } Arena* arena() const { return ResolveTaggedArena<&SooRep::resolver_, kResolverTaggedBits>(this); } int size() const { int res = size_; PROTOBUF_ASSUME(res >= 0); return res; } void set_size(int size) { ABSL_DCHECK(!is_soo() || size <= kSooCapacityBytes); size_ = size; } int capacity() const { ABSL_DCHECK(!this->is_soo()); return heap_rep_->capacity(); } // Initializes the SooRep in non-SOO mode with the given heap allocation. void set_non_soo(HeapRep* heap_rep) { resolver_.SetTag(kNotSooBit); heap_rep_ = heap_rep; } HeapRep* heap_rep() const { ABSL_DCHECK(!is_soo()); return heap_rep_; } template const Element* elements(bool is_soo) const { ABSL_DCHECK_EQ(is_soo, this->is_soo()); if (is_soo) { return reinterpret_cast(soo_data_); } else { return heap_rep_->elements(); } } template Element* elements(bool is_soo) { ABSL_DCHECK_EQ(is_soo, this->is_soo()); if (is_soo) { return reinterpret_cast(soo_data_); } else { return heap_rep_->elements(); } } void swap(SooRep& other) { resolver_.SwapTags(other.resolver_); internal::memswap( reinterpret_cast(&this->size_), reinterpret_cast(&other.size_)); } private: TaggedInternalMetadataResolver resolver_; uint32_t size_ = 0; union { char soo_data_[kSooCapacityBytes]; HeapRep* heap_rep_; // NOTE: in some language versions, we can't have a constexpr constructor // if we don't initialize all fields, but we don't need to initialize this // field, so initialize an empty dummy variable instead. std::true_type dummy_ = {}; }; }; // The untyped common base class for `RepeatedField`. class RepeatedFieldBase { protected: constexpr RepeatedFieldBase() = default; constexpr explicit RepeatedFieldBase(internal::InternalMetadataOffset offset) : soo_rep_( offset .TranslateForMember()) {} SooRep soo_rep_; }; // Out-of-line abort for MergeFrom self-reference. Declared here (not in the // call site) so that the failure path does not pull ABSL_CHECK streaming // support into every inlined MergeFrom instantiation. [[noreturn]] PROTOBUF_EXPORT void LogSelfMergeAndAbort() noexcept; } // namespace internal // RepeatedField is used to represent repeated fields of a primitive type (in // other words, everything except strings and nested Messages). Most users will // not ever use a RepeatedField directly; they will use the get-by-index, // set-by-index, and add accessors that are generated for all repeated fields. // Actually, in addition to primitive types, we use RepeatedField for repeated // Cords, because the Cord class is in fact just a reference-counted pointer. // We have to specialize several methods in the Cord case to get the memory // management right; e.g. swapping when appropriate, etc. template class ABSL_ATTRIBUTE_WARN_UNUSED PROTOBUF_DECLSPEC_EMPTY_BASES RepeatedField final : private internal::RepeatedFieldBase, private internal::ContainerDestructorSkippableBase { static_assert( alignof(Arena) >= alignof(Element), "We only support types that have an alignment smaller than Arena"); static_assert(!std::is_const_v, "We do not support const value types."); static_assert(!std::is_volatile_v, "We do not support volatile value types."); static_assert(!std::is_pointer_v, "We do not support pointer value types."); static_assert(!std::is_reference_v, "We do not support reference value types."); static constexpr PROTOBUF_ALWAYS_INLINE void StaticValidityCheck() { static_assert( std::disjunction_v, internal::is_supported_floating_point_type, std::is_same, std::is_same, is_proto_enum>, "We only support non-string scalars in RepeatedField."); } public: using value_type = Element; using size_type = int; using difference_type = ptrdiff_t; using reference = Element&; using const_reference = const Element&; using pointer = Element*; using const_pointer = const Element*; using iterator = internal::RepeatedIterator; using const_iterator = internal::RepeatedIterator; using reverse_iterator = std::reverse_iterator; using const_reverse_iterator = std::reverse_iterator; constexpr RepeatedField(); RepeatedField(const RepeatedField& rhs) : RepeatedField(internal::InternalMetadataOffset(), /*arena=*/nullptr, rhs) {} template ())>>> RepeatedField(Iter begin, Iter end); // Arena enabled constructors: for internal use only. constexpr RepeatedField(internal::InternalVisibility, internal::InternalMetadataOffset offset) : RepeatedField(offset) {} RepeatedField(internal::InternalVisibility, internal::InternalMetadataOffset offset, Arena* arena, const RepeatedField& rhs) : RepeatedField(offset, arena, rhs) {} RepeatedField& operator=(const RepeatedField& other) ABSL_ATTRIBUTE_LIFETIME_BOUND; RepeatedField(RepeatedField&& rhs) noexcept : RepeatedField(internal::InternalMetadataOffset(), /*arena=*/nullptr, std::move(rhs)) {} RepeatedField& operator=(RepeatedField&& other) noexcept ABSL_ATTRIBUTE_LIFETIME_BOUND; ~RepeatedField(); PROTOBUF_FUTURE_ADD_NODISCARD bool empty() const; PROTOBUF_FUTURE_ADD_NODISCARD int size() const; PROTOBUF_FUTURE_ADD_NODISCARD const_reference Get(int index) const ABSL_ATTRIBUTE_LIFETIME_BOUND; PROTOBUF_FUTURE_ADD_NODISCARD pointer Mutable(int index) ABSL_ATTRIBUTE_LIFETIME_BOUND; PROTOBUF_FUTURE_ADD_NODISCARD const_reference operator[](int index) const ABSL_ATTRIBUTE_LIFETIME_BOUND { return Get(index); } PROTOBUF_FUTURE_ADD_NODISCARD reference operator[](int index) ABSL_ATTRIBUTE_LIFETIME_BOUND { return *Mutable(index); } PROTOBUF_FUTURE_ADD_NODISCARD const_reference at(int index) const ABSL_ATTRIBUTE_LIFETIME_BOUND; PROTOBUF_FUTURE_ADD_NODISCARD reference at(int index) ABSL_ATTRIBUTE_LIFETIME_BOUND; void Set(int index, const Element& value); void Add(Element value); // Appends a new element and returns a pointer to it. // The new element is uninitialized if |Element| is a POD type. pointer Add() ABSL_ATTRIBUTE_LIFETIME_BOUND; // Appends elements in the range [begin, end) after reserving // the appropriate number of elements. template void Add(Iter begin, Iter end); // The following APIs are for internal use only. template void InternalAddWithArena(internal::InternalVisibility, ArenaProvider arena_provider, Element value); template pointer InternalAddWithArena(internal::InternalVisibility, ArenaProvider arena_provider) ABSL_ATTRIBUTE_LIFETIME_BOUND; // Removes the last element in the array. void RemoveLast(); // Extracts elements with indices in "[start .. start+num-1]". // Copies them into "elements[0 .. num-1]" if "elements" is not nullptr. // Caution: also moves elements with indices [start+num ..]. // Calling this routine inside a loop can cause quadratic behavior. void ExtractSubrange(int start, int num, Element* elements); ABSL_ATTRIBUTE_REINITIALIZES void Clear(); // Appends the elements from `other` after this instance. // The end result length will be `other.size() + this->size()`. void MergeFrom(const RepeatedField& other); // Replaces the contents with a copy of the elements from `other`. ABSL_ATTRIBUTE_REINITIALIZES void CopyFrom(const RepeatedField& other); // Replaces the contents with RepeatedField(begin, end). template ABSL_ATTRIBUTE_REINITIALIZES void Assign(Iter begin, Iter end); // Reserves space to expand the field to at least the given size. If the // array is grown, it will always be at least doubled in size. void Reserve(int new_size); // Resizes the RepeatedField to a new, smaller size. This is O(1). // Except for RepeatedField, for which it is O(size-new_size). void Truncate(int new_size); void AddAlreadyReserved(Element value); PROTOBUF_FUTURE_ADD_NODISCARD int Capacity() const; // Adds `n` elements to this instance asserting there is enough capacity. // The added elements are uninitialized if `Element` is trivial. pointer AddAlreadyReserved() ABSL_ATTRIBUTE_LIFETIME_BOUND; pointer AddNAlreadyReserved(int n) ABSL_ATTRIBUTE_LIFETIME_BOUND; ABSL_DEPRECATE_AND_INLINE() void Resize(size_type new_size, const Element& value); // Like STL resize. Uses value to fill appended elements. // Like Truncate() if new_size <= size(), otherwise this is // O(new_size - size()). void resize(size_type new_size); void resize(size_type new_size, const Element& value); // Gets the underlying array. This pointer is possibly invalidated by // any add or remove operation. PROTOBUF_FUTURE_ADD_NODISCARD pointer mutable_data() ABSL_ATTRIBUTE_LIFETIME_BOUND; PROTOBUF_FUTURE_ADD_NODISCARD const_pointer data() const ABSL_ATTRIBUTE_LIFETIME_BOUND; // Swaps entire contents with "other". If they are separate arenas, then // copies data between each other. void Swap(RepeatedField* other); // Swaps two elements. void SwapElements(int index1, int index2); PROTOBUF_FUTURE_ADD_NODISCARD iterator begin() ABSL_ATTRIBUTE_LIFETIME_BOUND; PROTOBUF_FUTURE_ADD_NODISCARD const_iterator begin() const ABSL_ATTRIBUTE_LIFETIME_BOUND; PROTOBUF_FUTURE_ADD_NODISCARD const_iterator cbegin() const ABSL_ATTRIBUTE_LIFETIME_BOUND; PROTOBUF_FUTURE_ADD_NODISCARD iterator end() ABSL_ATTRIBUTE_LIFETIME_BOUND; PROTOBUF_FUTURE_ADD_NODISCARD const_iterator end() const ABSL_ATTRIBUTE_LIFETIME_BOUND; PROTOBUF_FUTURE_ADD_NODISCARD const_iterator cend() const ABSL_ATTRIBUTE_LIFETIME_BOUND; // Reverse iterator support PROTOBUF_FUTURE_ADD_NODISCARD reverse_iterator rbegin() ABSL_ATTRIBUTE_LIFETIME_BOUND { return reverse_iterator(end()); } PROTOBUF_FUTURE_ADD_NODISCARD const_reverse_iterator rbegin() const ABSL_ATTRIBUTE_LIFETIME_BOUND { return const_reverse_iterator(end()); } PROTOBUF_FUTURE_ADD_NODISCARD reverse_iterator rend() ABSL_ATTRIBUTE_LIFETIME_BOUND { return reverse_iterator(begin()); } PROTOBUF_FUTURE_ADD_NODISCARD const_reverse_iterator rend() const ABSL_ATTRIBUTE_LIFETIME_BOUND { return const_reverse_iterator(begin()); } // Returns the number of bytes used by the repeated field, excluding // sizeof(*this) PROTOBUF_FUTURE_ADD_NODISCARD size_t SpaceUsedExcludingSelfLong() const; PROTOBUF_FUTURE_ADD_NODISCARD int SpaceUsedExcludingSelf() const { return internal::ToIntSize(SpaceUsedExcludingSelfLong()); } // Removes the element referenced by position. // // Returns an iterator to the element immediately following the removed // element. // // Invalidates all iterators at or after the removed element, including end(). iterator erase(const_iterator position) ABSL_ATTRIBUTE_LIFETIME_BOUND; // Removes the elements in the range [first, last). // // Returns an iterator to the element immediately following the removed range. // // Invalidates all iterators at or after the removed range, including end(). iterator erase(const_iterator first, const_iterator last) ABSL_ATTRIBUTE_LIFETIME_BOUND; // Gets the Arena on which this RepeatedField stores its elements. PROTOBUF_FUTURE_ADD_NODISCARD inline Arena* GetArena() { // Note: we make this function non-const to force callers to call the // `mutable_*` accessor on the repeated field before calling `GetArena()`, // which initializes the field if it is split. If this method were const, // then `msg.repeated_field().GetArena()` would be valid, but for split // repeated fields `repeated_field()` could point to the default split // instance. This would always return `nullptr`, which is incorrect when // using arenas. return soo_rep_.arena(); } // For internal use only. // // This is public due to it being called by generated code. inline void InternalSwap(RepeatedField* other); private: using InternalArenaConstructable_ = void; template friend class Arena::InternalHelper; friend class Arena; friend class DynamicMessage; friend class internal::FieldWithArena>; // For access to private `*WithArena` functions. friend class google::protobuf::Reflection; friend class internal::EpsCopyInputStream; friend class internal::TcParser; friend class internal::WireFormat; template friend class internal::MutableRepeatedFieldProxyImpl; // For access to private arena constructor. friend class UnknownFieldSet; static constexpr int kSooCapacityElements = internal::SooCapacityElements(); static constexpr int kInitialSize = 0; static constexpr const size_t kHeapRepHeaderSize = internal::HeapRep::SizeOf(); explicit constexpr RepeatedField(internal::InternalMetadataOffset offset); RepeatedField(internal::InternalMetadataOffset offset, Arena* arena, const RepeatedField& rhs); RepeatedField(internal::InternalMetadataOffset offset, Arena* arena, RepeatedField&& rhs); template void ResizeImpl(int new_size, Init init); internal::SerialArena* GetSerialArena() { return ResolveArena(GetArena()); } template auto ResolveArena(T provider) { return internal::GetSerialArena(provider); } // Passing `SelfArena{}` is equivalent to `this`, but it avoids an extra // parameter in the ABI. We don't need to duplicate the `this` parameter. struct SelfArena {}; auto ResolveArena(SelfArena) { return GetSerialArena(); } bool is_soo() const { return soo_rep_.is_soo(); } void set_size(int size) { ABSL_DCHECK_LE(size, Capacity()); soo_rep_.set_size(size); } int Capacity(bool is_soo) const { int res = is_soo ? kSooCapacityElements : soo_rep_.capacity(); PROTOBUF_ASSUME(res >= 0); return res; } template void ReserveWithArena(ArenaProvider arena_provider, int new_size); void GrowByWithArena(Arena* arena, int grow_by) { ReserveWithArena(arena, size() + grow_by); } template void* AddUninitializedWithArena(ArenaProvider arena_provider); template pointer AddWithArena(ArenaProvider arena_provider, Element value); template pointer AddWithArena(ArenaProvider arena_provider) ABSL_ATTRIBUTE_LIFETIME_BOUND; template void AddWithArena(ArenaProvider arena_provider, Iter begin, Iter end); // Private-only API for in-place construction of elements in the repeated // field. template pointer EmplaceWithArena(Arena* arena, Args&&... args); void SwapFallbackWithTemp(Arena* arena, RepeatedField& other, Arena* other_arena, RepeatedField& temp); // Swaps entire contents with "other". Should be called only if the caller can // guarantee that both repeated fields are on the same arena or are on the // heap. Swapping between different arenas is disallowed and caught by a // ABSL_DCHECK (see API docs for details). void UnsafeArenaSwap(RepeatedField* other); // Copy constructs `n` instances in place into the array `dst`. // This function is identical to `std::uninitialized_copy_n(src, n, dst)` // except that we explicit declare the memory to not be aliased, which will // result in `memcpy` code generation instead of `memmove` for trivial types. static inline void UninitializedCopyN(const Element* PROTOBUF_RESTRICT src, int n, Element* PROTOBUF_RESTRICT dst) { std::uninitialized_copy_n(src, n, dst); } // Copy constructs `[begin, end)` instances in place into the array `dst`. // See above `UninitializedCopyN()` function comments for more information. template static inline void UninitializedCopy(Iter begin, Iter end, Element* PROTOBUF_RESTRICT dst) { std::uninitialized_copy(begin, end, dst); } // Destroys all elements in [begin, end). // This function does nothing if `Element` is trivial. static void Destroy([[maybe_unused]] const Element* begin, [[maybe_unused]] const Element* end) { if constexpr (!std::is_trivially_destructible_v) { std::for_each(begin, end, [&](const Element& e) { e.~Element(); }); } } template void AddForwardIterator(ArenaProvider arena_provider, Iter begin, Iter end); template void AddInputIterator(ArenaProvider arena_provider, Iter begin, Iter end); // Reserves space to expand the field to at least the given size. // If the array is grown, it will always be at least doubled in size. // If `annotate_size` is true (the default), then this function will annotate // the old container from `old_size` to `Capacity()` (unpoison memory) // directly before it is being released, and annotate the new container from // `Capacity()` to `old_size` (poison unused memory). template void Grow(ArenaProvider arena_provider, bool was_soo, int old_size, int new_size); template void GrowNoAnnotate(ArenaProvider arena_provider, bool was_soo, int old_size, int new_size); // Annotates a change in size of this instance. This function should be called // with (capacity, old_size) after new memory has been allocated and filled // from previous memory. void AnnotateSize(int old_size, int new_size) const { if (old_size != new_size) { [[maybe_unused]] const bool is_soo = this->is_soo(); [[maybe_unused]] const Element* elem = soo_rep_.elements(is_soo); ABSL_ANNOTATE_CONTIGUOUS_CONTAINER(elem, elem + Capacity(is_soo), elem + old_size, elem + new_size); if (new_size < old_size) { ABSL_ANNOTATE_MEMORY_IS_UNINITIALIZED( elem + new_size, (old_size - new_size) * sizeof(Element)); } } } // This should be called right before (previously annotated) memory is // released. void AnnotateForRelease() const { AnnotateSize(size(), Capacity()); } // Replaces size with new_size and returns the previous value of // size. This function is intended to be the only place where // size is modified, with the exception of `AddInputIterator()` // where the size of added items is not known in advance. inline int ExchangeCurrentSize(int new_size) { const int prev_size = size(); AnnotateSize(prev_size, new_size); set_size(new_size); return prev_size; } // Returns a pointer to elements array. // pre-condition: Capacity() > 0. Element* elements(bool is_soo) { ABSL_DCHECK_GT(Capacity(is_soo), 0); return unsafe_elements(is_soo); } const Element* elements(bool is_soo) const { ABSL_DCHECK_GT(Capacity(is_soo), 0); return unsafe_elements(is_soo); } // Returns a pointer to elements array if it exists; otherwise an invalid // pointer is returned. This only happens for empty repeated fields, where you // can't dereference this pointer anyway (it's empty). Element* unsafe_elements(bool is_soo) { return soo_rep_.elements(is_soo); } const Element* unsafe_elements(bool is_soo) const { return soo_rep_.elements(is_soo); } // Returns a pointer to the HeapRep struct. // pre-condition: the HeapRep must have been allocated, ie !is_soo(). internal::HeapRep* heap_rep() const { ABSL_DCHECK(!is_soo()); return soo_rep_.heap_rep(); } // Internal helper to delete all elements and deallocate the storage. template void InternalDeallocate(ArenaProvider arena_provider) { ABSL_DCHECK(!is_soo()); ABSL_DCHECK_EQ(ResolveArena(arena_provider), GetSerialArena()); const size_t bytes = Capacity(false) * sizeof(Element) + kHeapRepHeaderSize; if constexpr (in_destructor && Arena::is_destructor_skippable::value) { // Repeated fields with destructor-skippable elements are never destroyed // during arena cleanup. ABSL_DCHECK_EQ(GetArena(), nullptr); internal::SizedDelete(heap_rep(), bytes); } else { auto* arena = ResolveArena(arena_provider); if (arena == nullptr) { internal::SizedDelete(heap_rep(), bytes); } else if (!in_destructor) { // If we are in the destructor, we might be being destroyed as part of // the arena teardown. We can't try and return blocks to the arena then. arena->ReturnArrayMemory(heap_rep(), bytes); } } } }; namespace internal { template using RepeatedFieldWithArena = internal::FieldWithArena>; template struct FieldArenaRep> { using Type = RepeatedFieldWithArena; static RepeatedField* Get(Type* arena_rep) { return &arena_rep->field(); } }; template struct FieldArenaRep> { using Type = const RepeatedFieldWithArena; static const RepeatedField* Get(Type* arena_rep) { return &arena_rep->field(); } }; } // namespace internal // implementation ==================================================== template constexpr RepeatedField::RepeatedField() { StaticValidityCheck(); #ifdef __cpp_lib_is_constant_evaluated if (!std::is_constant_evaluated()) { AnnotateSize(kSooCapacityElements, 0); } #endif // __cpp_lib_is_constant_evaluated } template constexpr RepeatedField::RepeatedField( internal::InternalMetadataOffset offset) : RepeatedFieldBase(offset) { static_assert( sizeof(RepeatedField) == sizeof(RepeatedFieldBase), "Since we are passing an `InternalMetadataOffset` for this " "`RepeatedField` to the `RepeatedFieldBase` constructor, " "`RepeatedFieldBase` must have the same start address as `this`. We can " "validate this by checking that the two types have the same size."); StaticValidityCheck(); #ifdef __cpp_lib_is_constant_evaluated if (!std::is_constant_evaluated()) { AnnotateSize(kSooCapacityElements, 0); } #endif // __cpp_lib_is_constant_evaluated } template inline RepeatedField::RepeatedField( internal::InternalMetadataOffset offset, Arena* arena, const RepeatedField& rhs) : RepeatedField(offset) { StaticValidityCheck(); ABSL_DCHECK_EQ(arena, GetArena()); AnnotateSize(kSooCapacityElements, 0); if (auto size = rhs.size()) { bool is_soo = true; if (size > kSooCapacityElements) { Grow(arena, is_soo, 0, size); is_soo = false; } ExchangeCurrentSize(size); UninitializedCopyN(rhs.elements(rhs.is_soo()), size, unsafe_elements(is_soo)); } } template template RepeatedField::RepeatedField(Iter begin, Iter end) { StaticValidityCheck(); AnnotateSize(kSooCapacityElements, 0); Add(begin, end); } template RepeatedField::~RepeatedField() { StaticValidityCheck(); const bool is_soo = this->is_soo(); #ifndef NDEBUG auto* arena = GetArena(); // Try to trigger segfault / asan failure in non-opt builds if arena_ // lifetime has ended before the destructor. if (arena) (void)arena->SpaceAllocated(); #endif const int size = this->size(); if (size > 0) { Element* elem = unsafe_elements(is_soo); Destroy(elem, elem + size); } AnnotateForRelease(); if (!is_soo) { InternalDeallocate(SelfArena{}); } } template inline RepeatedField& RepeatedField::operator=( const RepeatedField& other) ABSL_ATTRIBUTE_LIFETIME_BOUND { if (this != &other) CopyFrom(other); return *this; } template inline RepeatedField::RepeatedField( internal::InternalMetadataOffset offset, Arena* arena, RepeatedField&& rhs) : RepeatedField(offset) { ABSL_DCHECK_EQ(arena, GetArena()); if (internal::CanMoveWithInternalSwap(arena, rhs.GetArena())) { InternalSwap(&rhs); } else { // We don't just call Swap(&rhs) here because it would perform 3 copies if // rhs is on a different arena. CopyFrom(rhs); } } template inline RepeatedField& RepeatedField::operator=( RepeatedField&& other) noexcept ABSL_ATTRIBUTE_LIFETIME_BOUND { // We don't just call Swap(&other) here because it would perform 3 copies if // the two fields are on different arenas. if (this != &other) { if (internal::CanMoveWithInternalSwap(GetArena(), other.GetArena())) { InternalSwap(&other); } else { CopyFrom(other); } } return *this; } template inline bool RepeatedField::empty() const { return size() == 0; } template inline int RepeatedField::size() const { return soo_rep_.size(); } template inline int RepeatedField::Capacity() const { return Capacity(is_soo()); } template inline void RepeatedField::AddAlreadyReserved(Element value) { const bool is_soo = this->is_soo(); const int old_size = size(); internal::RuntimeAssertInBounds(old_size, Capacity(is_soo)); void* p = elements(is_soo) + ExchangeCurrentSize(old_size + 1); ::new (p) Element(std::move(value)); } template inline Element* RepeatedField::AddAlreadyReserved() ABSL_ATTRIBUTE_LIFETIME_BOUND { const bool is_soo = this->is_soo(); const int old_size = size(); internal::RuntimeAssertInBounds(old_size, Capacity(is_soo)); // new (p) compiles into nothing: this is intentional as this // function is documented to return uninitialized data for trivial types. void* p = elements(is_soo) + ExchangeCurrentSize(old_size + 1); return ::new (p) Element; } template inline Element* RepeatedField::AddNAlreadyReserved(int n) ABSL_ATTRIBUTE_LIFETIME_BOUND { internal::RuntimeAssertInBoundsGE(n, 0); const bool is_soo = this->is_soo(); const int old_size = size(); [[maybe_unused]] const int capacity = Capacity(is_soo); const int64_t new_size_64 = static_cast(old_size) + n; internal::RuntimeAssertInBoundsLE(new_size_64, capacity); Element* p = unsafe_elements(is_soo) + ExchangeCurrentSize(old_size + n); for (Element *begin = p, *end = p + n; begin != end; ++begin) { new (static_cast(begin)) Element; } return p; } template ABSL_DEPRECATE_AND_INLINE() inline void RepeatedField::Resize(int new_size, const Element& value) { resize(new_size, value); } template template inline void RepeatedField::ResizeImpl(int new_size, Init init) { ABSL_DCHECK_GE(new_size, 0); bool is_soo = this->is_soo(); const int old_size = size(); if (new_size > old_size) { if (new_size > Capacity(is_soo)) { Grow(SelfArena{}, is_soo, old_size, new_size); is_soo = false; } Element* elem = elements(is_soo); Element* first = elem + ExchangeCurrentSize(new_size); init(first, elem + new_size); } else if (new_size < old_size) { Element* elem = unsafe_elements(is_soo); Destroy(elem + new_size, elem + old_size); ExchangeCurrentSize(new_size); } } template inline void RepeatedField::resize(size_type new_size, const Element& value) { ResizeImpl(new_size, [&](auto* first, auto* last) { std::uninitialized_fill(first, last, value); }); } template inline void RepeatedField::resize(size_type new_size) { ResizeImpl(new_size, [](auto* first, auto* last) { std::uninitialized_value_construct(first, last); }); } template inline const Element& RepeatedField::Get(int index) const ABSL_ATTRIBUTE_LIFETIME_BOUND { internal::RuntimeAssertInBounds(index, size()); return elements(is_soo())[index]; } template inline const Element& RepeatedField::at(int index) const ABSL_ATTRIBUTE_LIFETIME_BOUND { ABSL_CHECK_GE(index, 0); ABSL_CHECK_LT(index, size()); return elements(is_soo())[index]; } template inline Element& RepeatedField::at(int index) ABSL_ATTRIBUTE_LIFETIME_BOUND { ABSL_CHECK_GE(index, 0); ABSL_CHECK_LT(index, size()); return elements(is_soo())[index]; } template inline Element* RepeatedField::Mutable(int index) ABSL_ATTRIBUTE_LIFETIME_BOUND { internal::RuntimeAssertInBounds(index, size()); return &elements(is_soo())[index]; } template inline void RepeatedField::Set(int index, const Element& value) { *Mutable(index) = value; } template template inline void* RepeatedField::AddUninitializedWithArena( ArenaProvider arena_provider) { ABSL_DCHECK_EQ(ResolveArena(arena_provider), GetSerialArena()); bool is_soo = this->is_soo(); const int old_size = size(); if (ABSL_PREDICT_FALSE(old_size == Capacity(is_soo))) { Grow(arena_provider, is_soo, old_size, internal::CheckedAdd(old_size, 1)); is_soo = false; } return unsafe_elements(is_soo) + ExchangeCurrentSize(old_size + 1); } template inline void RepeatedField::Add(Element value) { AddWithArena(SelfArena{}, std::move(value)); } template template inline void RepeatedField::InternalAddWithArena( internal::InternalVisibility, ArenaProvider arena_provider, Element value) { AddWithArena(arena_provider, std::move(value)); } template template inline auto RepeatedField::AddWithArena(ArenaProvider arena_provider, Element value) -> pointer { ABSL_DCHECK_EQ(ResolveArena(arena_provider), GetSerialArena()); bool is_soo = this->is_soo(); const int old_size = size(); int capacity = Capacity(is_soo); Element* elem = unsafe_elements(is_soo); if (ABSL_PREDICT_FALSE(old_size == capacity)) { Grow(arena_provider, is_soo, old_size, internal::CheckedAdd(old_size, 1)); is_soo = false; capacity = Capacity(is_soo); elem = unsafe_elements(is_soo); } int new_size = old_size + 1; void* p = elem + ExchangeCurrentSize(new_size); auto* result = ::new (p) Element(std::move(value)); // The below helps the compiler optimize dense loops. // Note: we can't call functions in PROTOBUF_ASSUME so use local variables. [[maybe_unused]] const bool final_is_soo = this->is_soo(); PROTOBUF_ASSUME(is_soo == final_is_soo); [[maybe_unused]] const int final_size = size(); PROTOBUF_ASSUME(new_size == final_size); [[maybe_unused]] Element* const final_elements = unsafe_elements(is_soo); PROTOBUF_ASSUME(elem == final_elements); [[maybe_unused]] const int final_capacity = Capacity(is_soo); PROTOBUF_ASSUME(capacity == final_capacity); return result; } template inline Element* RepeatedField::Add() ABSL_ATTRIBUTE_LIFETIME_BOUND { return AddWithArena(SelfArena{}); } template template inline Element* RepeatedField::InternalAddWithArena( internal::InternalVisibility, ArenaProvider arena_provider) ABSL_ATTRIBUTE_LIFETIME_BOUND { return AddWithArena(arena_provider); } template template inline Element* RepeatedField::AddWithArena( ArenaProvider arena_provider) ABSL_ATTRIBUTE_LIFETIME_BOUND { return ::new (AddUninitializedWithArena(arena_provider)) Element; } template template inline void RepeatedField::AddForwardIterator( ArenaProvider arena_provider, Iter begin, Iter end) { ABSL_DCHECK_EQ(ResolveArena(arena_provider), GetSerialArena()); bool is_soo = this->is_soo(); const int old_size = size(); int capacity = Capacity(is_soo); Element* elem = unsafe_elements(is_soo); // Check for signed overflow. const size_t distance = std::distance(begin, end); ABSL_CHECK_LE(distance, static_cast(std::numeric_limits::max())) << "Input too large"; // Check again for signed overflow. const int new_size = internal::CheckedAdd(old_size, static_cast(distance)); if (ABSL_PREDICT_FALSE(new_size > capacity)) { Grow(arena_provider, is_soo, old_size, new_size); is_soo = false; elem = unsafe_elements(is_soo); capacity = Capacity(is_soo); } UninitializedCopy(begin, end, elem + ExchangeCurrentSize(new_size)); // The below helps the compiler optimize dense loops. // Note: we can't call functions in PROTOBUF_ASSUME so use local variables. [[maybe_unused]] const bool final_is_soo = this->is_soo(); PROTOBUF_ASSUME(is_soo == final_is_soo); [[maybe_unused]] const int final_size = size(); PROTOBUF_ASSUME(new_size == final_size); [[maybe_unused]] Element* const final_elements = unsafe_elements(is_soo); PROTOBUF_ASSUME(elem == final_elements); [[maybe_unused]] const int final_capacity = Capacity(is_soo); PROTOBUF_ASSUME(capacity == final_capacity); } template template inline void RepeatedField::AddInputIterator( ArenaProvider arena_provider, Iter begin, Iter end) { ABSL_DCHECK_EQ(ResolveArena(arena_provider), GetSerialArena()); bool is_soo = this->is_soo(); int size = this->size(); int capacity = Capacity(is_soo); Element* elem = unsafe_elements(is_soo); Element* first = elem + size; Element* last = elem + capacity; AnnotateForRelease(); while (begin != end) { if (ABSL_PREDICT_FALSE(first == last)) { size = first - elem; GrowNoAnnotate(arena_provider, is_soo, size, internal::CheckedAdd(size, 1)); is_soo = false; elem = unsafe_elements(is_soo); capacity = Capacity(is_soo); first = elem + size; last = elem + capacity; } ::new (static_cast(first)) Element(*begin); ++begin; ++first; } const int new_size = first - elem; soo_rep_.set_size(new_size); AnnotateSize(capacity, new_size); } template template inline void RepeatedField::Add(Iter begin, Iter end) { AddWithArena(SelfArena{}, std::move(begin), std::move(end)); } template template inline void RepeatedField::AddWithArena(ArenaProvider arena_provider, Iter begin, Iter end) { if (std::is_base_of_v< std::forward_iterator_tag, typename std::iterator_traits::iterator_category>) { AddForwardIterator(arena_provider, begin, end); } else { AddInputIterator(arena_provider, begin, end); } } template template typename RepeatedField::pointer RepeatedField::EmplaceWithArena(Arena* arena, Args&&... args) { return ::new (AddUninitializedWithArena(arena)) Element(std::forward(args)...); } template inline void RepeatedField::RemoveLast() { const bool is_soo = this->is_soo(); const int old_size = size(); ABSL_DCHECK_GT(old_size, 0); elements(is_soo)[old_size - 1].~Element(); ExchangeCurrentSize(old_size - 1); } template void RepeatedField::ExtractSubrange(int start, int num, Element* elements) { ABSL_DCHECK_GE(start, 0); ABSL_DCHECK_GE(num, 0); const bool is_soo = this->is_soo(); const int old_size = size(); ABSL_DCHECK_LE(start + num, old_size); Element* elem = unsafe_elements(is_soo); // Save the values of the removed elements if requested. if (elements != nullptr) { for (int i = 0; i < num; ++i) elements[i] = std::move(elem[i + start]); } // Slide remaining elements down to fill the gap. if (num > 0) { for (int i = start + num; i < old_size; ++i) elem[i - num] = std::move(elem[i]); Truncate(old_size - num); } } template inline void RepeatedField::Clear() { const bool is_soo = this->is_soo(); Element* elem = unsafe_elements(is_soo); Destroy(elem, elem + size()); ExchangeCurrentSize(0); } template inline void RepeatedField::MergeFrom(const RepeatedField& other) { if (ABSL_PREDICT_FALSE(&other == this)) { PROTOBUF_NO_MERGE internal::LogSelfMergeAndAbort(); } const bool other_is_soo = other.is_soo(); if (auto other_size = other.size()) { const int old_size = size(); Reserve(old_size + other_size); const bool is_soo = this->is_soo(); Element* dst = elements(is_soo) + ExchangeCurrentSize(old_size + other_size); UninitializedCopyN(other.elements(other_is_soo), other_size, dst); } } template inline void RepeatedField::CopyFrom(const RepeatedField& other) { if (&other == this) return; Clear(); MergeFrom(other); } template template inline void RepeatedField::Assign(Iter begin, Iter end) { Clear(); Add(begin, end); } template inline typename RepeatedField::iterator RepeatedField::erase( const_iterator position) ABSL_ATTRIBUTE_LIFETIME_BOUND { return erase(position, position + 1); } template inline typename RepeatedField::iterator RepeatedField::erase( const_iterator first, const_iterator last) ABSL_ATTRIBUTE_LIFETIME_BOUND { size_type first_offset = first - cbegin(); if (first != last) { Truncate(std::copy(last, cend(), begin() + first_offset) - cbegin()); } return begin() + first_offset; } template inline Element* RepeatedField::mutable_data() ABSL_ATTRIBUTE_LIFETIME_BOUND { return unsafe_elements(is_soo()); } template inline const Element* RepeatedField::data() const ABSL_ATTRIBUTE_LIFETIME_BOUND { return unsafe_elements(is_soo()); } template inline void RepeatedField::InternalSwap( RepeatedField* PROTOBUF_RESTRICT other) { ABSL_DCHECK(this != other); // We need to unpoison during the swap in case we're in SOO mode. AnnotateForRelease(); other->AnnotateForRelease(); soo_rep_.swap(other->soo_rep_); AnnotateSize(Capacity(), size()); other->AnnotateSize(other->Capacity(), other->size()); } template void RepeatedField::SwapFallbackWithTemp( Arena* arena, RepeatedField& other, Arena* other_arena, RepeatedField& temp) { ABSL_DCHECK(this != &other); temp.MergeFrom(*this); CopyFrom(other); other.UnsafeArenaSwap(&temp); } template void RepeatedField::Swap(RepeatedField* other) { if (this == other) return; Arena* arena = GetArena(); Arena* other_arena = other->GetArena(); if (internal::CanUseInternalSwap(arena, other_arena)) { InternalSwap(other); } else if (other_arena != nullptr) { // We can't call the destructor of the temp container since it allocates // memory from an arena, and the destructor of FieldWithArena expects to be // called only when arena is nullptr. absl::NoDestructor> temp_container(other_arena); auto& temp = temp_container->field(); SwapFallbackWithTemp(arena, *other, other_arena, temp); // If the element type is not destructor-skippable, then we need to invoke // the destructor of the temporary `RepeatedField`. if constexpr (!Arena::is_destructor_skippable::value) { temp.~RepeatedField(); } } else { RepeatedField temp; SwapFallbackWithTemp(arena, *other, other_arena, temp); } } template void RepeatedField::UnsafeArenaSwap(RepeatedField* other) { if (this == other) return; ABSL_DCHECK_EQ(GetArena(), other->GetArena()); InternalSwap(other); } template void RepeatedField::SwapElements(int index1, int index2) { internal::RuntimeAssertInBounds(index1, size()); internal::RuntimeAssertInBounds(index2, size()); Element* elem = elements(is_soo()); using std::swap; // enable ADL with fallback swap(elem[index1], elem[index2]); } template inline typename RepeatedField::iterator RepeatedField::begin() ABSL_ATTRIBUTE_LIFETIME_BOUND { return iterator(unsafe_elements(is_soo())); } template inline typename RepeatedField::const_iterator RepeatedField::begin() const ABSL_ATTRIBUTE_LIFETIME_BOUND { return const_iterator(unsafe_elements(is_soo())); } template inline typename RepeatedField::const_iterator RepeatedField::cbegin() const ABSL_ATTRIBUTE_LIFETIME_BOUND { return const_iterator(unsafe_elements(is_soo())); } template inline typename RepeatedField::iterator RepeatedField::end() ABSL_ATTRIBUTE_LIFETIME_BOUND { const bool is_soo = this->is_soo(); return iterator(unsafe_elements(is_soo) + size()); } template inline typename RepeatedField::const_iterator RepeatedField::end() const ABSL_ATTRIBUTE_LIFETIME_BOUND { const bool is_soo = this->is_soo(); return const_iterator(unsafe_elements(is_soo) + size()); } template inline typename RepeatedField::const_iterator RepeatedField::cend() const ABSL_ATTRIBUTE_LIFETIME_BOUND { const bool is_soo = this->is_soo(); return const_iterator(unsafe_elements(is_soo) + size()); } template inline size_t RepeatedField::SpaceUsedExcludingSelfLong() const { const int capacity = Capacity(); return capacity > kSooCapacityElements ? capacity * sizeof(Element) + kHeapRepHeaderSize : 0; } // Like C++20's std::erase_if, for RepeatedField template size_t erase_if(RepeatedField& cont, Pred pred) { // Intentionally decay `elem` to avoid exposing a reference to elements of the // repeated field directly. using DecayedElement = internal::DecayedRepeatedFieldElement; auto it = std::remove_if(cont.begin(), cont.end(), [&pred](const DecayedElement elem) { return pred(elem); }); size_t removed = cont.end() - it; cont.Truncate(cont.size() - removed); return removed; } // Like C++20's std::erase, for RepeatedField template size_t erase(RepeatedField& cont, const U& value) { return google::protobuf::erase_if(cont, [&](const auto& elem) { return elem == value; }); } // These functions mimic their std counterpart, but potentially more efficient // for Protobuf containers. template void sort(internal::RepeatedIterator begin, internal::RepeatedIterator end, Compare cmp) { // Intentionally decay `lhs` and `rhs` to avoid exposing a reference to // elements of the repeated field directly. using DecayedElement = internal::DecayedRepeatedFieldElement; std::sort(begin, end, [&cmp](const DecayedElement lhs, const DecayedElement rhs) { return cmp(lhs, rhs); }); } template void sort(internal::RepeatedIterator begin, internal::RepeatedIterator end) { google::protobuf::sort(begin, end, std::less<>{}); } template void stable_sort(internal::RepeatedIterator begin, internal::RepeatedIterator end, Compare cmp) { // Intentionally decay `lhs` and `rhs` to avoid exposing a reference to // elements of the repeated field directly. using DecayedElement = internal::DecayedRepeatedFieldElement; std::stable_sort(begin, end, [&cmp](const DecayedElement lhs, const DecayedElement rhs) { return cmp(lhs, rhs); }); } template void stable_sort(internal::RepeatedIterator begin, internal::RepeatedIterator end) { google::protobuf::stable_sort(begin, end, std::less<>{}); } // These functions mimic their absl counterpart, but they are more efficient for // Protobuf containers. template void c_sort(RepeatedField& cont, Compare cmp) { google::protobuf::sort(cont.begin(), cont.end(), cmp); } template void c_sort(RepeatedField& cont) { google::protobuf::c_sort(cont, std::less<>{}); } template void c_stable_sort(RepeatedField& cont, Compare cmp) { google::protobuf::stable_sort(cont.begin(), cont.end(), cmp); } template void c_stable_sort(RepeatedField& cont) { google::protobuf::c_stable_sort(cont, std::less<>{}); } namespace internal { // Returns the new size for a reserved field based on its 'capacity' and the // requested 'new_size'. The result is clamped to the closed interval: // [internal::kMinRepeatedFieldAllocationSize, // std::numeric_limits::max()] // Requires: new_size > capacity template inline int CalculateReserveSize(int capacity, int new_size) { constexpr int lower_limit = RepeatedFieldLowerClampLimit(); if (new_size < lower_limit) { // Clamp to smallest allowed size. return lower_limit; } constexpr int kMaxSizeBeforeClamp = (std::numeric_limits::max() - kHeapRepHeaderSize) / 2; if (ABSL_PREDICT_FALSE(capacity > kMaxSizeBeforeClamp)) { return std::numeric_limits::max(); } constexpr int kSooCapacityElements = SooCapacityElements(); if (kSooCapacityElements > 0 && kSooCapacityElements < lower_limit) { // In this case, we need to set capacity to 0 here to ensure power-of-two // sized allocations. if (capacity < lower_limit) capacity = 0; } else { ABSL_DCHECK(capacity == 0 || capacity >= lower_limit) << capacity << " " << lower_limit; } // We want to double the number of bytes, not the number of elements, to try // to stay within power-of-two allocations. // The allocation has kHeapRepHeaderSize + sizeof(T) * capacity. int doubled_size = 2 * capacity + kHeapRepHeaderSize / sizeof(T); return std::max(doubled_size, new_size); } } // namespace internal template inline void RepeatedField::Reserve(int new_size) { ReserveWithArena(SelfArena{}, new_size); } template template void RepeatedField::ReserveWithArena(ArenaProvider arena_provider, int new_size) { const bool was_soo = is_soo(); if (ABSL_PREDICT_FALSE(new_size > Capacity(was_soo))) { Grow(arena_provider, was_soo, size(), new_size); } } // Avoid inlining of Reserve(): new, copy, and delete[] lead to a significant // amount of code bloat. template template PROTOBUF_NOINLINE void RepeatedField::GrowNoAnnotate( ArenaProvider arena_provider, bool was_soo, int old_size, int new_size) { ABSL_DCHECK_EQ(ResolveArena(arena_provider), GetSerialArena()); const int old_capacity = Capacity(was_soo); ABSL_DCHECK_GT(new_size, old_capacity); internal::HeapRep* new_rep; new_size = internal::CalculateReserveSize( old_capacity, new_size); ABSL_DCHECK_LE(static_cast(new_size), (std::numeric_limits::max() - kHeapRepHeaderSize) / sizeof(Element)) << "Requested size is too large to fit into size_t."; size_t bytes = kHeapRepHeaderSize + sizeof(Element) * static_cast(new_size); internal::SerialArena* arena = ResolveArena(arena_provider); if (arena == nullptr) { ABSL_DCHECK_LE((bytes - kHeapRepHeaderSize) / sizeof(Element), static_cast(std::numeric_limits::max())) << "Requested size is too large to fit element count into int."; internal::SizedPtr res = internal::AllocateAtLeast(bytes); size_t num_available = std::min((res.n - kHeapRepHeaderSize) / sizeof(Element), static_cast(std::numeric_limits::max())); new_size = static_cast(num_available); new_rep = new (res.p) internal::HeapRep(new_size); } else { if constexpr (internal::ArenaAlignDefault::Ceil(sizeof(Element)) != sizeof(Element)) { // We need to manually align the allocation. bytes = internal::ArenaAlignDefault::Ceil(bytes); } new_rep = new (arena->AllocateAligned(bytes)) internal::HeapRep(new_size); } if (old_size > 0) { Element* pnew = new_rep->elements(); Element* pold = elements(was_soo); if constexpr (std::is_trivially_copyable_v || absl::is_trivially_relocatable::value) { memcpy(static_cast(pnew), pold, old_size * sizeof(Element)); } else { for (Element* end = pnew + old_size; pnew != end; ++pnew, ++pold) { ::new (static_cast(pnew)) Element(std::move(*pold)); pold->~Element(); } } } if (!was_soo) InternalDeallocate(arena); soo_rep_.set_non_soo(new_rep); } // Ideally we would be able to use: // template // void Grow(); // However, as explained in b/266411038#comment9, this causes issues // in shared libraries for Youtube (and possibly elsewhere). template template PROTOBUF_NOINLINE void RepeatedField::Grow( ArenaProvider arena_provider, bool was_soo, int old_size, int new_size) { AnnotateForRelease(); GrowNoAnnotate(arena_provider, was_soo, old_size, new_size); AnnotateSize(Capacity(), old_size); } template inline void RepeatedField::Truncate(int new_size) { const int old_size = size(); ABSL_DCHECK_LE(new_size, old_size); if (new_size < old_size) { Element* elem = unsafe_elements(this->is_soo()); Destroy(elem + new_size, elem + old_size); ExchangeCurrentSize(new_size); } } template <> PROTOBUF_EXPORT size_t RepeatedField::SpaceUsedExcludingSelfLong() const; // ------------------------------------------------------------------- // Iterators and helper functions that follow the spirit of the STL // std::back_insert_iterator and std::back_inserter but are tailor-made // for RepeatedField and RepeatedPtrField. Typical usage would be: // // std::copy(some_sequence.begin(), some_sequence.end(), // RepeatedFieldBackInserter(proto.mutable_sequence())); // // Ported by johannes from util/gtl/proto-array-iterators.h namespace internal { // STL-like iterator implementation for RepeatedField. You should not // refer to this class directly; use RepeatedField::iterator instead. // // Note: All of the iterator operators *must* be inlined to avoid performance // regressions. This is caused by the extern template declarations below (which // are required because of the RepeatedField extern template declarations). If // any of these functions aren't explicitly inlined (e.g. defined in the class), // the compiler isn't allowed to inline them. template class RepeatedIterator { private: using traits = std::iterator_traits*>; public: // Note: value_type is never cv-qualified. using value_type = typename traits::value_type; using difference_type = typename traits::difference_type; using pointer = Element*; using reference = Element&; using iterator_category = typename traits::iterator_category; using iterator_concept = typename IteratorConceptSupport::tag; constexpr RepeatedIterator() noexcept : it_(nullptr) {} // Allows "upcasting" from RepeatedIterator to // RepeatedIterator. template ::value>::type* = nullptr> constexpr RepeatedIterator( const RepeatedIterator& other) noexcept : it_(other.it_) {} // dereferenceable PROTOBUF_FUTURE_ADD_NODISCARD constexpr reference operator*() const noexcept { return *it_; } PROTOBUF_FUTURE_ADD_NODISCARD constexpr pointer operator->() const noexcept { return it_; } private: // Helper alias to hide the internal type. using iterator = RepeatedIterator; public: // {inc,dec}rementable iterator& operator++() noexcept { ++it_; return *this; } iterator operator++(int) noexcept { return iterator(it_++); } iterator& operator--() noexcept { --it_; return *this; } iterator operator--(int) noexcept { return iterator(it_--); } // equality_comparable friend constexpr bool operator==(const iterator& x, const iterator& y) noexcept { return x.it_ == y.it_; } friend constexpr bool operator!=(const iterator& x, const iterator& y) noexcept { return x.it_ != y.it_; } // less_than_comparable friend constexpr bool operator<(const iterator& x, const iterator& y) noexcept { return x.it_ < y.it_; } friend constexpr bool operator<=(const iterator& x, const iterator& y) noexcept { return x.it_ <= y.it_; } friend constexpr bool operator>(const iterator& x, const iterator& y) noexcept { return x.it_ > y.it_; } friend constexpr bool operator>=(const iterator& x, const iterator& y) noexcept { return x.it_ >= y.it_; } // addable, subtractable iterator& operator+=(difference_type d) noexcept { it_ += d; return *this; } constexpr iterator operator+(difference_type d) const noexcept { return iterator(it_ + d); } friend constexpr iterator operator+(const difference_type d, iterator it) noexcept { return it + d; } iterator& operator-=(difference_type d) noexcept { it_ -= d; return *this; } iterator constexpr operator-(difference_type d) const noexcept { return iterator(it_ - d); } // indexable PROTOBUF_FUTURE_ADD_NODISCARD constexpr reference operator[]( difference_type d) const noexcept { return it_[d]; } // random access iterator friend constexpr difference_type operator-(iterator it1, iterator it2) noexcept { return it1.it_ - it2.it_; } private: template friend class RepeatedIterator; // Allow construction from RepeatedField. friend class RepeatedField; explicit RepeatedIterator(pointer it) noexcept : it_(it) {} // The internal iterator. pointer it_; }; // A back inserter for RepeatedField objects. template class RepeatedFieldBackInsertIterator { public: using iterator_category = std::output_iterator_tag; using value_type = T; using pointer = void; using reference = void; using difference_type = std::ptrdiff_t; explicit RepeatedFieldBackInsertIterator( RepeatedField* const mutable_field) : field_(mutable_field) {} RepeatedFieldBackInsertIterator& operator=(const T& value) { field_->Add(value); return *this; } RepeatedFieldBackInsertIterator& operator*() { return *this; } RepeatedFieldBackInsertIterator& operator++() { return *this; } RepeatedFieldBackInsertIterator& operator++(int /* unused */) { return *this; } private: RepeatedField* field_; }; } // namespace internal // Provides a back insert iterator for RepeatedField instances, // similar to std::back_inserter(). template internal::RepeatedFieldBackInsertIterator RepeatedFieldBackInserter( RepeatedField* const mutable_field) { return internal::RepeatedFieldBackInsertIterator(mutable_field); } namespace internal { template inline void CheckIndexInBoundsOrAbort(const RepeatedField& field, int index) { if (ABSL_PREDICT_FALSE(index < 0 || index >= field.size())) { LogIndexOutOfBoundsAndAbort(index, field.size()); } } template const T& CheckedGetOrAbort(const RepeatedField& field, int index) { CheckIndexInBoundsOrAbort(field, index); return field.Get(index); } template inline T* CheckedMutableOrAbort(RepeatedField* field, int index) { CheckIndexInBoundsOrAbort(*field, index); return field->Mutable(index); } } // namespace internal } // namespace protobuf } // namespace google #include "google/protobuf/port_undef.inc" #endif // GOOGLE_PROTOBUF_REPEATED_FIELD_H__