Blender V5.0
BLI_vector_list.hh
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2023 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
8
9#pragma once
10
11#include <algorithm>
12#include <cmath>
13
14#include "BLI_math_bits.h"
15#include "BLI_utildefines.h"
16#include "BLI_vector.hh"
17
18namespace blender {
19
38template<typename T, int64_t CapacityStart = 32, int64_t CapacityMax = 4096> class VectorList {
40 using VectorT = Vector<T, 0>;
41
42 static_assert(is_power_of_2(CapacityStart));
43 static_assert(is_power_of_2(CapacityMax));
44 static_assert(CapacityStart <= CapacityMax);
45
47 Vector<VectorT> vectors_;
49 int64_t used_vectors_ = 0;
51 int64_t size_ = 0;
52
53 public:
55 {
56 this->append_vector();
57 used_vectors_ = 1;
58 }
59
60 VectorList(VectorList &&other) noexcept
61 {
62 vectors_ = std::move(other.vectors_);
63 used_vectors_ = other.used_vectors_;
64 size_ = other.size_;
65 other.clear_and_shrink();
66 }
67
69 {
70 return move_assign_container(*this, std::move(other));
71 }
72
74 void append(const T &value)
75 {
76 this->append_as(value);
77 }
78
80 void append(T &&value)
81 {
82 this->append_as(std::move(value));
83 }
84
86 template<typename ForwardT> void append_as(ForwardT &&value)
87 {
88 VectorT &vector = this->ensure_space_for_one();
89 vector.append_unchecked_as(std::forward<ForwardT>(value));
90 size_++;
91 }
92
98 {
99 BLI_assert(size() > 0);
100 return vectors_.first().first();
101 }
102
108 {
109 BLI_assert(size() > 0);
110 return vectors_[used_vectors_ - 1].last();
111 }
112
114 int64_t size() const
115 {
116 return size_;
117 }
118
124 bool is_empty() const
125 {
126 return size_ == 0;
127 }
128
130 void clear()
131 {
132 for (VectorT &vector : vectors_) {
133 vector.clear();
134 }
135 used_vectors_ = 1;
136 size_ = 0;
137 }
138
141 {
142 vectors_.clear();
143 this->append_vector();
144 used_vectors_ = 1;
145 size_ = 0;
146 }
147
152 const T &operator[](int64_t index) const
153 {
154 std::pair<int64_t, int64_t> index_pair = this->global_index_to_index_pair(index);
155 return vectors_[index_pair.first][index_pair.second];
156 }
157
163 {
164 std::pair<int64_t, int64_t> index_pair = this->global_index_to_index_pair(index);
165 return vectors_[index_pair.first][index_pair.second];
166 }
167
168 private:
174 std::pair<int64_t, int64_t> global_index_to_index_pair(int64_t index) const
175 {
176 BLI_assert(index >= 0);
177 BLI_assert(index < this->size());
178
179 auto log2 = [](int64_t value) -> int64_t {
180 return 31 - bitscan_reverse_uint(uint32_t(value));
181 };
182 auto geometric_sum = [](int64_t index) -> int64_t {
183 return CapacityStart * ((2 << index) - 1);
184 };
185 auto index_from_sum = [log2](int64_t sum) -> int64_t {
186 return log2((sum / CapacityStart) + 1);
187 };
188
189 static const int64_t start_log2 = log2(CapacityStart);
190 static const int64_t end_log2 = log2(CapacityMax);
191 /* The number of vectors until CapacityMax size is reached. */
192 static const int64_t geometric_steps = end_log2 - start_log2 + 1;
193 /* The number of elements until CapacityMax size is reached. */
194 static const int64_t geometric_total = geometric_sum(geometric_steps - 1);
195
196 int64_t index_a, index_b;
197 if (index < geometric_total) {
198 index_a = index_from_sum(index);
199 index_b = index_a > 0 ? index - geometric_sum(index_a - 1) : index;
200 }
201 else {
202 int64_t linear_start = index - geometric_total;
203 index_a = geometric_steps + linear_start / CapacityMax;
204 index_b = linear_start % CapacityMax;
205 }
206 return {index_a, index_b};
207 }
208
209 VectorT &ensure_space_for_one()
210 {
211 if (vectors_[used_vectors_ - 1].is_at_capacity()) {
212 if (used_vectors_ == vectors_.size()) {
213 this->append_vector();
214 }
215 used_vectors_++;
216 }
217 return vectors_[used_vectors_ - 1];
218 }
219
220 void append_vector()
221 {
222 const int64_t new_vector_capacity = this->get_next_vector_capacity();
223 vectors_.append({});
224 vectors_.last().reserve(new_vector_capacity);
225 }
226
227 int64_t get_next_vector_capacity()
228 {
229 if (vectors_.is_empty()) {
230 return CapacityStart;
231 }
232 return std::min(vectors_.last().capacity() * 2, CapacityMax);
233 }
234
235 template<typename IterableT, typename ElemT> struct Iterator {
236 IterableT &vector_list;
237 int64_t index_a = 0;
238 int64_t index_b = 0;
239
240 Iterator(IterableT &vector_list, int64_t index_a = 0, int64_t index_b = 0)
241 : vector_list(vector_list), index_a(index_a), index_b(index_b)
242 {
243 }
244
245 ElemT &operator*() const
246 {
247 return vector_list.vectors_[index_a][index_b];
248 }
249
250 Iterator &operator++()
251 {
252 if (vector_list.vectors_[index_a].size() == index_b + 1) {
253 if (index_a + 1 == vector_list.used_vectors_) {
254 /* Reached the end. */
255 index_b++;
256 }
257 else {
258 index_a++;
259 index_b = 0;
260 }
261 }
262 else {
263 index_b++;
264 }
265 return *this;
266 }
267
268 bool operator==(const Iterator &other) const
269 {
270 BLI_assert(&other.vector_list == &vector_list);
271 return other.index_a == index_a && other.index_b == index_b;
272 }
273
274 bool operator!=(const Iterator &other) const
275 {
276 return !(other == *this);
277 }
278 };
279
280 using MutIterator = Iterator<SelfT, T>;
281 using ConstIterator = Iterator<const SelfT, const T>;
282
283 public:
284 MutIterator begin()
285 {
286 return MutIterator(*this, 0, 0);
287 }
288 MutIterator end()
289 {
290 return MutIterator(*this, used_vectors_ - 1, vectors_[used_vectors_ - 1].size());
291 }
292
293 ConstIterator begin() const
294 {
295 return ConstIterator(*this, 0, 0);
296 }
297 ConstIterator end() const
298 {
299 return ConstIterator(*this, used_vectors_ - 1, vectors_[used_vectors_ - 1].size());
300 }
301};
302
303} // namespace blender
#define BLI_assert(a)
Definition BLI_assert.h:46
MINLINE unsigned int bitscan_reverse_uint(unsigned int a)
long long int int64_t
static T sum(const btAlignedObjectArray< T > &items)
VectorList & operator=(VectorList &&other)
T & operator[](int64_t index)
void append(const T &value)
void append_as(ForwardT &&value)
const T & operator[](int64_t index) const
void append(T &&value)
ConstIterator begin() const
ConstIterator end() const
VectorList(VectorList &&other) noexcept
#define log2
#define T
Container & move_assign_container(Container &dst, Container &&src) noexcept(std::is_nothrow_move_constructible_v< Container >)