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