Clingo
Loading...
Searching...
No Matches
small_vector.hh
1#pragma once
2
3#include <algorithm>
4#include <clingo/util/macro.hh>
5
6#include <cassert>
7#include <cstdint>
8#include <iterator>
9#include <memory>
10#include <utility>
11
12namespace CppClingo::Util {
13
16
23template <class T, size_t N = 2>
24 requires(std::is_nothrow_destructible_v<T> && std::is_nothrow_move_constructible_v<T>)
25// NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init)
27 public:
29 using value_type = T;
31 using iterator = T *;
33 using const_iterator = T const *;
35 using reference = T &;
37 using const_reference = T const &;
39 using pointer = T *;
41 using const_pointer = T const *;
42
44 small_vector() = default;
47 reserve(other.size());
48 std::ranges::copy(other, std::back_inserter(*this));
49 }
51 small_vector(small_vector &&other) noexcept : small_vector{} { *this = std::move(other); }
52
54 template <std::input_iterator It, std::sentinel_for<It> Is> small_vector(It begin, Is end) : small_vector{} {
55 assign(begin, end);
56 }
57
59 template <std::input_iterator It, std::sentinel_for<It> Is> void assign(It first, Is last) {
60 clear();
61 if constexpr (std::sized_sentinel_for<Is, It>) {
62 size_t n = std::ranges::distance(first, last);
63 reserve(n);
64 auto ib = begin();
65 auto ie = std::next(ib, n);
66 std::ranges::uninitialized_copy(first, last, ib, ie);
67 if (is_small_()) {
68 size_small_(n);
69 } else {
70 end_large_() = ie;
71 }
72 } else {
73 std::ranges::copy(first, last, std::back_inserter(*this));
74 }
75 assert(check_());
76 }
77
79 auto operator=(small_vector const &other) -> small_vector & {
80 if (this != &other) {
81 clear();
82 reserve(other.size());
83 std::ranges::copy(other, std::back_inserter(*this));
84 }
85 return *this;
86 }
88 auto operator=(small_vector &&other) noexcept -> small_vector & {
89 if (this != &other) {
90 clear();
91 if (other.is_small_()) {
92 std::uninitialized_move_n(other.begin_small_(), other.size_small_(), begin_small_());
93 std::ranges::destroy_n(other.begin_small_(), other.size_small_());
94 std::ranges::swap(other.begin_, begin_);
95 } else {
96 std::ranges::swap(begin_, other.begin_);
97 std::ranges::swap(end_large_(), other.end_large_());
98 std::ranges::swap(cap_large_(), other.cap_large_());
99 }
100 }
101 assert(check_());
102 return *this;
103 }
104
106 void resize(size_t n) {
107 if (n <= size()) {
108 erase(begin() + n, end());
109 } else {
110 // Note: could be optimized
111 while (n < size()) {
112 emplace_back();
113 }
114 }
115 }
116
122 [[nodiscard]] auto size() const -> size_t {
123 if (is_small_()) {
124 return size_small_();
125 }
126 return std::ranges::distance(begin_large_(), end_large_());
127 }
128
130 [[nodiscard]] auto empty() const -> bool { return size() == 0; }
131
137 [[nodiscard]] auto capacity() const -> size_t {
138 if (is_small_()) {
139 return N;
140 }
141 return std::ranges::distance(begin_large_(), cap_large_());
142 }
143
145 [[nodiscard]] auto begin() -> iterator { return is_small_() ? begin_small_() : begin_large_(); }
146
148 [[nodiscard]] auto begin() const -> const_iterator {
149 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast)
150 return const_cast<small_vector *>(this)->begin();
151 }
152
154 [[nodiscard]] auto cbegin() const -> const_iterator { return begin(); }
155
157 [[nodiscard]] auto end() -> iterator {
158 if (is_small_()) {
159 return std::ranges::next(begin(), size_small_());
160 }
161 return end_large_();
162 }
163
165 [[nodiscard]] auto end() const -> const_iterator {
166 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast)
167 return const_cast<small_vector *>(this)->end();
168 }
169
171 [[nodiscard]] auto cend() const -> const_iterator { return end(); }
172
174 [[nodiscard]] auto data() -> iterator { return begin(); }
175
177 [[nodiscard]] auto data() const -> const_iterator { return cbegin(); }
178
180 [[nodiscard]] auto cdata() const -> const_iterator { return cbegin(); }
181
183 [[nodiscard]] auto operator[](size_t i) -> reference {
184 assert(i < size());
185 return *std::ranges::next(begin(), i);
186 }
187
189 [[nodiscard]] auto operator[](size_t i) const -> const_reference {
190 assert(i < size());
191 return *std::ranges::next(begin(), i);
192 }
193
195 void reserve(size_t n) {
196 if (auto m = capacity(); m < n) {
197 if (n < 2 * m) {
198 n = 2 * m;
199 }
200 auto l = size();
201 auto *data = ::operator new[](sizeof(value_type) * n);
202 std::uninitialized_move(begin(), end(), static_cast<pointer>(data));
203 destroy_();
204 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
205 begin_ = reinterpret_cast<uintptr_t>(data);
206 end_large_() = std::ranges::next(begin_large_(), l);
207 cap_large_() = std::ranges::next(begin_large_(), n);
208 }
209 assert(check_());
210 }
211
213 auto front() -> reference {
214 assert(!empty());
215 return *begin();
216 }
217
219 auto front() const -> const_reference {
220 assert(!empty());
221 return *begin();
222 }
223
225 auto back() -> reference {
226 assert(!empty());
227 return *std::ranges::prev(end());
228 }
229
231 auto back() const -> const_reference {
232 assert(!empty());
233 return *std::ranges::prev(end());
234 }
235
237 auto erase(iterator it) -> iterator {
238 assert(it != end());
239 std::ranges::destroy_at(std::move(std::ranges::next(it), end(), it));
240 if (is_small_()) {
241 size_small_(size_small_() - 1);
242 } else {
243 std::ranges::advance(end_large_(), -1);
244 }
245 assert(check_());
246 return it;
247 }
248
250 auto erase(iterator first, iterator last) -> iterator {
251 assert(first <= last);
252 if (auto n = std::ranges::distance(first, last); n > 0) {
253 std::ranges::destroy(std::move(last, end(), first), end());
254 if (is_small_()) {
255 size_small_(size_small_() - n);
256 } else {
257 std::ranges::advance(end_large_(), -n);
258 std::ranges::advance(last, -n);
259 }
260 }
261 assert(check_());
262 return last;
263 }
264
266 template <class... U> void emplace(const_iterator loc, U &&...args) {
267 // TODO: moves can be avoided when reallocating
268 auto i = std::ranges::distance(cbegin(), loc);
269 reserve(size() + 1);
270 auto it = std::next(begin(), i);
271 if (auto ie = end(), ip = std::ranges::prev(ie); it != ie) {
272 std::ranges::construct_at(ie, std::move(*ip));
273 std::ranges::move_backward(it, ip, ie);
274 }
275 std::ranges::construct_at(it, std::forward<U>(args)...);
276 if (is_small_()) {
277 size_small_(size_small_() + 1);
278 } else {
279 std::ranges::advance(end_large_(), 1);
280 }
281 assert(check_());
282 }
283
285 template <class... U> void emplace_back(U &&...args) {
286 reserve(size() + 1);
287 std::ranges::construct_at(end(), std::forward<U>(args)...);
288 if (is_small_()) {
289 size_small_(size_small_() + 1);
290 } else {
291 std::advance(end_large_(), 1);
292 }
293 assert(check_());
294 }
295
297 void push_back(value_type const &x) { return emplace_back(x); }
298
300 void pop_back() {
301 assert(!empty());
302 std::destroy_at(std::prev(end()));
303 if (is_small_()) {
304 size_small_(size_small_() - 1);
305 } else {
306 std::advance(end_large_(), -1);
307 }
308 assert(check_());
309 }
310
314 void clear() noexcept {
315 destroy_();
316 begin_ = 1;
317 assert(check_());
318 }
319
321 ~small_vector() noexcept { destroy_(); }
322
323 private:
324 [[nodiscard]] auto begin_large_() const -> pointer {
325 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
326 return reinterpret_cast<T *>(begin_);
327 }
328 [[nodiscard]] auto end_large_() -> pointer & {
329 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access)
330 return end_;
331 }
332 [[nodiscard]] auto end_large_() const -> pointer {
333 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access)
334 return end_;
335 }
336 [[nodiscard]] auto cap_large_() const -> pointer {
337 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access)
338 return cap_;
339 }
340 [[nodiscard]] auto cap_large_() -> pointer & {
341 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access)
342 return cap_;
343 }
344 [[nodiscard]] auto begin_small_() -> pointer {
345 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast,cppcoreguidelines-pro-type-union-access)
346 return reinterpret_cast<T *>(buf_);
347 }
348 [[nodiscard]] auto is_small_() const -> bool { return (begin_ & 1) != 0; }
349 [[nodiscard]] auto size_small_() const -> size_t { return begin_ >> 1; }
350 [[nodiscard]] auto size_small_(size_t n) { begin_ = (n << 1) | 1; }
351
352#ifndef NDEBUG
354 [[nodiscard]] auto check_() const -> bool {
355 if (is_small_()) {
356 return size_small_() <= N;
357 }
358 return begin_large_() <= end_large_() && end_large_() <= cap_large_();
359 }
360#endif
361
362 void destroy_() noexcept {
363 std::destroy(begin(), end());
364 if (!is_small_()) {
365 ::operator delete[](begin_large_());
366 }
367 }
368
369 friend auto operator<=>(small_vector const &lhs, small_vector const &rhs) {
370 // return std::ranges::lexicographical_compare_three_way(lhs, rhs);
371 return std::lexicographical_compare_three_way(lhs.begin(), lhs.end(), rhs.begin(), rhs.end());
372 }
373
374 friend auto operator==(small_vector const &lhs, small_vector const &rhs) { return std::ranges::equal(lhs, rhs); }
375
376 CLINGO_IGNORE_UNION_B
377 uintptr_t begin_ = 1;
378 union {
379 struct {
380 // NOLINTNEXTLINE(modernize-avoid-c-arrays)
381 alignas(value_type) unsigned char buf_[N * sizeof(value_type)];
382 };
383 struct {
384 pointer end_;
385 pointer cap_;
386 };
387 };
388 CLINGO_IGNORE_UNION_E
389};
390
392
393} // namespace CppClingo::Util
A vector that misuses the begin, end and capacity pointers to store elements.
Definition small_vector.hh:26
T const * const_pointer
The const pointer type.
Definition small_vector.hh:41
auto cend() const -> const_iterator
Get a const iterator to the end of the vector.
Definition small_vector.hh:171
void assign(It first, Is last)
Assign the vector.
Definition small_vector.hh:59
auto cbegin() const -> const_iterator
Get a const iterator to the beginning of the vector.
Definition small_vector.hh:154
void pop_back()
Pop the last element.
Definition small_vector.hh:300
void reserve(size_t n)
Reserve space for at least n elements.
Definition small_vector.hh:195
auto begin() const -> const_iterator
Get a const iterator to the beginning of the vector.
Definition small_vector.hh:148
~small_vector() noexcept
Deconstruct the vector.
Definition small_vector.hh:321
auto front() -> reference
Get the first element in the vector.
Definition small_vector.hh:213
auto erase(iterator first, iterator last) -> iterator
Erase the given range of elements.
Definition small_vector.hh:250
void resize(size_t n)
Resize to the given size.
Definition small_vector.hh:106
void push_back(value_type const &x)
Append an element after the last element.
Definition small_vector.hh:297
auto capacity() const -> size_t
Get the capacity of the vector.
Definition small_vector.hh:137
auto data() const -> const_iterator
Get a const pointer to the stored C array.
Definition small_vector.hh:177
auto operator=(small_vector &&other) noexcept -> small_vector &
Move assign the vector.
Definition small_vector.hh:88
T & reference
The reference type.
Definition small_vector.hh:35
auto front() const -> const_reference
Get the first element in the vector.
Definition small_vector.hh:219
auto operator[](size_t i) const -> const_reference
Get the element at the given index.
Definition small_vector.hh:189
T value_type
The value type.
Definition small_vector.hh:29
small_vector(It begin, Is end)
Initialize vector from an iterator range.
Definition small_vector.hh:54
small_vector()=default
Construct an empty vector.
auto end() const -> const_iterator
Get a const iterator to the end of the vector.
Definition small_vector.hh:165
void emplace(const_iterator loc, U &&...args)
Emplace an element before the given iterator.
Definition small_vector.hh:266
T const & const_reference
The const reference type.
Definition small_vector.hh:37
T const * const_iterator
The const iterator type.
Definition small_vector.hh:33
auto begin() -> iterator
Get an iterator to the beginning of the vector.
Definition small_vector.hh:145
auto data() -> iterator
Get a pointer to the stored C array.
Definition small_vector.hh:174
void emplace_back(U &&...args)
Emplace an element after the last element.
Definition small_vector.hh:285
auto end() -> iterator
Get an iterator to the end of the vector.
Definition small_vector.hh:157
auto back() -> reference
Get the last element in the vector.
Definition small_vector.hh:225
auto operator[](size_t i) -> reference
Get the element at the given index.
Definition small_vector.hh:183
small_vector(small_vector const &other)
Copy construct the vector.
Definition small_vector.hh:46
T * pointer
The pointer type.
Definition small_vector.hh:39
auto operator=(small_vector const &other) -> small_vector &
Copy assign the vector.
Definition small_vector.hh:79
auto erase(iterator it) -> iterator
Erase the given element.
Definition small_vector.hh:237
small_vector(small_vector &&other) noexcept
Move construct the vector.
Definition small_vector.hh:51
T * iterator
The iterator type.
Definition small_vector.hh:31
auto back() const -> const_reference
Get the last element in the vector.
Definition small_vector.hh:231
auto empty() const -> bool
Check if the vector is empty.
Definition small_vector.hh:130
auto size() const -> size_t
Get the size of the vector.
Definition small_vector.hh:122
auto cdata() const -> const_iterator
Get a const pointer to the stored C array.
Definition small_vector.hh:180
void clear() noexcept
Clear the vector.
Definition small_vector.hh:314