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 }
76
78 auto operator=(small_vector const &other) -> small_vector & {
79 if (this != &other) {
80 clear();
81 reserve(other.size());
82 std::ranges::copy(other, std::back_inserter(*this));
83 }
84 return *this;
85 }
87 auto operator=(small_vector &&other) noexcept -> small_vector & {
88 if (this != &other) {
89 clear();
90 if (other.is_small_()) {
91 std::uninitialized_move_n(other.begin_small_(), other.size_small_(), begin_small_());
92 std::ranges::destroy_n(other.begin_small_(), other.size_small_());
93 std::ranges::swap(other.begin_, begin_);
94 } else {
95 std::ranges::swap(begin_, other.begin_);
96 std::ranges::swap(end_large_(), other.end_large_());
97 std::ranges::swap(cap_large_(), other.cap_large_());
98 }
99 }
100 return *this;
101 }
102
104 void resize(size_t n) {
105 if (n <= size()) {
106 erase(begin() + n, end());
107 } else {
108 // Note: could be optimized
109 while (n < size()) {
110 emplace_back();
111 }
112 }
113 }
114
116 [[nodiscard]] auto size() const -> size_t {
117 if (is_small_()) {
118 return size_small_();
119 }
120 return std::ranges::distance(begin_large_(), end_large_());
121 }
122
124 [[nodiscard]] auto empty() const -> bool { return size() == 0; }
125
127 [[nodiscard]] auto capacity() const -> size_t {
128 if (is_small_()) {
129 return N;
130 }
131 return std::ranges::distance(begin_large_(), end_large_());
132 }
133
135 [[nodiscard]] auto begin() -> iterator { return is_small_() ? begin_small_() : begin_large_(); }
136
138 [[nodiscard]] auto begin() const -> const_iterator {
139 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast)
140 return const_cast<small_vector *>(this)->begin();
141 }
142
144 [[nodiscard]] auto cbegin() const -> const_iterator { return begin(); }
145
147 [[nodiscard]] auto end() -> iterator {
148 if (is_small_()) {
149 return std::ranges::next(begin(), size_small_());
150 }
151 return end_large_();
152 }
153
155 [[nodiscard]] auto end() const -> const_iterator {
156 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast)
157 return const_cast<small_vector *>(this)->end();
158 }
159
161 [[nodiscard]] auto cend() const -> const_iterator { return end(); }
162
164 [[nodiscard]] auto data() -> iterator { return begin(); }
165
167 [[nodiscard]] auto data() const -> const_iterator { return cbegin(); }
168
170 [[nodiscard]] auto cdata() const -> const_iterator { return cbegin(); }
171
173 [[nodiscard]] auto operator[](size_t i) -> reference {
174 assert(i < size());
175 return *std::ranges::next(begin(), i);
176 }
177
179 [[nodiscard]] auto operator[](size_t i) const -> const_reference {
180 assert(i < size());
181 return *std::ranges::next(begin(), i);
182 }
183
185 void reserve(size_t n) {
186 if (auto m = capacity(); m < n) {
187 if (n < 2 * m) {
188 n = 2 * m;
189 }
190 auto l = size();
191 auto *data = ::operator new[](sizeof(value_type) * n);
192 std::uninitialized_move(begin(), end(), static_cast<pointer>(data));
193 destroy_();
194 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
195 begin_ = reinterpret_cast<uintptr_t>(data);
196 end_large_() = std::ranges::next(begin_large_(), l);
197 cap_large_() = std::ranges::next(begin_large_(), n);
198 }
199 }
200
202 auto front() -> reference {
203 assert(!empty());
204 return *begin();
205 }
206
208 auto front() const -> const_reference {
209 assert(!empty());
210 return *begin();
211 }
212
214 auto back() -> reference {
215 assert(!empty());
216 return *std::ranges::prev(end());
217 }
218
220 auto back() const -> const_reference {
221 assert(!empty());
222 return *std::ranges::prev(end());
223 }
224
226 auto erase(iterator it) -> iterator {
227 assert(it != end());
228 std::ranges::destroy_at(std::move(std::ranges::next(it), end(), it));
229 if (is_small_()) {
230 size_small_(size_small_() - 1);
231 } else {
232 std::ranges::advance(end_large_(), -1);
233 }
234 return it;
235 }
236
238 auto erase(iterator first, iterator last) -> iterator {
239 assert(first <= last);
240 if (auto n = std::ranges::distance(first, last); n > 0) {
241 std::ranges::destroy(std::move(last, end(), first), end());
242 if (is_small_()) {
243 size_small_(size_small_() - n);
244 } else {
245 std::ranges::advance(end_large_(), -n);
246 std::ranges::advance(last, -n);
247 }
248 }
249 return last;
250 }
251
253 template <class... U> void emplace(const_iterator loc, U &&...args) {
254 // TODO: moves can be avoided when reallocating
255 auto i = std::ranges::distance(cbegin(), loc);
256 reserve(size() + 1);
257 auto it = std::next(begin(), i);
258 if (auto ie = end(), ip = std::ranges::prev(ie); it != ie) {
259 std::ranges::construct_at(ie, std::move(*ip));
260 std::ranges::move_backward(it, ip, ie);
261 }
262 std::ranges::construct_at(it, std::forward<U>(args)...);
263 if (is_small_()) {
264 size_small_(size_small_() + 1);
265 } else {
266 std::ranges::advance(end_large_(), 1);
267 }
268 }
269
271 template <class... U> void emplace_back(U &&...args) {
272 reserve(size() + 1);
273 std::ranges::construct_at(end(), std::forward<U>(args)...);
274 if (is_small_()) {
275 size_small_(size_small_() + 1);
276 } else {
277 std::advance(end_large_(), 1);
278 }
279 }
280
282 void push_back(value_type const &x) { return emplace_back(x); }
283
285 void pop_back() {
286 assert(!empty());
287 std::destroy_at(std::prev(end()));
288 if (is_small_()) {
289 size_small_(size_small_() - 1);
290 } else {
291 std::advance(end_large_(), -1);
292 }
293 }
294
298 void clear() noexcept {
299 destroy_();
300 begin_ = 1;
301 }
302
304 ~small_vector() noexcept { destroy_(); }
305
306 private:
307 [[nodiscard]] auto begin_large_() const -> pointer {
308 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
309 return reinterpret_cast<T *>(begin_);
310 }
311 [[nodiscard]] auto end_large_() -> pointer & {
312 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access)
313 return end_;
314 }
315 [[nodiscard]] auto end_large_() const -> pointer {
316 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access)
317 return end_;
318 }
319 [[nodiscard]] auto cap_large_() const -> pointer {
320 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access)
321 return cap_;
322 }
323 [[nodiscard]] auto cap_large_() -> pointer & {
324 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access)
325 return cap_;
326 }
327 [[nodiscard]] auto begin_small_() -> pointer {
328 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast,cppcoreguidelines-pro-type-union-access)
329 return reinterpret_cast<T *>(buf_);
330 }
331 [[nodiscard]] auto is_small_() const -> bool { return (begin_ & 1) != 0; }
332 [[nodiscard]] auto size_small_() const -> size_t { return begin_ >> 1; }
333 [[nodiscard]] auto size_small_(size_t n) { begin_ = (n << 1) | 1; }
334
335 void destroy_() noexcept {
336 std::destroy(begin(), end());
337 if (!is_small_()) {
338 ::operator delete[](begin_large_());
339 }
340 }
341
342 friend auto operator<=>(small_vector const &lhs, small_vector const &rhs) {
343 // return std::ranges::lexicographical_compare_three_way(lhs, rhs);
344 return std::lexicographical_compare_three_way(lhs.begin(), lhs.end(), rhs.begin(), rhs.end());
345 }
346
347 friend auto operator==(small_vector const &lhs, small_vector const &rhs) { return std::ranges::equal(lhs, rhs); }
348
349 CLINGO_IGNORE_UNION_B
350 uintptr_t begin_ = 1;
351 union {
352 struct {
353 // NOLINTNEXTLINE(modernize-avoid-c-arrays)
354 alignas(value_type) unsigned char buf_[N * sizeof(value_type)];
355 };
356 struct {
357 pointer end_;
358 pointer cap_;
359 };
360 };
361 CLINGO_IGNORE_UNION_E
362};
363
365
366} // 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:161
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:144
void pop_back()
Pop the last element.
Definition small_vector.hh:285
void reserve(size_t n)
Reserve space for at least n elements.
Definition small_vector.hh:185
auto begin() const -> const_iterator
Get a const iterator to the beginning of the vector.
Definition small_vector.hh:138
~small_vector() noexcept
Deconstruct the vector.
Definition small_vector.hh:304
auto front() -> reference
Get the first element in the vector.
Definition small_vector.hh:202
auto erase(iterator first, iterator last) -> iterator
Erase the given range of elements.
Definition small_vector.hh:238
void resize(size_t n)
Resize to the given size.
Definition small_vector.hh:104
void push_back(value_type const &x)
Append an element after the last element.
Definition small_vector.hh:282
auto capacity() const -> size_t
Get the capacity of the vector.
Definition small_vector.hh:127
auto data() const -> const_iterator
Get a const pointer to the stored C array.
Definition small_vector.hh:167
auto operator=(small_vector &&other) noexcept -> small_vector &
Move assign the vector.
Definition small_vector.hh:87
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:208
auto operator[](size_t i) const -> const_reference
Get the element at the given index.
Definition small_vector.hh:179
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:155
void emplace(const_iterator loc, U &&...args)
Emplace an element before the given iterator.
Definition small_vector.hh:253
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:135
auto data() -> iterator
Get a pointer to the stored C array.
Definition small_vector.hh:164
void emplace_back(U &&...args)
Emplace an element after the last element.
Definition small_vector.hh:271
auto end() -> iterator
Get an iterator to the end of the vector.
Definition small_vector.hh:147
auto back() -> reference
Get the last element in the vector.
Definition small_vector.hh:214
auto operator[](size_t i) -> reference
Get the element at the given index.
Definition small_vector.hh:173
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:78
auto erase(iterator it) -> iterator
Erase the given element.
Definition small_vector.hh:226
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:220
auto empty() const -> bool
Check if the vector is empty.
Definition small_vector.hh:124
auto size() const -> size_t
Get the size of the vector.
Definition small_vector.hh:116
auto cdata() const -> const_iterator
Get a const pointer to the stored C array.
Definition small_vector.hh:170
void clear() noexcept
Clear the vector.
Definition small_vector.hh:298