Aleph-w 3.0
A C++ Library for Data Structures and Algorithms
Loading...
Searching...
No Matches
tpl_small_vector.H
Go to the documentation of this file.
1
2/*
3 Aleph_w
4
5 Data structures & Algorithms
6 https://github.com/lrleon/Aleph-w
7
8 This file is part of Aleph-w library
9
10 Copyright (c) 2002-2026 Leandro Rabindranath Leon
11
12 Permission is hereby granted, free of charge, to any person obtaining a copy
13 of this software and associated documentation files (the "Software"), to deal
14 in the Software without restriction, including without limitation the rights
15 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
16 copies of the Software, and to permit persons to whom the Software is
17 furnished to do so, subject to the following conditions:
18
19 The above copyright notice and this permission notice shall be included in all
20 copies or substantial portions of the Software.
21
22 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
23 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
25 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
27 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
28 SOFTWARE.
29*/
30
59#ifndef TPL_SMALL_VECTOR_H
60#define TPL_SMALL_VECTOR_H
61
62#include <cstddef>
63#include <initializer_list>
64#include <iterator>
65#include <limits>
66#include <memory>
67#include <new>
68#include <type_traits>
69#include <utility>
70
71#include <ah-errors.H>
72
73namespace Aleph {
74
116template <typename T, size_t N = 8>
118{
119 static_assert(N > 0, "SmallVector requires a positive inline capacity");
120 static_assert(std::is_move_constructible_v<T>,
121 "SmallVector requires a move-constructible element type");
122
123 // Raw inline storage for the first N elements. The buffer is aligned as T so
124 // placement-new can construct T objects here without a separate heap allocation.
125 alignas(T) std::byte storage_[N * sizeof(T)];
127 size_t n_ = 0;
128 size_t cap_ = N;
129
131 {
132 return reinterpret_cast<T *>(storage_);
133 }
134
136 {
137 return reinterpret_cast<const T *>(storage_);
138 }
139
140 static T *allocate(size_t m)
141 {
142 return std::allocator<T>().allocate(m);
143 }
144
145 static void deallocate(T *p, size_t m) noexcept
146 {
147 std::allocator<T>().deallocate(p, m);
148 }
149
150 // Moves the elements into a fresh heap buffer of capacity new_cap
151 // (strong guarantee via move_if_noexcept) and releases the old buffer.
152 void relocate(const size_t new_cap)
153 {
154 T *np = allocate(new_cap);
155 size_t i = 0;
156 try
157 {
158 for (; i < n_; ++i)
159 ::new (static_cast<void *>(np + i)) T(std::move_if_noexcept(ptr_[i]));
160 }
161 catch (...)
162 {
163 std::destroy(np, np + i);
165 throw;
166 }
167 std::destroy(ptr_, ptr_ + n_);
168 if (not is_small())
170 ptr_ = np;
171 cap_ = new_cap;
172 }
173
174 // Ensures room for at least min_cap elements, growing geometrically.
175 void grow(const size_t min_cap)
176 {
177 size_t new_cap = cap_;
178 while (new_cap < min_cap)
179 {
180 ah_overflow_error_if(new_cap > (static_cast<size_t>(-1) >> 1))
181 << "SmallVector::grow(): capacity overflow";
182 new_cap <<= 1;
183 }
185 }
186
187 // Takes over o's contents. *this must be empty and inline.
188 void steal(SmallVector &&o) noexcept(std::is_nothrow_move_constructible_v<T>)
189 {
190 if (o.is_small())
191 {
192 if constexpr (std::is_nothrow_move_constructible_v<T>)
193 {
194 for (; n_ < o.n_; ++n_)
195 ::new (static_cast<void *>(ptr_ + n_)) T(std::move(o.ptr_[n_]));
196 }
197 else
198 {
199 try
200 {
201 for (; n_ < o.n_; ++n_)
202 ::new (static_cast<void *>(ptr_ + n_)) T(std::move(o.ptr_[n_]));
203 }
204 catch (...)
205 {
206 release();
207 throw;
208 }
209 }
210 std::destroy(o.ptr_, o.ptr_ + o.n_);
211 o.n_ = 0;
212 }
213 else
214 {
215 ptr_ = o.ptr_;
216 cap_ = o.cap_;
217 n_ = o.n_;
218 o.ptr_ = o.inline_ptr();
219 o.cap_ = N;
220 o.n_ = 0;
221 }
222 }
223
224 // Destroys all elements and releases the heap buffer, back to inline.
226 {
227 std::destroy(ptr_, ptr_ + n_);
228 if (not is_small())
230 ptr_ = inline_ptr();
231 cap_ = N;
232 n_ = 0;
233 }
234
235public:
236 using Item_Type = T;
237 using value_type = T;
238 using size_type = size_t;
239 using iterator = T *;
240 using const_iterator = const T *;
241
244
251 SmallVector(const size_t n, const T &value)
252 requires std::is_copy_constructible_v<T>
253 : ptr_(inline_ptr())
254 {
255 if (n > cap_)
256 grow(n);
257 try
258 {
259 for (; n_ < n; ++n_)
260 ::new (static_cast<void *>(ptr_ + n_)) T(value);
261 }
262 catch (...)
263 {
264 release();
265 throw;
266 }
267 }
268
273 SmallVector(std::initializer_list<T> l)
274 requires std::is_copy_constructible_v<T>
275 : ptr_(inline_ptr())
276 {
277 if (l.size() > cap_)
278 grow(l.size());
279 try
280 {
281 for (const T &x : l)
282 {
283 ::new (static_cast<void *>(ptr_ + n_)) T(x);
284 ++n_;
285 }
286 }
287 catch (...)
288 {
289 release();
290 throw;
291 }
292 }
293
300 template <std::input_iterator It>
301 SmallVector(It first, It last) : ptr_(inline_ptr())
302 {
303 try
304 {
305 for (; first != last; ++first)
306 append(*first);
307 }
308 catch (...)
309 {
310 release();
311 throw;
312 }
313 }
314
317 requires std::is_copy_constructible_v<T>
318 : ptr_(inline_ptr())
319 {
320 if (v.n_ > cap_)
321 grow(v.n_);
322 try
323 {
324 for (; n_ < v.n_; ++n_)
325 ::new (static_cast<void *>(ptr_ + n_)) T(v.ptr_[n_]);
326 }
327 catch (...)
328 {
329 release();
330 throw;
331 }
332 }
333
339 SmallVector(SmallVector &&v) noexcept(std::is_nothrow_move_constructible_v<T>)
340 : ptr_(inline_ptr())
341 {
342 steal(std::move(v));
343 }
344
347 requires std::is_copy_constructible_v<T>
348 {
349 if (this == &v)
350 return *this;
351 release();
352 if (v.n_ > cap_)
353 grow(v.n_);
354 for (; n_ < v.n_; ++n_)
355 ::new (static_cast<void *>(ptr_ + n_)) T(v.ptr_[n_]);
356 return *this;
357 }
358
360 SmallVector &operator = (SmallVector &&v) noexcept(std::is_nothrow_move_constructible_v<T>)
361 {
362 if (this == &v)
363 return *this;
364 release();
365 steal(std::move(v));
366 return *this;
367 }
368
370 {
371 release();
372 }
373
381 void swap(SmallVector &v) noexcept(std::is_nothrow_move_constructible_v<T>)
382 {
383 if (not is_small() and not v.is_small())
384 {
385 std::swap(ptr_, v.ptr_);
386 std::swap(n_, v.n_);
387 std::swap(cap_, v.cap_);
388 return;
389 }
390 SmallVector tmp(std::move(*this));
391 *this = std::move(v);
392 v = std::move(tmp);
393 }
394
395 // -- capacity -------------------------------------------------------------
396
399 {
400 return n_;
401 }
402
405 {
406 return cap_;
407 }
408
411 {
412 return n_ == 0;
413 }
414
417 {
418 return ptr_ == inline_ptr();
419 }
420
425 void reserve(const size_t cap)
426 {
427 if (cap > cap_)
428 grow(cap);
429 }
430
436 {
437 std::destroy(ptr_, ptr_ + n_);
438 n_ = 0;
439 }
440
443 {
444 empty();
445 }
446
447 // -- element access -------------------------------------------------------
448
454 [[nodiscard]] T &operator [] (size_t i)
455 {
456 ah_out_of_range_error_if(i >= n_) << "SmallVector: index out of range";
457 return ptr_[i];
458 }
459
461 [[nodiscard]] const T &operator [] (size_t i) const
462 {
463 ah_out_of_range_error_if(i >= n_) << "SmallVector: index out of range";
464 return ptr_[i];
465 }
466
468 [[nodiscard]] T &operator () (size_t i) noexcept
469 {
470 return ptr_[i];
471 }
472
474 [[nodiscard]] const T &operator () (size_t i) const noexcept
475 {
476 return ptr_[i];
477 }
478
484 {
485 ah_underflow_error_if(n_ == 0) << "SmallVector::get_first(): empty";
486 return ptr_[0];
487 }
488
490 [[nodiscard]] const T &get_first() const
491 {
492 ah_underflow_error_if(n_ == 0) << "SmallVector::get_first(): empty";
493 return ptr_[0];
494 }
495
501 {
502 ah_underflow_error_if(n_ == 0) << "SmallVector::get_last(): empty";
503 return ptr_[n_ - 1];
504 }
505
507 [[nodiscard]] const T &get_last() const
508 {
509 ah_underflow_error_if(n_ == 0) << "SmallVector::get_last(): empty";
510 return ptr_[n_ - 1];
511 }
512
515 {
516 return ptr_;
517 }
518
521 {
522 return ptr_;
523 }
524
525 // -- modifiers ------------------------------------------------------------
526
535 template <class... Args>
537 {
538 if (n_ == cap_)
539 {
540 T tmp(std::forward<Args>(args)...);
541 grow(n_ + 1);
542 ::new (static_cast<void *>(ptr_ + n_)) T(std::move(tmp));
543 }
544 else
545 ::new (static_cast<void *>(ptr_ + n_)) T(std::forward<Args>(args)...);
546 return ptr_[n_++];
547 }
548
555 T &append(const T &item)
556 requires std::is_copy_constructible_v<T>
557 {
558 return emplace_back(item);
559 }
560
567 T &append(T &&item)
568 {
569 return emplace_back(std::move(item));
570 }
571
613 void append_range(const T *first, const size_t count)
614 requires std::is_copy_constructible_v<T>
615 {
616 if (count == 0)
617 return;
618 ah_invalid_argument_if(first == nullptr)
619 << "SmallVector::append_range(): null source pointer";
620 // Checked *before* computing `n_ + count`, not after: for an
621 // adversarial or mistaken huge `count`, the raw sum can wrap around
622 // `size_t` and come out `<= cap_`, which would skip the `grow()`
623 // call below entirely and then `memcpy` (or the placement-new loop)
624 // straight past the end of the real, too-small buffer.
625 ah_overflow_error_if(count > std::numeric_limits<size_t>::max() - n_)
626 << "SmallVector::append_range(): count overflows size_t when added "
627 "to the current size";
628 if (n_ + count > cap_)
629 grow(n_ + count);
630 if constexpr (std::is_trivially_copyable_v<T>)
631 {
632 // std::uninitialized_copy_n, not a raw memcpy: copying the object
633 // representation into storage that holds no object yet only
634 // implicitly starts a `T` object's lifetime there (C++20
635 // [basic.types.general]) when `T` is an *implicit-lifetime*
636 // type -- trivially copyable does not by itself guarantee that
637 // (e.g. a type whose only non-deleted special member is a
638 // trivial assignment operator, with every constructor deleted,
639 // is trivially copyable but not implicit-lifetime). Standard
640 // library implementations detect the trivially-copyable case
641 // and lower this to the same memcpy, so this costs nothing.
642 std::uninitialized_copy_n(first, count, ptr_ + n_);
643 n_ += count;
644 }
645 else
646 {
647 size_t i = 0;
648 try
649 {
650 for (; i < count; ++i)
651 ::new (static_cast<void *>(ptr_ + n_ + i)) T(first[i]);
652 }
653 catch (...)
654 {
655 std::destroy(ptr_ + n_, ptr_ + n_ + i);
656 throw;
657 }
658 n_ += count;
659 }
660 }
661
663 T &push_back(const T &item)
664 requires std::is_copy_constructible_v<T>
665 {
666 return append(item);
667 }
668
670 T &push_back(T &&item)
671 {
672 return append(std::move(item));
673 }
674
681 {
682 ah_underflow_error_if(n_ == 0) << "SmallVector::remove_last(): empty";
683 T ret = std::move(ptr_[n_ - 1]);
684 std::destroy_at(ptr_ + --n_);
685 return ret;
686 }
687
692 void pop_back()
693 {
694 ah_underflow_error_if(n_ == 0) << "SmallVector::pop_back(): empty";
695 std::destroy_at(ptr_ + --n_);
696 }
697
706 T &insert(size_t pos, T item)
707 requires std::is_move_assignable_v<T>
708 {
709 ah_out_of_range_error_if(pos > n_) << "SmallVector::insert(): position out of range";
710 if (n_ == cap_)
711 grow(n_ + 1);
712 if (pos == n_)
713 {
714 ::new (static_cast<void *>(ptr_ + n_)) T(std::move(item));
715 return ptr_[n_++];
716 }
717 ::new (static_cast<void *>(ptr_ + n_)) T(std::move(ptr_[n_ - 1]));
718 for (size_t i = n_ - 1; i > pos; --i)
719 ptr_[i] = std::move(ptr_[i - 1]);
720 ptr_[pos] = std::move(item);
721 ++n_;
722 return ptr_[pos];
723 }
724
730 void erase(const size_t pos)
731 requires std::is_move_assignable_v<T>
732 {
733 ah_out_of_range_error_if(pos >= n_) << "SmallVector::erase(): position out of range";
734 for (size_t i = pos; i + 1 < n_; ++i)
735 ptr_[i] = std::move(ptr_[i + 1]);
736 std::destroy_at(ptr_ + --n_);
737 }
738
739 // -- iteration ------------------------------------------------------------
740
743 {
744 return ptr_;
745 }
746
749 {
750 return ptr_ + n_;
751 }
752
755 {
756 return ptr_;
757 }
758
761 {
762 return ptr_ + n_;
763 }
764
767 {
768 return ptr_;
769 }
770
773 {
774 return ptr_ + n_;
775 }
776
786 template <class Operation>
788 {
789 for (size_t i = 0; i < n_; ++i)
790 if (not operation(ptr_[i]))
791 return false;
792 return true;
793 }
794
800 template <class Operation>
802 {
803 for (size_t i = 0; i < n_; ++i)
804 if (not operation(ptr_[i]))
805 return false;
806 return true;
807 }
808
809 // -- comparison -----------------------------------------------------------
810
816 template <size_t M>
817 bool operator == (const SmallVector<T, M> &v) const
818 {
819 if (n_ != v.size())
820 return false;
821 for (size_t i = 0; i < n_; ++i)
822 if (not (ptr_[i] == v(i)))
823 return false;
824 return true;
825 }
826
828 template <size_t M>
829 bool operator != (const SmallVector<T, M> &v) const
830 {
831 return not (*this == v);
832 }
833};
834
835} // end namespace Aleph
836
837#endif // TPL_SMALL_VECTOR_H
Exception handling system with formatted messages for Aleph-w.
#define ah_out_of_range_error_if(C)
Throws std::out_of_range if condition holds.
Definition ah-errors.H:584
#define ah_underflow_error_if(C)
Throws std::underflow_error if condition holds.
Definition ah-errors.H:373
#define ah_overflow_error_if(C)
Throws std::overflow_error if condition holds.
Definition ah-errors.H:468
#define ah_invalid_argument_if(C)
Throws std::invalid_argument if condition holds.
Definition ah-errors.H:644
size_t size_t int32_t value
Definition ca-c-api.h:116
Generic filter iterator wrapper.
size_t size() const noexcept
Count the number of elements of the list.
Definition htlist.H:1065
Contiguous dynamic array with N elements of inline storage.
const_iterator end() const noexcept
Const iterator past the last element. O(1).
void relocate(const size_t new_cap)
static void deallocate(T *p, size_t m) noexcept
T & push_back(const T &item)
STL-style alias of append(const T &).
size_t size_type
STL convention: size type.
size_t capacity() const noexcept
Return the current capacity (inline or heap). O(1).
size_t size() const noexcept
Return the number of stored elements. O(1).
void reserve(const size_t cap)
Reserve capacity for at least cap elements.
T & get_last()
Last element (checked).
const T * const_iterator
Random-access const iterator.
SmallVector(It first, It last)
Construct from an iterator range.
SmallVector(const SmallVector &v)
Copy constructor (requires copyable T).
T & emplace_back(Args &&...args)
Construct an element in place at the end.
const_iterator cend() const noexcept
Const iterator past the last element. O(1).
const_iterator begin() const noexcept
Const iterator to the first element. O(1).
void append_range(const T *first, const size_t count)
Append count copies from [first, first + count), in order.
T & append(const T &item)
Append a copy of item.
SmallVector() noexcept
Construct an empty vector using the inline storage. Never allocates.
void empty() noexcept
Destroy all elements (Aleph convention).
iterator end() noexcept
Iterator past the last element. O(1).
void steal(SmallVector &&o) noexcept(std::is_nothrow_move_constructible_v< T >)
bool traverse(Operation operation) const
Const traversal in order while operation returns true.
SmallVector(const size_t n, const T &value)
Construct with n copies of value.
static T * allocate(size_t m)
T * data() noexcept
Pointer to the contiguous element storage. O(1).
T & operator()(size_t i) noexcept
Unchecked access to the i-th element (must be < size()).
bool traverse(Operation operation)
Traverse elements in order while operation returns true.
void swap(SmallVector &v) noexcept(std::is_nothrow_move_constructible_v< T >)
Swap contents with v.
const T & get_first() const
bool is_small() const noexcept
Return true while the elements still live in the inline buffer. O(1).
T & append(T &&item)
Append item by moving.
void release() noexcept
T * inline_ptr() noexcept
T remove_last()
Remove and return the last element.
T & get_first()
First element (checked).
T value_type
STL convention: value type.
bool operator==(const SmallVector< T, M > &v) const
Equality: same size and pairwise equal elements.
std::byte storage_[N *sizeof(T)]
SmallVector & operator=(const SmallVector &v)
Copy assignment (requires copyable T).
T & insert(size_t pos, T item)
Insert an element at position pos, shifting the tail right.
void pop_back()
Remove the last element (STL style).
const T * data() const noexcept
SmallVector(SmallVector &&v) noexcept(std::is_nothrow_move_constructible_v< T >)
Move constructor.
iterator begin() noexcept
Iterator to the first element. O(1).
T & operator[](size_t i)
Checked access to the i-th element.
bool operator!=(const SmallVector< T, M > &v) const
Inequality: negation of operator==.
const T * inline_ptr() const noexcept
void erase(const size_t pos)
Remove the element at position pos, shifting the tail left.
bool is_empty() const noexcept
Return true if no elements are stored. O(1).
SmallVector(std::initializer_list< T > l)
Construct from an initializer list.
T * iterator
Random-access iterator.
const_iterator cbegin() const noexcept
Const iterator to the first element. O(1).
T & push_back(T &&item)
STL-style alias of append(T &&).
void grow(const size_t min_cap)
void clear() noexcept
Destroy all elements. Alias of empty(). Capacity is kept.
T Item_Type
Aleph convention: element type.
const T & get_last() const
#define N
Definition fib.C:294
size_t blossom_maximum_cardinality_matching(const GT &g, DynDlist< typename GT::Arc * > &matching, SA sa=SA())
Alias of compute_maximum_cardinality_general_matching().
Definition Blossom.H:466
Main namespace for Aleph-w library functions.
Definition ah-arena.H:89
and
Check uniqueness with explicit hash + equality functors.
std::decay_t< typename HeadC::Item_Type > T
Definition ah-zip.H:105
Itor::difference_type count(const Itor &beg, const Itor &end, const T &value)
Count elements equal to a value.
Definition ahAlgo.H:127
FooMap m(5, fst_unit_pair_hash, snd_unit_pair_hash)
DynList< int > l