Skip to content
LeviMC
English
AutoLightDark
LeviLamina
Loading...
Searching...
No Matches
SmallVector.h
1#pragma once
2
3#include <algorithm>
4#include <concepts>
5#include <cstddef>
6#include <cstdint>
7#include <cstring>
8#include <initializer_list>
9#include <iterator>
10#include <limits>
11#include <memory>
12#include <new>
13#include <stdexcept>
14#include <type_traits>
15#include <utility>
16
17#include "ll/api/memory/Memory.h"
18#include "mc/deps/core/memory/IMemoryAllocator.h"
19
20namespace ll::data {
21class SmallVectorHeader {
22public:
23 using size_type = uint32_t;
24
25 [[nodiscard]] size_type size() const noexcept { return mSize; }
26 [[nodiscard]] size_type capacity() const noexcept { return mCapacity; }
27 [[nodiscard]] bool empty() const noexcept { return mSize == 0; }
28 [[nodiscard]] static constexpr size_type max_size() noexcept { return std::numeric_limits<size_type>::max(); }
29
30protected:
31 static constexpr float growthFactor = 1.5f;
32
33 std::byte* mData;
34 size_type mSize;
35 size_type mCapacity;
36
37 SmallVectorHeader(std::byte* data, size_type capacity) noexcept : mData(data), mSize(0), mCapacity(capacity) {}
38};
39
40template <class T>
41class SmallVectorBase : public SmallVectorHeader {
42public:
43 using value_type = T;
44 using size_type = SmallVectorHeader::size_type;
45 using difference_type = std::ptrdiff_t;
46 using reference = T&;
47 using const_reference = T const&;
48 using pointer = T*;
49 using const_pointer = T const*;
50 using iterator = pointer;
51 using const_iterator = const_pointer;
52 using reverse_iterator = std::reverse_iterator<iterator>;
53 using const_reverse_iterator = std::reverse_iterator<const_iterator>;
54
55 SmallVectorBase(SmallVectorBase const&) = delete;
56 SmallVectorBase(SmallVectorBase&&) = delete;
57
58 SmallVectorBase& operator=(SmallVectorBase const& other) {
59 if (this != &other) assign(other.begin(), other.end());
60 return *this;
61 }
62 SmallVectorBase& operator=(SmallVectorBase&& other) noexcept(std::is_nothrow_move_constructible_v<T>) {
63 if (this == &other) return *this;
64 if (other.isInline()) {
65 assign(std::make_move_iterator(other.begin()), std::make_move_iterator(other.end()));
66 other.clear();
67 } else {
68 destroyAll();
69 freeHeap();
70 mData = std::exchange(other.mData, nullptr);
71 mSize = std::exchange(other.mSize, 0);
72 mCapacity = std::exchange(other.mCapacity, 0);
73 }
74 return *this;
75 }
76 SmallVectorBase& operator=(std::initializer_list<T> list) {
77 assign(list);
78 return *this;
79 }
80
81 [[nodiscard]] reference operator[](size_type i) noexcept { return data()[i]; }
82 [[nodiscard]] const_reference operator[](size_type i) const noexcept { return data()[i]; }
83 [[nodiscard]] reference at(size_type i) {
84 if (i >= mSize) throw std::out_of_range("small_vector index out of range");
85 return data()[i];
86 }
87 [[nodiscard]] const_reference at(size_type i) const {
88 if (i >= mSize) throw std::out_of_range("small_vector index out of range");
89 return data()[i];
90 }
91 [[nodiscard]] reference front() noexcept { return data()[0]; }
92 [[nodiscard]] const_reference front() const noexcept { return data()[0]; }
93 [[nodiscard]] reference back() noexcept { return data()[mSize - 1]; }
94 [[nodiscard]] const_reference back() const noexcept { return data()[mSize - 1]; }
95 [[nodiscard]] pointer data() noexcept { return std::launder(reinterpret_cast<T*>(mData)); }
96 [[nodiscard]] const_pointer data() const noexcept { return std::launder(reinterpret_cast<T const*>(mData)); }
97
98 iterator begin() noexcept { return data(); }
99 const_iterator begin() const noexcept { return data(); }
100 const_iterator cbegin() const noexcept { return data(); }
101 iterator end() noexcept { return data() + mSize; }
102 const_iterator end() const noexcept { return data() + mSize; }
103 const_iterator cend() const noexcept { return end(); }
104 reverse_iterator rbegin() noexcept { return reverse_iterator{end()}; }
105 const_reverse_iterator rbegin() const noexcept { return const_reverse_iterator{end()}; }
106 const_reverse_iterator crbegin() const noexcept { return rbegin(); }
107 reverse_iterator rend() noexcept { return reverse_iterator{begin()}; }
108 const_reverse_iterator rend() const noexcept { return const_reverse_iterator{begin()}; }
109 const_reverse_iterator crend() const noexcept { return rend(); }
110
111 void reserve(size_type n) {
112 if (n > mCapacity) reallocate(n);
113 }
114
115 void shrink_to_fit() {
116 if (isInline() || mSize == mCapacity) return;
117 if (mSize == 0) {
118 deallocate(mData);
119 mData = nullptr;
120 mCapacity = 0;
121 } else {
122 reallocate(mSize);
123 }
124 }
125
126 template <class... Args>
127 reference emplace_back(Args&&... args) {
128 if (mSize < mCapacity) return emplaceBackNoAllocate(std::forward<Args>(args)...);
129 return *emplaceReallocate(end(), std::forward<Args>(args)...);
130 }
131 reference push_back(T const& value) { return emplace_back(value); }
132 reference push_back(T&& value) { return emplace_back(std::move(value)); }
133
134 template <class... Args>
135 iterator emplace(const_iterator pos, Args&&... args) {
136 if (pos == cend()) return &emplace_back(std::forward<Args>(args)...);
137 if (mSize == mCapacity) return emplaceReallocate(pos, std::forward<Args>(args)...);
138 T tmp(std::forward<Args>(args)...);
139 T* p = unwrap(pos);
140 T* e = end();
141 std::construct_at(e, std::move(e[-1]));
142 ++mSize;
143 std::move_backward(p, e - 1, e);
144 *p = std::move(tmp);
145 return p;
146 }
147
148 iterator insert(const_iterator pos, T const& value) { return emplace(pos, value); }
149 iterator insert(const_iterator pos, T&& value) { return emplace(pos, std::move(value)); }
150 iterator insert(const_iterator pos, size_type count, T const& value) {
151 auto idx = static_cast<size_type>(pos - cbegin());
152 if (count == 0) return begin() + idx;
153 T tmp(value);
154 insertGap(idx, count, [&](T* first, T* last) { std::uninitialized_fill(first, last, tmp); });
155 return begin() + idx;
156 }
157 template <std::input_iterator It>
158 iterator insert(const_iterator pos, It first, It last) {
159 auto idx = static_cast<size_type>(pos - cbegin());
160 if constexpr (std::forward_iterator<It>) {
161 auto count = static_cast<size_type>(std::distance(first, last));
162 if (count != 0) {
163 insertGap(idx, count, [&](T* f, T*) { std::uninitialized_copy(first, last, f); });
164 }
165 } else {
166 for (auto i = idx; first != last; ++first, ++i) emplace(cbegin() + i, *first);
167 }
168 return begin() + idx;
169 }
170 iterator insert(const_iterator pos, std::initializer_list<T> list) { return insert(pos, list.begin(), list.end()); }
171
172 void pop_back() noexcept { std::destroy_at(data() + --mSize); }
173
174 iterator erase(const_iterator pos) { return erase(pos, pos + 1); }
175 iterator erase(const_iterator first, const_iterator last) {
176 T* f = unwrap(first);
177 T* l = unwrap(last);
178 if (f != l) {
179 T* newEnd = std::move(l, end(), f);
180 std::destroy(newEnd, end());
181 mSize = static_cast<size_type>(newEnd - data());
182 }
183 return f;
184 }
185
186 void clear() noexcept {
187 destroyAll();
188 mSize = 0;
189 }
190
191 void resize(size_type n) {
192 resizeImpl(n, [](T* f, T* l) { std::uninitialized_value_construct(f, l); });
193 }
194 void resize(size_type n, T const& value) {
195 resizeImpl(n, [&](T* f, T* l) { std::uninitialized_fill(f, l, value); });
196 }
197
198 void assign(size_type count, T const& value) {
199 clear();
200 reserve(count);
201 std::uninitialized_fill_n(data(), count, value);
202 mSize = count;
203 }
204 template <std::input_iterator It>
205 void assign(It first, It last) {
206 clear();
207 if constexpr (std::forward_iterator<It>) {
208 auto count = static_cast<size_type>(std::distance(first, last));
209 reserve(count);
210 std::uninitialized_copy(first, last, data());
211 mSize = count;
212 } else {
213 for (; first != last; ++first) emplace_back(*first);
214 }
215 }
216 void assign(std::initializer_list<T> list) { assign(list.begin(), list.end()); }
217
218 void swap(SmallVectorBase& other) {
219 if (this == &other) return;
220 if (!isInline() && !other.isInline()) {
221 std::swap(mData, other.mData);
222 std::swap(mSize, other.mSize);
223 std::swap(mCapacity, other.mCapacity);
224 return;
225 }
226 SmallVectorBase& small = mSize <= other.mSize ? *this : other;
227 SmallVectorBase& large = mSize <= other.mSize ? other : *this;
228 small.reserve(large.mSize);
229 large.reserve(small.mSize);
230 size_type common = small.mSize;
231 std::swap_ranges(small.begin(), small.begin() + common, large.begin());
232 std::uninitialized_move(large.begin() + common, large.end(), small.end());
233 std::destroy(large.begin() + common, large.end());
234 std::swap(small.mSize, large.mSize);
235 }
236
237 friend bool operator==(SmallVectorBase const& a, SmallVectorBase const& b) {
238 return std::equal(a.begin(), a.end(), b.begin(), b.end());
239 }
240 friend auto operator<=>(SmallVectorBase const& a, SmallVectorBase const& b) {
241 return std::lexicographical_compare_three_way(a.begin(), a.end(), b.begin(), b.end());
242 }
243
244protected:
245 explicit SmallVectorBase(size_type inlineCap) noexcept : SmallVectorHeader(inlineData(), inlineCap) {}
246
247 ~SmallVectorBase() {
248 destroyAll();
249 freeHeap();
250 }
251
252private:
253 std::byte* inlineData() noexcept { return reinterpret_cast<std::byte*>(this + 1); }
254 std::byte const* inlineData() const noexcept { return reinterpret_cast<std::byte const*>(this + 1); }
255
256 bool isInline() const noexcept { return mData == inlineData(); }
257
258 static T* unwrap(const_iterator it) noexcept { return const_cast<T*>(it); }
259
260 static T* allocate(size_type n) {
261 size_t bytes = size_t{n} * sizeof(T);
262 void* p = memory::getDefaultAllocator()._allocate(bytes);
263 if (!p) memory::throwMemoryException(bytes);
264 return static_cast<T*>(p);
265 }
266 static void deallocate(void* p) noexcept {
267 if (p) memory::getDefaultAllocator()._release(p);
268 }
269 void freeHeap() noexcept { freeHeap(data()); }
270 void freeHeap(T* p) noexcept {
271 if (reinterpret_cast<std::byte*>(p) != inlineData()) deallocate(p);
272 }
273
274 void destroyAll() noexcept { std::destroy(begin(), end()); }
275
276 static void relocate(T* first, T* last, T* dest) {
277 if constexpr (std::is_trivially_copyable_v<T>) {
278 if (first != last) std::memmove(dest, first, (last - first) * sizeof(T));
279 } else {
280 std::uninitialized_move(first, last, dest);
281 std::destroy(first, last);
282 }
283 }
284
285 size_type newCapacity(size_type required) const {
286 if (mCapacity > max_size() - static_cast<size_type>(mCapacity / growthFactor)) return max_size();
287 auto grown = static_cast<size_type>(mCapacity * growthFactor);
288 return std::max(grown, required);
289 }
290
291 void reallocate(size_type n) {
292 T* fresh = allocate(n);
293 T* old = data();
294 try {
295 relocate(old, old + mSize, fresh);
296 } catch (...) {
297 deallocate(fresh);
298 throw;
299 }
300 freeHeap(old);
301 mData = reinterpret_cast<std::byte*>(fresh);
302 mCapacity = n;
303 }
304
305 template <class... Args>
306 reference emplaceBackNoAllocate(Args&&... args) {
307 T* p = std::construct_at(end(), std::forward<Args>(args)...);
308 ++mSize;
309 return *p;
310 }
311
312 template <class Fill>
313 void insertGap(size_type idx, size_type count, Fill&& fill) {
314 if (mSize > max_size() - count) throw std::length_error("New would be greater than max_size");
315 size_type required = mSize + count;
316 if (required > mCapacity) {
317 size_type cap = newCapacity(required);
318 T* fresh = allocate(cap);
319 T* old = data();
320 try {
321 fill(fresh + idx, fresh + idx + count);
322 } catch (...) {
323 deallocate(fresh);
324 throw;
325 }
326 relocate(old, old + idx, fresh);
327 relocate(old + idx, old + mSize, fresh + idx + count);
328 freeHeap(old);
329 mData = reinterpret_cast<std::byte*>(fresh);
330 mCapacity = cap;
331 mSize = required;
332 return;
333 }
334 T* p = data() + idx;
335 T* e = end();
336 auto tail = static_cast<size_type>(e - p);
337 if (tail == 0) {
338 fill(e, e + count);
339 } else {
340 // Build the inserted run aside first so a throwing constructor leaves *this intact.
341 TempBuffer tmp(count);
342 fill(tmp.get(), tmp.get() + count);
343 tmp.built();
344 if (count <= tail) {
345 std::uninitialized_move(e - count, e, e);
346 std::move_backward(p, e - count, e);
347 std::move(tmp.get(), tmp.get() + count, p);
348 } else {
349 std::uninitialized_move(p, e, p + count);
350 std::move(tmp.get(), tmp.get() + tail, p);
351 std::uninitialized_move(tmp.get() + tail, tmp.get() + count, e);
352 }
353 }
354 mSize = required;
355 }
356
357 template <class... Args>
358 iterator emplaceReallocate(const_iterator pos, Args&&... args) {
359 auto idx = static_cast<size_type>(pos - cbegin());
360 insertGap(idx, 1, [&](T* f, T*) { std::construct_at(f, std::forward<Args>(args)...); });
361 return begin() + idx;
362 }
363
364 template <class Fill>
365 void resizeImpl(size_type n, Fill&& fill) {
366 if (n < mSize) {
367 std::destroy(begin() + n, end());
368 } else if (n > mSize) {
369 reserve(n);
370 fill(end(), data() + n);
371 }
372 mSize = n;
373 }
374
375 class TempBuffer {
376 T* mPtr;
377 size_type mCount;
378 size_type mBuilt{};
379
380 public:
381 explicit TempBuffer(size_type n) : mPtr(allocate(n)), mCount(n) {}
382 TempBuffer(TempBuffer const&) = delete;
383 TempBuffer& operator=(TempBuffer const&) = delete;
384 ~TempBuffer() {
385 std::destroy(mPtr, mPtr + mBuilt);
386 deallocate(mPtr);
387 }
388 T* get() const noexcept { return mPtr; }
389 void built() noexcept { mBuilt = mCount; }
390 };
391};
392
393template <class T, uint32_t N>
394class SmallVector : public SmallVectorBase<T> {
395 static_assert(N > 0);
396 static_assert(alignof(T) <= alignof(SmallVectorHeader), "inline storage must start right after the header");
397
398 using Base = SmallVectorBase<T>;
399
400public:
401 using typename Base::size_type;
402
403 static constexpr uint32_t inlineCapacity = N;
404
405 SmallVector() noexcept : Base(N) {}
406 SmallVector(SmallVector const& other) : Base(N) { Base::assign(other.begin(), other.end()); }
407 SmallVector(SmallVector&& other) noexcept(std::is_nothrow_move_constructible_v<T>) : Base(N) {
408 Base::operator=(std::move(other));
409 }
410 explicit SmallVector(size_type count) : Base(N) { Base::resize(count); }
411 SmallVector(size_type count, T const& value) : Base(N) { Base::assign(count, value); }
412 SmallVector(std::initializer_list<T> list) : Base(N) { Base::assign(list); }
413 template <std::input_iterator It>
414 SmallVector(It first, It last) : Base(N) {
415 Base::assign(first, last);
416 }
417
418 ~SmallVector() = default;
419
420 SmallVector& operator=(SmallVector const& other) {
421 Base::operator=(other);
422 return *this;
423 }
424 SmallVector& operator=(SmallVector&& other) noexcept(std::is_nothrow_move_constructible_v<T>) {
425 Base::operator=(std::move(other));
426 return *this;
427 }
428 SmallVector& operator=(std::initializer_list<T> list) {
429 Base::assign(list);
430 return *this;
431 }
432
433 friend void swap(SmallVector& a, SmallVector& b) { a.swap(b); }
434
435private:
436 alignas(T) std::byte mBuffer[N * sizeof(T)];
437};
438} // namespace ll::data
Definition SmallVector.h:21