// Copyright 2021 gRPC authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #ifndef GRPC_CORE_LIB_GPRPP_CHUNKED_VECTOR_H #define GRPC_CORE_LIB_GPRPP_CHUNKED_VECTOR_H #include #include #include #include #include #include "src/core/lib/gprpp/manual_constructor.h" #include "src/core/lib/resource_quota/arena.h" namespace grpc_core { // Arena-friendly vector type. // This "vector" allocates non-contiguous runs of kChunkSize T's at a time. // Expectation is that most usage will fit in one chunk, sometimes two will be // needed, and very rarely three. Appending is constant time, calculating the // size is O(n_chunks). template class ChunkedVector { private: // One chunk of allocated memory. struct Chunk { Chunk() = default; Chunk* next = nullptr; size_t count = 0; ManualConstructor data[kChunkSize]; }; public: explicit ChunkedVector(Arena* arena) : arena_(arena) {} template ChunkedVector(Arena* arena, Iterator begin, Iterator end) : arena_(arena) { for (; begin != end; ++begin) { EmplaceBack(*begin); } } ChunkedVector(const ChunkedVector& other) : ChunkedVector(other.arena_, other.begin(), other.end()) {} ChunkedVector& operator=(const ChunkedVector& other) { ChunkedVector tmp(other); Swap(&tmp); return *this; } ChunkedVector(ChunkedVector&& other) noexcept : arena_(other.arena_), first_(other.first_), append_(other.append_) { other.first_ = nullptr; other.append_ = nullptr; } ChunkedVector& operator=(ChunkedVector&& other) noexcept { Swap(&other); return *this; } ~ChunkedVector() { Clear(); } void Swap(ChunkedVector* other) { std::swap(other->arena_, arena_); std::swap(other->first_, first_); std::swap(other->append_, append_); } Arena* arena() const { return arena_; } // Append a new element to the end of the vector. template T* EmplaceBack(Args&&... args) { auto* p = AppendSlot(); p->Init(std::forward(args)...); return &**p; } // Remove the last element and return it. T PopBack() { GPR_ASSERT(append_ != nullptr); if (append_->count == 0) { GPR_ASSERT(first_ != append_); Chunk* chunk = first_; while (chunk->next != append_) { chunk = chunk->next; } append_ = chunk; } const auto last = append_->count - 1; T result = std::move(*append_->data[last]); append_->data[last].Destroy(); append_->count = last; return result; } void Clear() { Chunk* chunk = first_; while (chunk != nullptr && chunk->count != 0) { for (size_t i = 0; i < chunk->count; i++) { chunk->data[i].Destroy(); } chunk->count = 0; chunk = chunk->next; } append_ = first_; } // Forward-only iterator. class ForwardIterator { public: ForwardIterator(Chunk* chunk, size_t n) : chunk_(chunk), n_(n) {} using difference_type = std::ptrdiff_t; using iterator_category = std::forward_iterator_tag; using value_type = T; using pointer = T*; using reference = T&; T& operator*() const { return *chunk_->data[n_]; } T* operator->() const { return &*chunk_->data[n_]; } ForwardIterator& operator++() { ++n_; while (chunk_ != nullptr && n_ == chunk_->count) { chunk_ = chunk_->next; n_ = 0; } return *this; } ForwardIterator& operator++(int) { ForwardIterator tmp = *this; ++*this; return tmp; } bool operator==(const ForwardIterator& other) const { return chunk_ == other.chunk_ && n_ == other.n_; } bool operator!=(const ForwardIterator& other) const { return !(*this == other); } private: friend class ChunkedVector; Chunk* chunk_; size_t n_; }; // Const Forward-only iterator. class ConstForwardIterator { public: ConstForwardIterator(const Chunk* chunk, size_t n) : chunk_(chunk), n_(n) {} using iterator_category = std::forward_iterator_tag; const T& operator*() const { return *chunk_->data[n_]; } const T* operator->() const { return &*chunk_->data[n_]; } ConstForwardIterator& operator++() { ++n_; while (chunk_ != nullptr && n_ == chunk_->count) { chunk_ = chunk_->next; n_ = 0; } return *this; } ConstForwardIterator& operator++(int) { ConstForwardIterator tmp = *this; ++*this; return tmp; } bool operator==(const ConstForwardIterator& other) const { return chunk_ == other.chunk_ && n_ == other.n_; } bool operator!=(const ConstForwardIterator& other) const { return !(*this == other); } private: const Chunk* chunk_; size_t n_; }; ForwardIterator begin() { if (first_ != nullptr && first_->count == 0) return end(); return ForwardIterator(first_, 0); } ForwardIterator end() { return ForwardIterator(nullptr, 0); } ConstForwardIterator begin() const { if (first_ != nullptr && first_->count == 0) return cend(); return ConstForwardIterator(first_, 0); } ConstForwardIterator end() const { return ConstForwardIterator(nullptr, 0); } ConstForwardIterator cbegin() const { return begin(); } ConstForwardIterator cend() const { return end(); } // Count the number of elements in the vector. size_t size() const { size_t n = 0; for (const Chunk* chunk = first_; chunk != nullptr; chunk = chunk->next) { n += chunk->count; } return n; } // Return true if the vector is empty. bool empty() const { return first_ == nullptr || first_->count == 0; } void SetEnd(ForwardIterator it) { if (it == end()) return; Chunk* chunk = it.chunk_; for (size_t i = it.n_; i < chunk->count; i++) { chunk->data[i].Destroy(); } chunk->count = it.n_; append_ = chunk; while ((chunk = chunk->next) != nullptr) { for (size_t i = 0; i < chunk->count; i++) { chunk->data[i].Destroy(); } chunk->count = 0; } } private: ManualConstructor* AppendSlot() { if (append_ == nullptr) { GPR_ASSERT(first_ == nullptr); first_ = arena_->New(); append_ = first_; } else if (append_->count == kChunkSize) { if (append_->next == nullptr) { append_->next = arena_->New(); } append_ = append_->next; } return &append_->data[append_->count++]; } Arena* arena_; Chunk* first_ = nullptr; Chunk* append_ = nullptr; }; } // namespace grpc_core #endif // GRPC_CORE_LIB_GPRPP_CHUNKED_VECTOR_H