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