• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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