1 // Copyright 2021 gRPC authors. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 #ifndef GRPC_SRC_CORE_LIB_GPRPP_CHUNKED_VECTOR_H 16 #define GRPC_SRC_CORE_LIB_GPRPP_CHUNKED_VECTOR_H 17 18 #include <grpc/support/port_platform.h> 19 20 #include <cstddef> 21 #include <iterator> 22 #include <utility> 23 24 #include <grpc/support/log.h> 25 26 #include "src/core/lib/gprpp/manual_constructor.h" 27 #include "src/core/lib/resource_quota/arena.h" 28 29 namespace grpc_core { 30 31 // Arena-friendly vector type. 32 // This "vector" allocates non-contiguous runs of kChunkSize T's at a time. 33 // Expectation is that most usage will fit in one chunk, sometimes two will be 34 // needed, and very rarely three. Appending is constant time, calculating the 35 // size is O(n_chunks). 36 template <typename T, size_t kChunkSize> 37 class ChunkedVector { 38 private: 39 // One chunk of allocated memory. 40 struct Chunk { 41 Chunk() = default; 42 Chunk* next = nullptr; 43 size_t count = 0; 44 ManualConstructor<T> data[kChunkSize]; 45 }; 46 47 public: ChunkedVector(Arena * arena)48 explicit ChunkedVector(Arena* arena) : arena_(arena) {} 49 template <class Iterator> ChunkedVector(Arena * arena,Iterator begin,Iterator end)50 ChunkedVector(Arena* arena, Iterator begin, Iterator end) : arena_(arena) { 51 for (; begin != end; ++begin) { 52 EmplaceBack(*begin); 53 } 54 } ChunkedVector(const ChunkedVector & other)55 ChunkedVector(const ChunkedVector& other) 56 : ChunkedVector(other.arena_, other.begin(), other.end()) {} 57 ChunkedVector& operator=(const ChunkedVector& other) { 58 ChunkedVector tmp(other); 59 Swap(&tmp); 60 return *this; 61 } ChunkedVector(ChunkedVector && other)62 ChunkedVector(ChunkedVector&& other) noexcept 63 : arena_(other.arena_), first_(other.first_), append_(other.append_) { 64 other.first_ = nullptr; 65 other.append_ = nullptr; 66 } 67 ChunkedVector& operator=(ChunkedVector&& other) noexcept { 68 Swap(&other); 69 return *this; 70 } ~ChunkedVector()71 ~ChunkedVector() { Clear(); } Swap(ChunkedVector * other)72 void Swap(ChunkedVector* other) { 73 std::swap(other->arena_, arena_); 74 std::swap(other->first_, first_); 75 std::swap(other->append_, append_); 76 } 77 arena()78 Arena* arena() const { return arena_; } 79 80 // Append a new element to the end of the vector. 81 template <typename... Args> EmplaceBack(Args &&...args)82 T* EmplaceBack(Args&&... args) { 83 auto* p = AppendSlot(); 84 p->Init(std::forward<Args>(args)...); 85 return &**p; 86 } 87 88 // Remove the last element and return it. PopBack()89 T PopBack() { 90 GPR_ASSERT(append_ != nullptr); 91 if (append_->count == 0) { 92 GPR_ASSERT(first_ != append_); 93 Chunk* chunk = first_; 94 while (chunk->next != append_) { 95 chunk = chunk->next; 96 } 97 append_ = chunk; 98 } 99 const auto last = append_->count - 1; 100 T result = std::move(*append_->data[last]); 101 append_->data[last].Destroy(); 102 append_->count = last; 103 return result; 104 } 105 Clear()106 void Clear() { 107 Chunk* chunk = first_; 108 while (chunk != nullptr && chunk->count != 0) { 109 for (size_t i = 0; i < chunk->count; i++) { 110 chunk->data[i].Destroy(); 111 } 112 chunk->count = 0; 113 chunk = chunk->next; 114 } 115 append_ = first_; 116 } 117 118 // Forward-only iterator. 119 class ForwardIterator { 120 public: ForwardIterator(Chunk * chunk,size_t n)121 ForwardIterator(Chunk* chunk, size_t n) : chunk_(chunk), n_(n) {} 122 123 using difference_type = std::ptrdiff_t; 124 using iterator_category = std::forward_iterator_tag; 125 using value_type = T; 126 using pointer = T*; 127 using reference = T&; 128 129 T& operator*() const { return *chunk_->data[n_]; } 130 T* operator->() const { return &*chunk_->data[n_]; } 131 ForwardIterator& operator++() { 132 ++n_; 133 while (chunk_ != nullptr && n_ == chunk_->count) { 134 chunk_ = chunk_->next; 135 n_ = 0; 136 } 137 return *this; 138 } 139 ForwardIterator& operator++(int) { 140 ForwardIterator tmp = *this; 141 ++*this; 142 return tmp; 143 } 144 bool operator==(const ForwardIterator& other) const { 145 return chunk_ == other.chunk_ && n_ == other.n_; 146 } 147 bool operator!=(const ForwardIterator& other) const { 148 return !(*this == other); 149 } 150 151 private: 152 friend class ChunkedVector; 153 154 Chunk* chunk_; 155 size_t n_; 156 }; 157 158 // Const Forward-only iterator. 159 class ConstForwardIterator { 160 public: ConstForwardIterator(const Chunk * chunk,size_t n)161 ConstForwardIterator(const Chunk* chunk, size_t n) : chunk_(chunk), n_(n) {} 162 163 using iterator_category = std::forward_iterator_tag; 164 165 const T& operator*() const { return *chunk_->data[n_]; } 166 const T* operator->() const { return &*chunk_->data[n_]; } 167 ConstForwardIterator& operator++() { 168 ++n_; 169 while (chunk_ != nullptr && n_ == chunk_->count) { 170 chunk_ = chunk_->next; 171 n_ = 0; 172 } 173 return *this; 174 } 175 ConstForwardIterator& operator++(int) { 176 ConstForwardIterator tmp = *this; 177 ++*this; 178 return tmp; 179 } 180 bool operator==(const ConstForwardIterator& other) const { 181 return chunk_ == other.chunk_ && n_ == other.n_; 182 } 183 bool operator!=(const ConstForwardIterator& other) const { 184 return !(*this == other); 185 } 186 187 private: 188 const Chunk* chunk_; 189 size_t n_; 190 }; 191 begin()192 ForwardIterator begin() { 193 if (first_ != nullptr && first_->count == 0) return end(); 194 return ForwardIterator(first_, 0); 195 } end()196 ForwardIterator end() { return ForwardIterator(nullptr, 0); } 197 begin()198 ConstForwardIterator begin() const { 199 if (first_ != nullptr && first_->count == 0) return cend(); 200 return ConstForwardIterator(first_, 0); 201 } end()202 ConstForwardIterator end() const { return ConstForwardIterator(nullptr, 0); } 203 cbegin()204 ConstForwardIterator cbegin() const { return begin(); } cend()205 ConstForwardIterator cend() const { return end(); } 206 207 // Count the number of elements in the vector. size()208 size_t size() const { 209 size_t n = 0; 210 for (const Chunk* chunk = first_; chunk != nullptr; chunk = chunk->next) { 211 n += chunk->count; 212 } 213 return n; 214 } 215 216 // Return true if the vector is empty. empty()217 bool empty() const { return first_ == nullptr || first_->count == 0; } 218 SetEnd(ForwardIterator it)219 void SetEnd(ForwardIterator it) { 220 if (it == end()) return; 221 Chunk* chunk = it.chunk_; 222 for (size_t i = it.n_; i < chunk->count; i++) { 223 chunk->data[i].Destroy(); 224 } 225 chunk->count = it.n_; 226 append_ = chunk; 227 while ((chunk = chunk->next) != nullptr) { 228 for (size_t i = 0; i < chunk->count; i++) { 229 chunk->data[i].Destroy(); 230 } 231 chunk->count = 0; 232 } 233 } 234 235 private: AppendSlot()236 ManualConstructor<T>* AppendSlot() { 237 if (append_ == nullptr) { 238 GPR_ASSERT(first_ == nullptr); 239 first_ = arena_->New<Chunk>(); 240 append_ = first_; 241 } else if (append_->count == kChunkSize) { 242 if (append_->next == nullptr) { 243 append_->next = arena_->New<Chunk>(); 244 } 245 append_ = append_->next; 246 } 247 return &append_->data[append_->count++]; 248 } 249 250 Arena* arena_; 251 Chunk* first_ = nullptr; 252 Chunk* append_ = nullptr; 253 }; 254 255 } // namespace grpc_core 256 257 #endif // GRPC_SRC_CORE_LIB_GPRPP_CHUNKED_VECTOR_H 258