jaulib v1.3.0
Jau Support Library (C++, Java, ..)
darray.hpp
Go to the documentation of this file.
1/*
2 * Author: Sven Gothel <sgothel@jausoft.com>
3 * Copyright (c) 2020 Gothel Software e.K.
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining
6 * a copy of this software and associated documentation files (the
7 * "Software"), to deal in the Software without restriction, including
8 * without limitation the rights to use, copy, modify, merge, publish,
9 * distribute, sublicense, and/or sell copies of the Software, and to
10 * permit persons to whom the Software is furnished to do so, subject to
11 * the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be
14 * included in all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23 */
24
25#ifndef JAU_DYN_ARRAY_HPP_
26#define JAU_DYN_ARRAY_HPP_
27
28#include <cstring>
29#include <cstdint>
30#include <cmath>
31#include <string>
32#include <limits>
33#include <atomic>
34#include <memory>
35#include <mutex>
36#include <condition_variable>
37#include <initializer_list>
38#include <algorithm>
39
40#include <jau/debug.hpp>
41#include <jau/basic_types.hpp>
43#include <jau/callocator.hpp>
44#include <jau/basic_algos.hpp>
45
46namespace jau {
47
48// #define JAU_DEBUG_DARRAY0 1
49#if JAU_DEBUG_DARRAY0
50 #define JAU_DARRAY_PRINTF0(...) { fprintf(stderr, __VA_ARGS__); fflush(stderr); }
51#else
52 #define JAU_DARRAY_PRINTF0(...)
53#endif
54
55// #define JAU_DEBUG_DARRAY 1
56#if JAU_DEBUG_DARRAY
57 #define JAU_DARRAY_PRINTF(...) { fprintf(stderr, __VA_ARGS__); fflush(stderr); }
58#else
59 #define JAU_DARRAY_PRINTF(...)
60#endif
61
62 /** \addtogroup DataStructs
63 *
64 * @{
65 */
66
67 /**
68 * Implementation of a dynamic linear array storage, aka vector.<br>
69 * Goals are to support a high-performance CoW dynamic array implementation, jau::cow_darray,<br>
70 * exposing fine grained control over its underlying storage facility.<br>
71 * Further, jau::darray provides high-performance and efficient storage properties on its own.
72 *
73 * This class shall be compliant with <i>C++ named requirements for Container</i>.
74 *
75 * API and design differences to std::vector
76 * - jau::darray adds a parameterized <i>growth factor</i> aspect, default to golden ration jau::darray::DEFAULT_GROWTH_FACTOR
77 * - <i>capacity</i> control via constructor and operations, related to <i>growth factor</i>.
78 * - Iterator jau::darray::const_iterator .. are harmonized with jau::cow_ro_iterator .. used in jau:cow_darray.
79 * - ...
80 * - Custom constructor and operations, supporting a more efficient jau::cow_darray implementation.
81 * - Custom template typename Size_type, defaults to jau::nsize_t.
82 * - ...
83 * - <b>Removed</b>: size_type x value_type fill operations, e.g. assign, constructor, .... for clarity, since supporting <i>capacity</i>.
84 * - ...
85 * - <b>TODO</b>: std::initializer_list<T> methods, ctor is provided.
86 *
87 * Implementation differences to std::vector and some details
88 * - Using zero overhead <i>value_type*</i> as iterator type.
89 * - ...
90 * - Storage is operated on three iterator: <i>begin</i>, <i>end</i> and <i>storage_end</i>.
91 * - Constructs and destructs value_type via <i>placement new</i> within the pre-allocated array capacity. Latter is managed via allocator_type.
92 *
93 *
94 * @anchor darray_ntt_params
95 * ### Non-Type Template Parameter (NTTP) controlling Value_type memory
96 * @anchor darray_memmove
97 * #### use_memmove
98 * `use_memmove` can be overriden and defaults to `std::is_trivially_copyable_v<Value_type>`.
99 *
100 * The default value has been chosen with care, see C++ Standard section 6.9 Types [TriviallyCopyable](https://en.cppreference.com/w/cpp/named_req/TriviallyCopyable).
101 *
102 * See [Trivial destructor](https://en.cppreference.com/w/cpp/language/destructor#Trivial_destructor)
103 * being key requirement to [TriviallyCopyable](https://en.cppreference.com/w/cpp/named_req/TriviallyCopyable).
104 * > A trivial destructor is a destructor that performs no action.
105 * > Objects with trivial destructors don't require a delete-expression and may be disposed of by simply deallocating their storage.
106 * > All data types compatible with the C language (POD types) are trivially destructible.`
107 *
108 * However, since the destructor is not being called when using `memmove` on elements within this container,
109 * the requirements are more relaxed and *TriviallyCopyable* not required nor guaranteed, see below.
110 *
111 * `memmove` will be used to move an object inside this container memory only,
112 * i.e. where construction and destruction is controlled.
113 * Not requiring *TriviallyCopyable* constraints `memmove` as follows:
114 * - We can't `memmove` one or more objects into this container, even with an `rvalue` reference.
115 * The `rvalue`'s destructor will be called and potential acquired resources are lost.
116 * - We can `memmove` an object around within this container, i.e. when growing or shrinking the array. (*Used*)
117 * - We can `memmove` an object out of this container to the user. (*Unused*)
118 *
119 * Relaxed requirements for `use_memmove` are:
120 * - Not using inner class pointer to inner class fields or methods (like launching a thread).
121 * - TBD ???
122 *
123 * Since element pointer and iterator are always invalidated for container after storage mutation,
124 * above constraints are not really anything novel and go along with normal std::vector.
125 *
126 * Users may include `typedef container_memmove_compliant` in their Value_type class
127 * to enforce `use_memmove` as follows:
128 * - `typedef std::true_type container_memmove_compliant;`
129 *
130 * @anchor darray_secmem
131 * #### use_secmem
132 * `use_secmem` can be overriden and defaults to `false`.
133 *
134 * `use_secmem`, if enabled, ensures that the underlying memory will be zeroed out
135 * after use and element erasure.
136 *
137 * Users may include `typedef enforce_secmem` in their Value_type class
138 * to enforce `use_secmem` as follows:
139 * - `typedef std::true_type enforce_secmem;`
140 *
141 * @see cow_darray
142 */
143 template <typename Value_type, typename Size_type = jau::nsize_t, typename Alloc_type = jau::callocator<Value_type>,
144 bool use_memmove = std::is_trivially_copyable_v<Value_type> || is_container_memmove_compliant_v<Value_type>,
145 bool use_secmem = is_enforcing_secmem_v<Value_type>
146 >
147 class darray
148 {
149 public:
150 /** Default growth factor using the golden ratio 1.618 */
151 constexpr static const float DEFAULT_GROWTH_FACTOR = 1.618f;
152
153 constexpr static const bool uses_memmove = use_memmove;
154 constexpr static const bool uses_secmem = use_secmem;
155 constexpr static const bool uses_realloc = use_memmove && std::is_base_of_v<jau::callocator<Value_type>, Alloc_type>;
156
157 // typedefs' for C++ named requirements: Container
158
161 typedef const value_type* const_pointer;
166 typedef Size_type size_type;
167 typedef typename std::make_signed<size_type>::type difference_type;
168 // typedef std::reverse_iterator<iterator> reverse_iterator;
169 // typedef std::reverse_iterator<const_iterator> const_reverse_iterator;
170 typedef Alloc_type allocator_type;
171
172 /** Used to determine whether this type is a darray or has a darray, see ::is_darray_type<T> */
173 typedef bool darray_tag;
174
175 private:
176 typedef std::remove_const_t<Value_type> value_type_mutable;
177 /** Required to create and move immutable elements, aka const */
178 typedef value_type_mutable* pointer_mutable;
179
180 static constexpr void* voidptr_cast(const_pointer p) { return reinterpret_cast<void*>( const_cast<pointer_mutable>( p ) ); }
181
182 constexpr static const size_type DIFF_MAX = std::numeric_limits<difference_type>::max();
183 constexpr static const size_type MIN_SIZE_AT_GROW = 10;
184
185 allocator_type alloc_inst;
186 pointer begin_;
187 pointer end_;
188 pointer storage_end_;
189 float growth_factor_;
190
191 /**
192 * Allocates a new store using allocator_type.
193 * <p>
194 * Throws jau::IllegalArgumentException() if size_ > <code>std::numeric_limits<difference_type>::max()</code>, i.e. difference_type maximum.
195 * </p>
196 * <p>
197 * Throws jau::OutOfMemoryError() if allocator_type::allocate() returns nullptr.
198 * </p>
199 * @param alloc the allocator_type instance
200 * @param size_ the element count, must be <= <code>std::numeric_limits<difference_type>::max()</code>
201 * @return nullptr if given <code>0 == size_</code> or the newly allocated memory
202 */
203 constexpr value_type * allocStore(const size_type size_) {
204 if( 0 != size_ ) {
205 if( size_ > DIFF_MAX ) {
206 throw jau::IllegalArgumentException("alloc "+std::to_string(size_)+" > difference_type max "+
207 std::to_string(DIFF_MAX), E_FILE_LINE);
208 }
209 value_type * m = alloc_inst.allocate(size_);
210 if( nullptr == m ) {
211 // NOLINTBEGIN(bugprone-sizeof-expression)
212 throw jau::OutOfMemoryError("alloc "+std::to_string(size_)+" elements * "+
213 std::to_string(sizeof(value_type))+" bytes/element = "+
214 std::to_string(size_ * sizeof(value_type))+" bytes -> nullptr", E_FILE_LINE);
215 // NOLINTEND(bugprone-sizeof-expression)
216 }
217 return m;
218 }
219 return nullptr;
220 }
221
222 template<class _Alloc_type>
223 constexpr value_type * reallocStore(const size_type new_capacity_,
224 std::enable_if_t< std::is_base_of<jau::callocator<value_type>, _Alloc_type>::value, bool > = true )
225 {
226 if( new_capacity_ > DIFF_MAX ) {
227 throw jau::IllegalArgumentException("realloc "+std::to_string(new_capacity_)+" > difference_type max "+
228 std::to_string(DIFF_MAX), E_FILE_LINE);
229 }
230 value_type * m = alloc_inst.reallocate(begin_, storage_end_-begin_, new_capacity_);
231 if( nullptr == m ) {
232 free(const_cast<pointer_mutable>(begin_)); // has not been touched by realloc
233 throw jau::OutOfMemoryError("realloc "+std::to_string(new_capacity_)+" elements * "+
234 std::to_string(sizeof(value_type))+" bytes/element = "+
235 std::to_string(new_capacity_ * sizeof(value_type))+" bytes -> nullptr", E_FILE_LINE);
236 }
237 return m;
238 }
239 template<class _Alloc_type>
240 constexpr value_type * reallocStore(const size_type new_capacity_,
241 std::enable_if_t< !std::is_base_of<jau::callocator<value_type>, _Alloc_type>::value, bool > = true )
242 {
243 (void)new_capacity_;
244 throw jau::UnsupportedOperationException("realloc not supported on non allocator_type not based upon jau::callocator", E_FILE_LINE);
245 }
246
247 constexpr void freeStore() {
248 if( nullptr != begin_ ) {
249 alloc_inst.deallocate(begin_, storage_end_-begin_);
250 }
251 }
252
253 constexpr void clear_iterator() noexcept {
254 begin_ = nullptr;
255 end_ = nullptr;
256 storage_end_ = nullptr;
257 }
258
259 constexpr void set_iterator(pointer new_storage_, difference_type size_, difference_type capacity_) noexcept {
260 begin_ = new_storage_;
261 end_ = new_storage_+size_;
262 storage_end_ = new_storage_+capacity_;
263 }
264
265 constexpr void set_iterator(difference_type size_, difference_type capacity_) noexcept {
266 end_ = begin_+size_;
267 storage_end_ = begin_+capacity_;
268 }
269
270 constexpr void dtor_one(iterator pos) {
271 JAU_DARRAY_PRINTF0("dtor [%zd], count 1\n", (pos-begin_));
272 ( pos )->~value_type(); // placement new -> manual destruction!
273 if constexpr ( uses_secmem ) {
274 ::explicit_bzero(voidptr_cast(pos), sizeof(value_type));
275 }
276 }
277
278 constexpr size_type dtor_range(iterator first, const_iterator last) {
279 size_type count=0;
280 JAU_DARRAY_PRINTF0("dtor [%zd .. %zd], count %zd\n", (first-begin_), (last-begin_)-1, (last-first)-1);
281 for(; first < last; ++first, ++count ) {
282 ( first )->~value_type(); // placement new -> manual destruction!
283 }
284 if constexpr ( uses_secmem ) {
285 ::explicit_bzero(voidptr_cast(last-count), count*sizeof(value_type));
286 }
287 return count;
288 }
289
290 constexpr void ctor_copy_range(pointer dest, iterator first, const_iterator last) {
291 JAU_DARRAY_PRINTF0("ctor_copy_range [%zd .. %zd] -> ??, dist %zd\n", (first-begin_), (last-begin_)-1, (last-first)-1);
292 /**
293 * TODO
294 *
295 * g++ (Debian 12.2.0-3) 12.2.0, Debian 12 Bookworm 2022-10-17
296 * g++ bug: False positive of '-Wnull-dereference'
297 *
298In copy constructor ‘std::__shared_count<_Lp>::__shared_count(const std::__shared_count<_Lp>&) [with __gnu_cxx::_Lock_policy _Lp = __gnu_cxx::_S_atomic]’,
299 inlined from ‘std::__shared_ptr<_Tp, _Lp>::__shared_ptr(const std::__shared_ptr<_Tp, _Lp>&) [with _Tp = direct_bt::BTDevice; __gnu_cxx::_Lock_policy _Lp = __gnu_cxx::_S_atomic]’ at /usr/include/c++/12/bits/shared_ptr_base.h:1522:7,
300 inlined from ‘std::shared_ptr<_Tp>::shared_ptr(const std::shared_ptr<_Tp>&) [with _Tp = direct_bt::BTDevice]’ at /usr/include/c++/12/bits/shared_ptr.h:204:7,
301 inlined from ‘constexpr void jau::darray<Value_type, Size_type, Alloc_type, Size_type, use_memmove, use_secmem>::ctor_copy_range(pointer, iterator, const_iterator) [with Value_type = std::shared_ptr<direct_bt::BTDevice>; Alloc_type = jau::callocator<std::shared_ptr<direct_bt::BTDevice> >; Size_type = long unsigned int; bool use_memmove = false; bool use_secmem = false]’ at direct_bt/jaulib/include/jau/darray.hpp:300:21,
302 ...
303/usr/include/c++/12/bits/shared_ptr_base.h:1075:9: warning: potential null pointer dereference [-Wnull-dereference]
304 1075 | : _M_pi(__r._M_pi)
305 | ^~~~~~~~~~~~~~~~
306 */
309 for(; first < last; ++dest, ++first) {
310 new (const_cast<pointer_mutable>(dest)) value_type( *first ); // placement new / TODO: See above
311 }
313 }
314 constexpr pointer clone_range(iterator first, const_iterator last) {
315 JAU_DARRAY_PRINTF0("clone_range [0 .. %zd], count %zd\n", (last-first)-1, (last-first)-1);
316 pointer dest = allocStore(size_type(last-first));
317 ctor_copy_range(dest, first, last);
318 return dest;
319 }
320 constexpr pointer clone_range(const size_type dest_capacity, iterator first, const_iterator last) {
321 JAU_DARRAY_PRINTF0("clone_range [0 .. %zd], count %zd -> %d\n", (last-begin_)-1, (last-first)-1, (int)dest_capacity);
322 pointer dest = allocStore(dest_capacity);
323 ctor_copy_range(dest, first, last);
324 return dest;
325 }
326 constexpr void ctor_copy_range_check(pointer dest, iterator first, const_iterator last) {
327 JAU_DARRAY_PRINTF0("ctor_copy_range_check [%zd .. %zd] -> ??, dist %zd\n", (first-begin_), (last-begin_)-1, (last-first)-1);
328 if( first > last ) {
329 throw jau::IllegalArgumentException("first "+to_hexstring(first)+" > last "+to_hexstring(last), E_FILE_LINE);
330 }
331 for(; first < last; ++dest, ++first) {
332 new (const_cast<pointer_mutable>(dest)) value_type( *first ); // placement new
333 }
334 }
335 constexpr pointer clone_range_check(const size_type dest_capacity, iterator first, const_iterator last) {
336 JAU_DARRAY_PRINTF0("clone_range_check [%zd .. %zd], count %zd -> %d\n", (first-begin_), (last-begin_)-1, (last-first)-1, (int)dest_capacity);
337 if( dest_capacity < size_type(last-first) ) {
338 throw jau::IllegalArgumentException("capacity "+std::to_string(dest_capacity)+" < source range "+
340 }
341 pointer dest = allocStore(dest_capacity);
342 ctor_copy_range_check(dest, first, last);
343 return dest;
344 }
345
346 template< class InputIt >
347 constexpr static void ctor_copy_range_foreign(pointer dest, InputIt first, InputIt last) {
348 if( first > last ) {
349 throw jau::IllegalArgumentException("first "+jau::to_string( first )+" > last "+
350 jau::to_string( last ), E_FILE_LINE);
351 }
352 for(; first != last; ++dest, ++first) {
353 new (const_cast<pointer_mutable>(dest)) value_type( *first ); // placement new
354 }
355 }
356 template< class InputIt >
357 constexpr pointer clone_range_foreign(const size_type dest_capacity, InputIt first, InputIt last) {
358 if( dest_capacity < size_type(last-first) ) {
359 throw jau::IllegalArgumentException("capacity "+std::to_string(dest_capacity)+" < source range "+
361 }
362 pointer dest = allocStore(dest_capacity);
363 ctor_copy_range_foreign(dest, first, last);
364 return dest;
365 }
366
367 constexpr void realloc_storage_move(const size_type new_capacity) {
368 if constexpr ( !uses_memmove ) {
369 pointer new_storage = allocStore(new_capacity);
370 {
371 iterator dest = new_storage;
372 iterator first = begin_;
373 for(; first < end_; ++dest, ++first) {
374 new (const_cast<pointer_mutable>(dest)) value_type( std::move( *first ) ); // placement new
375 dtor_one(first); // manual destruction, even after std::move (object still exists)
376 }
377 }
378 freeStore();
379 set_iterator(new_storage, size(), new_capacity);
380 } else if constexpr ( uses_realloc ) {
381 pointer new_storage = reallocStore<allocator_type>(new_capacity);
382 set_iterator(new_storage, size(), new_capacity);
383 } else {
384 pointer new_storage = allocStore(new_capacity);
385 ::memmove(voidptr_cast(new_storage),
386 begin_, (uint8_t*)end_-(uint8_t*)begin_); // we can simply copy the memory over, also no overlap
387 freeStore();
388 set_iterator(new_storage, size(), new_capacity);
389 }
390 }
391 constexpr void grow_storage_move(const size_type new_capacity) {
392 /**
393 * Determine a grown_capacity, which is at least
394 * - MIN_SIZE_AT_GROW
395 * - old_capacity * growth_factor ..
396 */
397 const size_type old_capacity = capacity();
398 const size_type grown_capacity = std::max<size_type>(
399 std::max<size_type>( MIN_SIZE_AT_GROW, new_capacity ),
400 std::max<size_type>( new_capacity, static_cast<size_type>( ::roundf(old_capacity * growth_factor()) ) )
401 );
402 realloc_storage_move( grown_capacity );
403 }
404 constexpr void grow_storage_move() {
405 realloc_storage_move( get_grown_capacity() );
406 }
407
408 constexpr void move_elements(iterator dest, const_iterator first, const difference_type count) {
409 // Debatable here: "Moved source array has been taken over, flush sources' pointer to avoid value_type dtor releasing taken resources!"
410 // Debatable, b/c is this even possible for user to hold an instance the way, that a dtor gets called? Probably not.
411 // Hence we leave it to 'uses_secmem' to bzero...
412 if constexpr ( uses_memmove ) {
413 // handles overlap
414 // NOLINTNEXTLINE(bugprone-undefined-memory-manipulation)
415 ::memmove(voidptr_cast(dest),
416 first, sizeof(value_type)*count);
417 if constexpr ( uses_secmem ) {
418 if( dest < first ) {
419 // move elems left
420 JAU_DARRAY_PRINTF0("move_elements.mmm.left [%zd .. %zd] -> %zd, dist %zd\n", (first-begin_), ((first + count)-begin_)-1, (dest-begin_), (first-dest));
421 ::explicit_bzero(voidptr_cast(dest+count), (first-dest)*sizeof(value_type));
422 } else {
423 // move elems right
424 JAU_DARRAY_PRINTF0("move_elements.mmm.right [%zd .. %zd] -> %zd, dist %zd, size %zu\n", (first-begin_), ((first + count)-begin_)-1, (dest-begin_), (dest-first), (dest-first)*sizeof(value_type));
425 /**
426 * TODO
427 *
428 * g++ (Debian 12.2.0-3) 12.2.0, Debian 12 Bookworm 2022-10-17
429 * g++ bug: False positive of '-Werror=stringop-overflow=' using ::explicit_bzero(..)
430 *
431 * In member function ‘constexpr void jau::darray<Value_type, Size_type, Alloc_type, Size_type, use_memmove, use_secmem>::move_elements(iterator, const_iterator, difference_type) [with Value_type = DataType02_Memmove_Secmem; Alloc_type = jau::callocator<DataType02_Memmove_Secmem>; Size_type = long unsigned int; bool use_memmove = true; bool use_secmem = true]’,
432 inlined from ‘constexpr jau::darray<Value_type, Size_type, Alloc_type, Size_type, use_memmove, use_secmem>::value_type* jau::darray<Value_type, Size_type, Alloc_type, Size_type, use_memmove, use_secmem>::erase(const_iterator) [with Value_type = DataType02_Memmove_Secmem; Alloc_type = jau::callocator<DataType02_Memmove_Secmem>; Size_type = long unsigned int; bool use_memmove = true; bool use_secmem = true]’ at include/jau/darray.hpp:911:38,
433 inlined from ‘void testDArrayValueType(const std::string&) [with Payload = DataType02_Memmove_Secmem]’ at test/test_darray_01.cpp:337:28:
434 include/jau/darray.hpp:406:45: error: ‘void explicit_bzero(void*, size_t)’ specified size 18446744073709551600 exceeds maximum object size 9223372036854775807 [-Werror=stringop-overflow=]
435 406 | ::explicit_bzero(voidptr_cast(first), (dest-first)*sizeof(value_type));
436 | ~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
437 */
439 PRAGMA_DISABLE_WARNING_STRINGOP_OVERFLOW
440 ::explicit_bzero(voidptr_cast(first), (dest-first)*sizeof(value_type)); // TODO: See above
442 }
443 }
444 } else {
445 if( dest < first ) {
446 // move elems left
447 const_iterator last = first + count;
448 JAU_DARRAY_PRINTF0("move_elements.def.left [%zd .. %zd] -> %zd, dist %zd\n", (first-begin_), (last-begin_)-1, (dest-begin_), (first-dest));
449 for(; first < last; ++dest, ++first ) {
450 new (const_cast<pointer_mutable>(dest)) value_type( std::move( *first ) ); // placement new
451 dtor_one( const_cast<value_type*>( first ) ); // manual destruction, even after std::move (object still exists)
452 }
453 } else {
454 // move elems right
455 iterator last = const_cast<iterator>(first + count);
456 JAU_DARRAY_PRINTF0("move_elements.def.right [%zd .. %zd] -> %zd, dist %zd\n", (first-begin_), (last-begin_)-1, (dest-begin_), (dest-first));
457 dest += count - 1;
458 for(--last; first <= last; --dest, --last ) {
459 new (const_cast<pointer_mutable>(dest)) value_type( std::move( *last ) ); // placement new
460 dtor_one( last ); // manual destruction, even after std::move (object still exists)
461 }
462 }
463 }
464 }
465
466
467 public:
468
469 // ctor w/o elements
470
471 /**
472 * Default constructor, giving zero capacity and zero memory footprint.
473 */
474 constexpr darray() noexcept
475 : alloc_inst(), begin_( nullptr ), end_( nullptr ), storage_end_( nullptr ),
476 growth_factor_(DEFAULT_GROWTH_FACTOR) {
477 JAU_DARRAY_PRINTF("ctor def: %s\n", get_info().c_str());
478 }
479
480 /**
481 * Creating an empty instance with initial capacity and other (default) properties.
482 * @param capacity initial capacity of the new instance.
483 * @param growth_factor given growth factor
484 * @param alloc given allocator_type
485 */
487 : alloc_inst( alloc ), begin_( allocStore(capacity) ), end_( begin_ ), storage_end_( begin_ + capacity ),
488 growth_factor_( growth_factor ) {
489 JAU_DARRAY_PRINTF("ctor 1: %s\n", get_info().c_str());
490 }
491
492 // copy_ctor on darray elements
493
494 /**
495 * Creates a new instance, copying all elements from the given darray.<br>
496 * Capacity and size will equal the given array, i.e. the result is a trimmed jau::darray.
497 * @param x the given darray, all elements will be copied into the new instance.
498 */
499 constexpr darray(const darray& x)
500 : alloc_inst( x.alloc_inst ), begin_( clone_range(x.begin_, x.end_) ), end_( begin_ + x.size() ),
501 storage_end_( begin_ + x.size() ), growth_factor_( x.growth_factor_ ) {
502 JAU_DARRAY_PRINTF("ctor copy0: this %s\n", get_info().c_str());
503 JAU_DARRAY_PRINTF("ctor copy0: x %s\n", x.get_info().c_str());
504 }
505
506 /**
507 * Creates a new instance, copying all elements from the given darray.<br>
508 * Capacity and size will equal the given array, i.e. the result is a trimmed jau::darray.
509 * @param x the given darray, all elements will be copied into the new instance.
510 * @param growth_factor custom growth factor
511 * @param alloc custom allocator_type instance
512 */
513 constexpr explicit darray(const darray& x, const float growth_factor, const allocator_type& alloc)
514 : alloc_inst( alloc ), begin_( clone_range(x.begin_, x.end_) ), end_( begin_ + x.size() ),
515 storage_end_( begin_ + x.size() ), growth_factor_( growth_factor ) {
516 JAU_DARRAY_PRINTF("ctor copy1: this %s\n", get_info().c_str());
517 JAU_DARRAY_PRINTF("ctor copy1: x %s\n", x.get_info().c_str());
518 }
519
520 /**
521 * Creates a new instance with custom initial storage capacity, copying all elements from the given darray.<br>
522 * Size will equal the given array.
523 * <p>
524 * Throws jau::IllegalArgumentException() if <code>_capacity < x.size()</code>.
525 * </p>
526 * @param x the given darray, all elements will be copied into the new instance.
527 * @param _capacity custom initial storage capacity
528 * @param growth_factor custom growth factor
529 * @param alloc custom allocator_type instance
530 */
531 constexpr explicit darray(const darray& x, const size_type _capacity, const float growth_factor, const allocator_type& alloc)
532 : alloc_inst( alloc ), begin_( clone_range( _capacity, x.begin_, x.end_) ), end_( begin_ + x.size() ),
533 storage_end_( begin_ + _capacity ), growth_factor_( growth_factor ) {
534 JAU_DARRAY_PRINTF("ctor copy2: this %s\n", get_info().c_str());
535 JAU_DARRAY_PRINTF("ctor copy2: x %s\n", x.get_info().c_str());
536 }
537
538 /**
539 * Like std::vector::operator=(&), assignment
540 */
541 constexpr darray& operator=(const darray& x) {
542 JAU_DARRAY_PRINTF("assignment copy.0: this %s\n", get_info().c_str());
543 JAU_DARRAY_PRINTF("assignment copy.0: x %s\n", x.get_info().c_str());
544 if( this != &x ) {
545 const size_type capacity_ = capacity();
546 const size_type x_size_ = x.size();
547 dtor_range(begin_, end_);
548 growth_factor_ = x.growth_factor_;
549 if( x_size_ > capacity_ ) {
550 freeStore();
551 begin_ = clone_range(x_size_, x.begin_, x.end_);
552 set_iterator(x_size_, x_size_);
553 } else {
554 ctor_copy_range(begin_, x.begin_, x.end_);
555 set_iterator(x_size_, capacity_);
556 }
557 }
558 JAU_DARRAY_PRINTF("assignment copy.X: this %s\n", get_info().c_str());
559 JAU_DARRAY_PRINTF("assignment copy.X: x %s\n", x.get_info().c_str());
560 return *this;
561 }
562
563 // move_ctor on darray elements
564
565 constexpr darray(darray && x) noexcept
566 : alloc_inst( std::move(x.alloc_inst) ), begin_( std::move(x.begin_) ), end_( std::move(x.end_) ),
567 storage_end_( std::move(x.storage_end_) ), growth_factor_( std::move(x.growth_factor_) )
568 {
569 JAU_DARRAY_PRINTF("ctor move0: this %s\n", get_info().c_str());
570 JAU_DARRAY_PRINTF("ctor move0: x %s\n", x.get_info().c_str());
571 // Moved source array has been taken over, flush sources' pointer to avoid value_type dtor releasing taken resources!
572 x.clear_iterator();
573 }
574
575 constexpr explicit darray(darray && x, const float growth_factor, const allocator_type& alloc) noexcept
576 : alloc_inst( std::move(alloc) ), begin_( std::move(x.begin_) ), end_( std::move(x.end_) ),
577 storage_end_( std::move(x.storage_end_) ), growth_factor_( growth_factor )
578 {
579 JAU_DARRAY_PRINTF("ctor move1: this %s\n", get_info().c_str());
580 JAU_DARRAY_PRINTF("ctor move1: x %s\n", x.get_info().c_str());
581 // Moved source array has been taken over, flush sources' pointer to avoid value_type dtor releasing taken resources!
582 x.clear_iterator();
583 }
584
585 /**
586 * Like std::vector::operator=(&&), move.
587 */
588 constexpr darray& operator=(darray&& x) noexcept {
589 JAU_DARRAY_PRINTF("assignment move.0: this %s\n", get_info().c_str());
590 JAU_DARRAY_PRINTF("assignment move.0: x %s\n", x.get_info().c_str());
591 if( this != &x ) {
592 clear();
593 alloc_inst = std::move(x.alloc_inst);
594 begin_ = std::move(x.begin_);
595 end_ = std::move(x.end_);
596 storage_end_ = std::move(x.storage_end_);
597 growth_factor_ = std::move( x.growth_factor_ );
598
599 // Moved source array has been taken over, flush sources' pointer to avoid value_type dtor releasing taken resources!
600 x.clear_iterator();
601 }
602 JAU_DARRAY_PRINTF("assignment move.X: this %s\n", get_info().c_str());
603 JAU_DARRAY_PRINTF("assignment move.X: x %s\n", x.get_info().c_str());
604 return *this;
605 }
606
607 // ctor on const_iterator and foreign template iterator
608
609 /**
610 * Creates a new instance with custom initial storage capacity,
611 * copying all elements from the given const_iterator value_type range [first, last).<br>
612 * Size will equal the range [first, last), i.e. <code>size_type(last-first)</code>.
613 * <p>
614 * Throws jau::IllegalArgumentException() if <code>_capacity < size_type(last - first)</code>.
615 * </p>
616 * @param _capacity custom initial storage capacity
617 * @param first const_iterator to first element of value_type range [first, last)
618 * @param last const_iterator to last element of value_type range [first, last)
619 * @param growth_factor custom growth factor
620 * @param alloc custom allocator_type instance
621 */
622 constexpr explicit darray(const size_type _capacity, const_iterator first, const_iterator last,
624 : alloc_inst( alloc ), begin_( clone_range_check(_capacity, first, last) ), end_(begin_ + size_type(last - first) ),
625 storage_end_( begin_ + _capacity ), growth_factor_( growth_factor ) {
626 JAU_DARRAY_PRINTF("ctor iters0: %s\n", get_info().c_str());
627 }
628
629 /**
630 * Creates a new instance with custom initial storage capacity,
631 * copying all elements from the given template input-iterator value_type range [first, last).<br>
632 * Size will equal the range [first, last), i.e. <code>size_type(last-first)</code>.
633 * <p>
634 * Throws jau::IllegalArgumentException() if <code>_capacity < size_type(last - first)</code>.
635 * </p>
636 * @tparam InputIt template input-iterator custom type
637 * @param _capacity custom initial storage capacity
638 * @param first template input-iterator to first element of value_type range [first, last)
639 * @param last template input-iterator to last element of value_type range [first, last)
640 * @param growth_factor custom growth factor
641 * @param alloc custom allocator_type instance
642 */
643 template< class InputIt >
644 constexpr explicit darray(const size_type _capacity, InputIt first, InputIt last,
646 : alloc_inst( alloc ), begin_( clone_range_foreign(_capacity, first, last) ), end_(begin_ + size_type(last - first) ),
647 storage_end_( begin_ + _capacity ), growth_factor_( growth_factor ) {
648 JAU_DARRAY_PRINTF("ctor iters1: %s\n", get_info().c_str());
649 }
650
651 /**
652 * Creates a new instance,
653 * copying all elements from the given template input-iterator value_type range [first, last).<br>
654 * Size will equal the range [first, last), i.e. <code>size_type(last-first)</code>.
655 * @tparam InputIt template input-iterator custom type
656 * @param first template input-iterator to first element of value_type range [first, last)
657 * @param last template input-iterator to last element of value_type range [first, last)
658 * @param alloc custom allocator_type instance
659 */
660 template< class InputIt >
661 constexpr darray(InputIt first, InputIt last, const allocator_type& alloc = allocator_type())
662 : alloc_inst( alloc ), begin_( clone_range_foreign(size_type(last - first), first, last) ), end_(begin_ + size_type(last - first) ),
663 storage_end_( begin_ + size_type(last - first) ), growth_factor_( DEFAULT_GROWTH_FACTOR ) {
664 JAU_DARRAY_PRINTF("ctor iters2: %s\n", get_info().c_str());
665 }
666
667 /**
668 * Create a new instance from an initializer list.
669 *
670 * Using the `std::initializer_list` requires to *copy* the given value_type objects into this darray.
671 *
672 * To utilize more efficient move semantics, see push_back_list() and jau::make_darray().
673 *
674 * @param initlist initializer_list.
675 * @param alloc allocator
676 * @see push_back_list()
677 * @see jau::make_darray()
678 */
679 constexpr darray(std::initializer_list<value_type> initlist, const allocator_type& alloc = allocator_type())
680 : alloc_inst( alloc ), begin_( clone_range_foreign(initlist.size(), initlist.begin(), initlist.end()) ),
681 end_(begin_ + initlist.size() ), storage_end_( begin_ + initlist.size() ), growth_factor_( DEFAULT_GROWTH_FACTOR ) {
682 JAU_DARRAY_PRINTF("ctor initlist: %s\n", get_info().c_str());
683 }
684
685 ~darray() noexcept {
686 JAU_DARRAY_PRINTF("dtor: %s\n", get_info().c_str());
687 clear();
688 }
689
690 /**
691 * Returns <code>std::numeric_limits<difference_type>::max()</code> as the maximum array size.
692 * <p>
693 * We rely on the signed <code>difference_type</code> for pointer arithmetic,
694 * deducing ranges from iterator.
695 * </p>
696 */
697 constexpr size_type max_size() const noexcept { return DIFF_MAX; }
698
699 // iterator
700
701 constexpr iterator begin() noexcept { return begin_; }
702
703 constexpr const_iterator begin() const noexcept { return begin_; }
704
705 constexpr const_iterator cbegin() const noexcept { return begin_; }
706
707 constexpr iterator end() noexcept { return end_; }
708
709 constexpr const_iterator end() const noexcept { return end_; }
710
711 constexpr const_iterator cend() const noexcept { return end_; }
712
713#if 0
714 constexpr iterator storage_end() noexcept { return storage_end_; }
715
716 constexpr const_iterator storage_end() const noexcept { return storage_end_; }
717
718 constexpr const_iterator cstorage_end() const noexcept { return storage_end_; }
719#endif
720
721 // read access
722
723 const allocator_type& get_allocator_ref() const noexcept {
724 return alloc_inst;
725 }
726
727 allocator_type get_allocator() const noexcept {
728 return allocator_type(alloc_inst);
729 }
730
731 constexpr float growth_factor() const noexcept {
732 return growth_factor_;
733 }
734
735 /**
736 * Return the current capacity.
737 */
738 constexpr size_type capacity() const noexcept { return size_type(storage_end_ - begin_); }
739
740 /**
741 * Return the current capacity() multiplied by the growth factor, minimum is max(capacity()+1, 10).
742 */
743 constexpr size_type get_grown_capacity() const noexcept {
744 const size_type a_capacity = capacity();
745 return std::max<size_type>( std::max<size_type>( MIN_SIZE_AT_GROW, a_capacity+1 ),
746 static_cast<size_type>( ::roundf(a_capacity * growth_factor()) ) );
747 }
748
749 /**
750 * Like std::vector::empty().
751 */
752 constexpr bool empty() const noexcept { return begin_ == end_; }
753
754 /**
755 * Returns true if capacity has been reached and the next push_back()
756 * will grow the storage and invalidates all iterators and references.
757 */
758 constexpr bool capacity_reached() const noexcept { return end_ >= storage_end_; }
759
760 /**
761 * Like std::vector::size().
762 */
763 constexpr size_type size() const noexcept { return size_type(end_ - begin_); }
764
765 // mixed mutable/immutable element access
766
767 /**
768 * Like std::vector::front(), mutable access.
769 */
770 constexpr reference front() { return *begin_; }
771
772 /**
773 * Like std::vector::front(), immutable access.
774 */
775 constexpr const_reference front() const { return *begin_; }
776
777 /**
778 * Like std::vector::back(), mutable access.
779 */
780 constexpr reference back() { return *(end_-1); }
781
782 /**
783 * Like std::vector::back(), immutable access.
784 */
785 constexpr const_reference back() const { return *(end_-1); }
786
787 /**
788 * Like std::vector::data(), const immutable pointer
789 */
790 constexpr const_pointer data() const noexcept { return begin_; }
791
792 /**
793 * Like std::vector::data(), mutable pointer
794 */
795 constexpr pointer data() noexcept { return begin_; }
796
797 /**
798 * Like std::vector::operator[](size_type), immutable reference.
799 */
801 return *(begin_+i);
802 }
803
804 /**
805 * Like std::vector::operator[](size_type), mutable reference.
806 */
808 return *(begin_+i);
809 }
810
811 /**
812 * Like std::vector::at(size_type), immutable reference.
813 */
815 if( 0 <= i && i < size() ) {
816 return *(begin_+i);
817 }
819 }
820
821 /**
822 * Like std::vector::at(size_type), mutable reference.
823 */
825 if( 0 <= i && i < size() ) {
826 return *(begin_+i);
827 }
829 }
830
831 // write access, mutable array operations
832
833 /**
834 * Like std::vector::reserve(), increases this instance's capacity to <code>new_capacity</code>.
835 * <p>
836 * Only creates a new storage and invalidates iterators if <code>new_capacity</code>
837 * is greater than the current jau::darray::capacity().
838 * </p>
839 */
840 void reserve(size_type new_capacity) {
841 const size_type capacity_ = capacity();
842 if( new_capacity > capacity_ ) {
843 grow_storage_move(new_capacity);
844 }
845 }
846
847 /**
848 * Like std::vector::shrink_to_fit(), but ensured `constexpr`.
849 *
850 * If capacity() > size(), reallocate storage to size().
851 */
852 constexpr void shrink_to_fit() {
853 const size_type size_ = size();
854 const size_type capacity_ = capacity();
855 if( capacity_ > size_ ) {
856 realloc_storage_move(size_);
857 }
858 }
859
860 /**
861 * Like std::vector::assign()
862 * @tparam InputIt foreign input-iterator to range of value_type [first, last)
863 * @param first first foreign input-iterator to range of value_type [first, last)
864 * @param last last foreign input-iterator to range of value_type [first, last)
865 */
866 template< class InputIt >
867 constexpr void assign( InputIt first, InputIt last ) {
868 const size_type size_ = size();
869 const size_type capacity_ = capacity();
870 const size_type x_size_ = size_type(last - first);
871 dtor_range(begin_, end_);
872 if( x_size_ > capacity_ ) {
873 freeStore();
874 begin_ = clone_range_foreign(x_size_, first, last);
875 set_iterator(x_size_, x_size_);
876 } else {
877 ctor_copy_range_foreign(begin_, first, last);
878 set_iterator(x_size_, capacity_);
879 }
880 }
881 /**
882 * Like std::vector::assign(), but non-template overload using const_iterator.
883 * @param first first const_iterator to range of value_type [first, last)
884 * @param last last const_iterator to range of value_type [first, last)
885 */
886 constexpr void assign( const_iterator first, const_iterator last ) {
887 const size_type size_ = size();
888 const size_type capacity_ = capacity();
889 const size_type x_size_ = size_type(last - first);
890 dtor_range(begin_, end_);
891 if( x_size_ > capacity_ ) {
892 freeStore();
893 begin_ = clone_range_check(x_size_, first, last);
894 set_iterator(x_size_, x_size_);
895 } else {
896 ctor_copy_range_check(begin_, first, last);
897 set_iterator(x_size_, capacity_);
898 }
899 }
900
901 /**
902 * Like std::vector::clear(), but ending with zero capacity.
903 */
904 constexpr void clear() noexcept {
905 dtor_range(begin_, end_);
906 freeStore();
907 begin_ = nullptr;
908 end_ = nullptr;
909 storage_end_ = nullptr;
910 }
911
912 /**
913 * Like std::vector::swap().
914 */
915 constexpr void swap(darray& x) noexcept {
916 JAU_DARRAY_PRINTF("swap.0: this %s\n", get_info().c_str());
917 JAU_DARRAY_PRINTF("swap.0: x %s\n", x.get_info().c_str());
918 std::swap(alloc_inst, x.alloc_inst);
919 std::swap(begin_, x.begin_);
920 std::swap(end_, x.end_);
921 std::swap(storage_end_, x.storage_end_);
922 std::swap(growth_factor_, x.growth_factor_);
923 JAU_DARRAY_PRINTF("swap.X: this %s\n", get_info().c_str());
924 JAU_DARRAY_PRINTF("swap.X: x %s\n", x.get_info().c_str());
925 }
926
927 /**
928 * Like std::vector::pop_back().
929 */
930 constexpr void pop_back() noexcept {
931 if( begin_ != end_ ) {
932 dtor_one( --end_ );
933 }
934 }
935
936 /**
937 * Like std::vector::erase(), removes the elements at pos.
938 * @return iterator following the last removed element.
939 */
940 constexpr iterator erase (const_iterator pos) {
941 if( begin_ <= pos && pos < end_ ) {
942 dtor_one( const_cast<value_type*>( pos ) );
943 const difference_type right_count = end_ - ( pos + 1 ); // pos is exclusive
944 if( 0 < right_count ) {
945 move_elements(const_cast<value_type*>(pos), pos+1, right_count); // move right elems one left
946 }
947 --end_;
948 }
949 return begin_ <= const_cast<iterator>(pos) && const_cast<iterator>(pos) <= end_ ? const_cast<iterator>(pos) : end_;
950 }
951
952 /**
953 * Like std::vector::erase(), removes the elements in the range [first, last).
954 * @return iterator following the last removed element.
955 */
956 constexpr iterator erase (iterator first, const_iterator last) {
957 const size_type count = dtor_range(first, last);
958 if( count > 0 ) {
959 const difference_type right_count = end_ - last; // last is exclusive
960 if( 0 < right_count ) {
961 move_elements(first, last, right_count); // move right elems count left
962 }
963 end_ -= count;
964 }
965 return begin_ <= const_cast<iterator>(first) && const_cast<iterator>(first) <= end_ ? const_cast<iterator>(first) : end_;
966 }
967
968 /**
969 * Similar to std::vector::erase() using an index, removes the elements at pos_idx.
970 * @return iterator following the last removed element.
971 */
972 constexpr iterator erase (const size_type pos_idx) {
973 return erase(begin_ + pos_idx);
974 }
975
976 /**
977 * Similar to std::vector::erase() using indices, removes the elements in the range [first_idx, last_idx).
978 * @return iterator following the last removed element.
979 */
980 constexpr iterator erase (const size_type first_idx, const size_type last_idx) {
981 return erase(begin_ + first_idx, begin_ + last_idx);
982 }
983
984 /**
985 * Like std::vector::insert(), copy
986 * <p>
987 * Inserts the element before pos
988 * and moves all elements from there to the right beforehand.
989 * </p>
990 * <p>
991 * size will be increased by one.
992 * </p>
993 * @param pos iterator before which the content will be inserted. pos may be the end() iterator
994 * @param x element value to insert
995 */
996 constexpr iterator insert(const_iterator pos, const value_type& x) {
997 if( begin_ <= pos && pos <= end_ ) {
998 if( end_ == storage_end_ ) {
999 const size_type pos_idx = pos - begin_;
1000 grow_storage_move();
1001 pos = begin_ + pos_idx;
1002 }
1003 const difference_type right_count = end_ - pos; // include original element at 'pos_new'
1004 if( 0 < right_count ) {
1005 move_elements(const_cast<iterator>(pos+1), pos, right_count); // move elems one right
1006 }
1007 new (const_cast<pointer_mutable>(pos)) value_type( x ); // placement new
1008 ++end_;
1009
1010 return begin_ <= pos && pos <= end_ ? const_cast<iterator>(pos) : end_;
1011 } else {
1013 }
1014 }
1015
1016 /**
1017 * Similar to std::vector::insert() using an index, copy
1018 * @param pos_idx index before which the content will be inserted. index may be the end size() index
1019 * @param x element value to insert
1020 */
1021 constexpr iterator insert(const size_type pos_idx, const value_type& x) {
1022 return insert(begin_ + pos_idx, x);
1023 }
1024
1025 /**
1026 * Like std::vector::insert(), move
1027 * <p>
1028 * Inserts the element before the given position
1029 * and moves all elements from there to the right beforehand.
1030 * </p>
1031 * <p>
1032 * size will be increased by one.
1033 * </p>
1034 * @param pos iterator before which the content will be inserted. pos may be the end() iterator
1035 * @param x element value to be moved into
1036 */
1038 if( begin_ <= pos && pos <= end_ ) {
1039 const size_type pos_idx = pos - begin_;
1040 if( end_ == storage_end_ ) {
1041 grow_storage_move();
1042 }
1043 iterator pos_new = begin_ + pos_idx;
1044 const difference_type right_count = end_ - pos_new; // include original element at 'pos_new'
1045 if( 0 < right_count ) {
1046 move_elements(pos_new+1, pos_new, right_count); // move elems one right
1047 }
1048 new (const_cast<pointer_mutable>(pos_new)) value_type( std::move( x ) ); // placement new
1049 ++end_;
1050
1051 return begin_ <= pos_new && pos_new <= end_ ? pos_new : end_;
1052 } else {
1054 }
1055 }
1056
1057 /**
1058 * Like std::vector::emplace(), construct a new element in place.
1059 * <p>
1060 * Constructs the element before the given position using placement new
1061 * and moves all elements from there to the right beforehand.
1062 * </p>
1063 * <p>
1064 * size will be increased by one.
1065 * </p>
1066 * @param pos iterator before which the content will be inserted. pos may be the end() iterator
1067 * @param args arguments to forward to the constructor of the element
1068 */
1069 template<typename... Args>
1070 constexpr iterator emplace(const_iterator pos, Args&&... args) {
1071 if( begin_ <= pos && pos <= end_ ) {
1072 const size_type pos_idx = pos - begin_;
1073 if( end_ == storage_end_ ) {
1074 grow_storage_move();
1075 }
1076 iterator pos_new = begin_ + pos_idx;
1077 const difference_type right_count = end_ - pos_new; // include original element at 'pos_new'
1078 if( 0 < right_count ) {
1079 move_elements(pos_new+1, pos_new, right_count); // move elems one right
1080 }
1081 new (const_cast<pointer_mutable>(pos_new)) value_type( std::forward<Args>(args)... ); // placement new, construct in-place
1082 ++end_;
1083
1084 return begin_ <= pos_new && pos_new <= end_ ? pos_new : end_;
1085 } else {
1087 }
1088 }
1089
1090 /**
1091 * Like std::vector::insert(), inserting the value_type range [first, last).
1092 * @tparam InputIt foreign input-iterator to range of value_type [first, last)
1093 * @param pos iterator before which the content will be inserted. pos may be the end() iterator
1094 * @param first first foreign input-iterator to range of value_type [first, last)
1095 * @param last last foreign input-iterator to range of value_type [first, last)
1096 * @return Iterator pointing to the first element inserted, or pos if first==last.
1097 */
1098 template< class InputIt >
1099 constexpr iterator insert( const_iterator pos, InputIt first, InputIt last ) {
1100 if( begin_ <= pos && pos <= end_ ) {
1101 const size_type new_elem_count = size_type(last - first);
1102 const size_type pos_idx = pos - begin_;
1103 if( end_ + new_elem_count >= storage_end_ ) {
1104 grow_storage_move(size() + new_elem_count);
1105 }
1106 iterator pos_new = begin_ + pos_idx;
1107 const difference_type right_count = end_ - pos_new; // include original element at 'pos_new'
1108 if( 0 < right_count ) {
1109 move_elements(pos_new + new_elem_count, pos_new, right_count); // move elems count right
1110 }
1111 ctor_copy_range_foreign(pos_new, first, last);
1112 end_ += new_elem_count;
1113
1114 return begin_ <= pos_new && pos_new <= end_ ? pos_new : end_;
1115 } else {
1117 }
1118 }
1119
1120 /**
1121 * Like std::vector::push_back(), copy
1122 * @param x the value to be added at the tail.
1123 */
1124 constexpr void push_back(const value_type& x) {
1125 if( end_ == storage_end_ ) {
1126 grow_storage_move();
1127 }
1128 new (const_cast<pointer_mutable>(end_)) value_type( x ); // placement new
1129 ++end_;
1130 }
1131
1132 /**
1133 * Like std::vector::push_back(), move
1134 * @param x the value to be added at the tail.
1135 */
1136 constexpr void push_back(value_type&& x) {
1137 if( end_ == storage_end_ ) {
1138 grow_storage_move();
1139 }
1140 new (const_cast<pointer_mutable>(end_)) value_type( std::move(x) ); // placement new, just one element - no optimization
1141 ++end_;
1142 }
1143
1144 /**
1145 * Like std::vector::push_front(), copy
1146 * @param x the value to be added at the front.
1147 */
1148 constexpr void push_front(const value_type& x) {
1149 insert(begin_, x);
1150 }
1151
1152 /**
1153 * Like std::vector::push_front(), move
1154 * @param x the value to be added at the front.
1155 */
1156 constexpr void push_front(value_type&& x) {
1157 insert(begin_, std::move(x));
1158 }
1159
1160 /**
1161 * Like std::vector::emplace_back(), construct a new element in place at the end().
1162 * <p>
1163 * Constructs the element at the end() using placement new.
1164 * </p>
1165 * <p>
1166 * size will be increased by one.
1167 * </p>
1168 * @param args arguments to forward to the constructor of the element
1169 */
1170 template<typename... Args>
1171 constexpr reference emplace_back(Args&&... args) {
1172 if( end_ == storage_end_ ) {
1173 grow_storage_move();
1174 }
1175 new (const_cast<pointer_mutable>(end_)) value_type( std::forward<Args>(args)... ); // placement new, construct in-place
1176 reference res = *end_;
1177 ++end_;
1178 return res;
1179 }
1180
1181 /**
1182 * Like std::vector::push_back(), but appends the value_type range [first, last).
1183 * @tparam InputIt foreign input-iterator to range of value_type [first, last)
1184 * @param first first foreign input-iterator to range of value_type [first, last)
1185 * @param last last foreign input-iterator to range of value_type [first, last)
1186 */
1187 template< class InputIt >
1188 constexpr void push_back( InputIt first, InputIt last ) {
1189 const size_type count = size_type(last - first);
1190
1191 if( end_ + count >= storage_end_ ) {
1192 grow_storage_move(size() + count);
1193 }
1194 ctor_copy_range_foreign(end_, first, last);
1195 end_ += count;
1196 }
1197
1198 /**
1199 * Like push_back(), but for more multiple const r-value to copy.
1200 *
1201 * @tparam Args
1202 * @param args r-value references to copy into this storage
1203 */
1204 template <typename... Args>
1205 constexpr void push_back_list(const Args&... args)
1206 {
1207 const size_type count = sizeof...(Args);
1208
1209 JAU_DARRAY_PRINTF("push_back_list.copy.0: %zu elems: this %s\n", count, get_info().c_str());
1210
1211 if( end_ + count >= storage_end_ ) {
1212 grow_storage_move(size() + count);
1213 }
1214 // C++17 fold expression on above C++11 template pack args
1215 ( new (const_cast<pointer_mutable>(end_++)) value_type( args ), ... ); // @suppress("Syntax error")
1216
1217 JAU_DARRAY_PRINTF("push_back_list.copy.X: %zu elems: this %s\n", count, get_info().c_str());
1218 }
1219
1220 /**
1221 * Like push_back(), but for more multiple r-value references to move.
1222 *
1223 * @tparam Args
1224 * @param args r-value references to move into this storage
1225 * @see jau::make_darray()
1226 */
1227 template <typename... Args>
1228 constexpr void push_back_list(Args&&... args)
1229 {
1230 const size_type count = sizeof...(Args);
1231
1232 JAU_DARRAY_PRINTF("push_back_list.move.0: %zu elems: this %s\n", count, get_info().c_str());
1233
1234 if( end_ + count >= storage_end_ ) {
1235 grow_storage_move(size() + count);
1236 }
1237 // C++17 fold expression on above C++11 template pack args
1238 ( new (const_cast<pointer_mutable>(end_++)) value_type( std::move(args) ), ... ); // @suppress("Syntax error")
1239
1240 JAU_DARRAY_PRINTF("push_back_list.move.X: %zu elems: this %s\n", count, get_info().c_str());
1241 }
1242
1243 /**
1244 * Generic value_type equal comparator to be user defined for e.g. jau::darray::push_back_unique().
1245 * @param a one element of the equality test.
1246 * @param b the other element of the equality test.
1247 * @return true if both are equal
1248 */
1249 typedef bool(*equal_comparator)(const value_type& a, const value_type& b);
1250
1251 /**
1252 * Like std::vector::push_back(), but only if the newly added element does not yet exist.
1253 * <p>
1254 * Examples
1255 * <pre>
1256 * static jau::darray<Thing>::equal_comparator thingEqComparator =
1257 * [](const Thing &a, const Thing &b) -> bool { return a == b; };
1258 * ...
1259 * jau::darray<Thing> list;
1260 *
1261 * bool added = list.push_back_unique(new_element, thingEqComparator);
1262 * ...
1263 * darray<std::shared_ptr<Thing>> listOfRefs;
1264 * bool added = listOfRefs.push_back_unique(new_element,
1265 * [](const std::shared_ptr<Thing> &a, const std::shared_ptr<Thing> &b) -> bool { return *a == *b; });
1266 * </pre>
1267 * </p>
1268 * @param x the value to be added at the tail, if not existing yet.
1269 * @param comparator the equal comparator to return true if both given elements are equal
1270 * @return true if the element has been uniquely added, otherwise false
1271 */
1272 constexpr bool push_back_unique(const value_type& x, equal_comparator comparator) {
1273 for(auto it = begin_; it != end_; ) {
1274 if( comparator( *it, x ) ) {
1275 return false; // already included
1276 } else {
1277 ++it;
1278 }
1279 }
1280 push_back(x);
1281 return true;
1282 }
1283
1284 /**
1285 * Erase either the first matching element or all matching elements.
1286 * <p>
1287 * Examples
1288 * <pre>
1289 * darray<Thing> list;
1290 * int count = list.erase_matching(element, true,
1291 * [](const Thing &a, const Thing &b) -> bool { return a == b; });
1292 * ...
1293 * static jau::darray<Thing>::equal_comparator thingRefEqComparator =
1294 * [](const std::shared_ptr<Thing> &a, const std::shared_ptr<Thing> &b) -> bool { return *a == *b; };
1295 * ...
1296 * darray<std::shared_ptr<Thing>> listOfRefs;
1297 * int count = listOfRefs.erase_matching(element, false, thingRefEqComparator);
1298 * </pre>
1299 * </p>
1300 * @param x the value to be added at the tail, if not existing yet.
1301 * @param all_matching if true, erase all matching elements, otherwise only the first matching element.
1302 * @param comparator the equal comparator to return true if both given elements are equal
1303 * @return number of erased elements
1304 */
1305 constexpr size_type erase_matching(const value_type& x, const bool all_matching, equal_comparator comparator) {
1306 size_type count = 0;
1307 for(auto it = end_-1; begin_ <= it; --it) {
1308 if( comparator( *it, x ) ) {
1309 erase(it);
1310 ++count;
1311 if( !all_matching ) {
1312 break;
1313 }
1314 }
1315 }
1316 return count;
1317 }
1318
1319 std::string toString() const noexcept {
1320 std::string res("{ " + std::to_string( size() ) + ": ");
1321 int i=0;
1322 jau::for_each_const(*this, [&res, &i](const value_type & e) {
1323 if( 1 < ++i ) { res.append(", "); }
1324 res.append( jau::to_string(e) );
1325 } );
1326 res.append(" }");
1327 return res;
1328 }
1329
1330 std::string get_info() const noexcept {
1331 difference_type cap_ = (storage_end_-begin_);
1332 difference_type size_ = (end_-begin_);
1333 std::string res("darray[this "+jau::to_hexstring(this)+
1334 ", size "+std::to_string(size_)+" / "+std::to_string(cap_)+
1335 ", growth "+std::to_string(growth_factor_)+
1336 ", type[integral "+std::to_string(std::is_integral_v<Value_type>)+
1337 ", trivialCpy "+std::to_string(std::is_trivially_copyable_v<Value_type>)+
1338 "], uses[mmove "+std::to_string(uses_memmove)+
1339 ", realloc "+std::to_string(uses_realloc)+
1340 ", smem "+std::to_string(uses_secmem)+
1341 "], begin "+jau::to_hexstring(begin_)+
1342 ", end "+jau::to_hexstring(end_)+
1343 ", send "+jau::to_hexstring(storage_end_)+
1344 "]");
1345 return res;
1346 }
1347 };
1348
1349 /**
1350 * Construct a darray<T> instance, initialized by move semantics from the variadic (template pack) argument list.
1351 *
1352 * std::initializer_list<T> enforces to copy the created instances into the container,
1353 * since its iterator references to `const` value_type.
1354 *
1355 * This alternative template passes the r-value argument references to darray::push_back_list(),
1356 * hence using `std::move` without copying semantics.
1357 *
1358 * All argument types must be of same type, i.e. std::is_same.
1359 * The deduced darray<T> instance also uses same type as its Value_type.
1360 *
1361 * @tparam First the first argument type, must be same
1362 * @tparam Next all other argument types, must be same
1363 * @tparam
1364 * @param arg1 the first r-value
1365 * @param argsN the other r-values
1366 * @return the new `darray`
1367 * @see darray::push_back_list()
1368 * @see make_darray()
1369 */
1370 template <typename First, typename... Next,
1371 // std::enable_if_t< ( std::is_same<First, Next>::value && ... ), bool> = true>
1372 std::enable_if_t< std::conjunction_v<std::is_same<First, Next>... >, bool> = true>
1373 constexpr darray< First > make_darray(First&& arg1, Next&&... argsN)
1374 {
1375 darray< First > d(1 + sizeof...(Next));
1376 // C++17 fold expression on above C++11 template pack arg1 and argsN
1377 // d.push_back_list( std::forward<First>(arg1), ( std::forward<Next>(argsN), ... ) ); // @suppress("Syntax error")
1378 d.push_back_list( arg1, argsN... ); // @suppress("Syntax error")
1379 return d;
1380 }
1381
1382 /**
1383 * Complement constructor for darray<T> instance, move semantics initializer for one argument.
1384 * @tparam First
1385 * @tparam Next
1386 * @param arg1
1387 * @return
1388 * @see darray::push_back()
1389 * @see darray::push_back_list()
1390 * @see make_darray()
1391 */
1392 template <typename First, typename... Next>
1393 constexpr darray< First > make_darray(First&& arg1)
1394 {
1395 darray< First > d(1);
1396 d.push_back( std::forward<First>(arg1) );
1397 return d;
1398 }
1399
1400 /****************************************************************************************
1401 ****************************************************************************************/
1402
1403 template<typename Value_type, typename Size_type, typename Alloc_type>
1404 std::ostream & operator << (std::ostream &out, const darray<Value_type, Size_type, Alloc_type> &c) {
1405 out << c.toString();
1406 return out;
1407 }
1408
1409 /****************************************************************************************
1410 ****************************************************************************************/
1411
1412 template<typename Value_type, typename Size_type, typename Alloc_type>
1414 if( &rhs == &lhs ) {
1415 return true;
1416 }
1417 return (rhs.size() == lhs.size() && std::equal(rhs.cbegin(), rhs.cend(), lhs.cbegin()));
1418 }
1419 template<typename Value_type, typename Size_type, typename Alloc_type>
1421 return !(rhs==lhs);
1422 }
1423
1424 template<typename Value_type, typename Size_type, typename Alloc_type>
1426 { return std::lexicographical_compare(rhs.cbegin(), rhs.cend(), lhs.cbegin(), lhs.cend()); }
1427
1428 template<typename Value_type, typename Size_type, typename Alloc_type>
1430 { return lhs < rhs; }
1431
1432 template<typename Value_type, typename Size_type, typename Alloc_type>
1434 { return !(lhs < rhs); }
1435
1436 template<typename Value_type, typename Size_type, typename Alloc_type>
1438 { return !(rhs < lhs); }
1439
1440 template<typename Value_type, typename Size_type, typename Alloc_type>
1442 { rhs.swap(lhs); }
1443
1444 /****************************************************************************************
1445 ****************************************************************************************/
1446
1447 /**
1448 * <code>template< class T > is_darray_type<T>::value</code> compile-time Type Trait,
1449 * determining whether the given template class is a - or has a darray type, e.g. jau::cow_darray,
1450 * jau::darray.
1451 */
1452 template< class, class = void >
1453 struct is_darray_type : std::false_type { };
1454
1455 /**
1456 * <code>template< class T > is_darray_type<T>::value</code> compile-time Type Trait,
1457 * determining whether the given template class is a - or has a darray type, e.g. jau::cow_darray,
1458 * jau::darray.
1459 */
1460 template< class T >
1461 struct is_darray_type<T, std::void_t<typename T::darray_tag>> : std::true_type { };
1462
1463 /**@}*/
1464
1465} /* namespace jau */
1466
1467#endif /* JAU_DYN_ARRAY_HPP_ */
#define E_FILE_LINE
Implementation of a dynamic linear array storage, aka vector.
Definition: darray.hpp:148
constexpr void push_front(value_type &&x)
Like std::vector::push_front(), move.
Definition: darray.hpp:1156
const allocator_type & get_allocator_ref() const noexcept
Definition: darray.hpp:723
constexpr iterator insert(const_iterator pos, InputIt first, InputIt last)
Like std::vector::insert(), inserting the value_type range [first, last).
Definition: darray.hpp:1099
constexpr void push_back(InputIt first, InputIt last)
Like std::vector::push_back(), but appends the value_type range [first, last).
Definition: darray.hpp:1188
const value_type * const_iterator
Definition: darray.hpp:165
bool darray_tag
Used to determine whether this type is a darray or has a darray, see ::is_darray_type<T>
Definition: darray.hpp:173
const_reference operator[](size_type i) const noexcept
Like std::vector::operator[](size_type), immutable reference.
Definition: darray.hpp:800
constexpr void swap(darray &x) noexcept
Like std::vector::swap().
Definition: darray.hpp:915
constexpr void assign(InputIt first, InputIt last)
Like std::vector::assign()
Definition: darray.hpp:867
constexpr darray & operator=(const darray &x)
Like std::vector::operator=(&), assignment.
Definition: darray.hpp:541
bool(* equal_comparator)(const value_type &a, const value_type &b)
Generic value_type equal comparator to be user defined for e.g.
Definition: darray.hpp:1249
constexpr void assign(const_iterator first, const_iterator last)
Like std::vector::assign(), but non-template overload using const_iterator.
Definition: darray.hpp:886
static constexpr const bool uses_secmem
Definition: darray.hpp:154
constexpr reference front()
Like std::vector::front(), mutable access.
Definition: darray.hpp:770
constexpr darray(std::initializer_list< value_type > initlist, const allocator_type &alloc=allocator_type())
Create a new instance from an initializer list.
Definition: darray.hpp:679
constexpr const_iterator begin() const noexcept
Definition: darray.hpp:703
constexpr iterator erase(iterator first, const_iterator last)
Like std::vector::erase(), removes the elements in the range [first, last).
Definition: darray.hpp:956
constexpr iterator end() noexcept
Definition: darray.hpp:707
~darray() noexcept
Definition: darray.hpp:685
Size_type size_type
Definition: darray.hpp:166
constexpr size_type capacity() const noexcept
Return the current capacity.
Definition: darray.hpp:738
value_type * iterator
Definition: darray.hpp:164
constexpr const_iterator cend() const noexcept
Definition: darray.hpp:711
constexpr size_type size() const noexcept
Like std::vector::size().
Definition: darray.hpp:763
void reserve(size_type new_capacity)
Like std::vector::reserve(), increases this instance's capacity to new_capacity.
Definition: darray.hpp:840
const_reference at(size_type i) const
Like std::vector::at(size_type), immutable reference.
Definition: darray.hpp:814
Value_type value_type
Definition: darray.hpp:159
constexpr void push_back(value_type &&x)
Like std::vector::push_back(), move.
Definition: darray.hpp:1136
const value_type * const_pointer
Definition: darray.hpp:161
constexpr const_iterator end() const noexcept
Definition: darray.hpp:709
constexpr void push_back(const value_type &x)
Like std::vector::push_back(), copy.
Definition: darray.hpp:1124
constexpr darray(darray &&x) noexcept
Definition: darray.hpp:565
constexpr reference emplace_back(Args &&... args)
Like std::vector::emplace_back(), construct a new element in place at the end().
Definition: darray.hpp:1171
constexpr darray(const darray &x)
Creates a new instance, copying all elements from the given darray.
Definition: darray.hpp:499
Alloc_type allocator_type
Definition: darray.hpp:170
constexpr iterator erase(const size_type first_idx, const size_type last_idx)
Similar to std::vector::erase() using indices, removes the elements in the range [first_idx,...
Definition: darray.hpp:980
constexpr size_type erase_matching(const value_type &x, const bool all_matching, equal_comparator comparator)
Erase either the first matching element or all matching elements.
Definition: darray.hpp:1305
constexpr darray(InputIt first, InputIt last, const allocator_type &alloc=allocator_type())
Creates a new instance, copying all elements from the given template input-iterator value_type range ...
Definition: darray.hpp:661
reference operator[](size_type i) noexcept
Like std::vector::operator[](size_type), mutable reference.
Definition: darray.hpp:807
constexpr iterator insert(const size_type pos_idx, const value_type &x)
Similar to std::vector::insert() using an index, copy.
Definition: darray.hpp:1021
std::string toString() const noexcept
Definition: darray.hpp:1319
constexpr bool empty() const noexcept
Like std::vector::empty().
Definition: darray.hpp:752
constexpr const_pointer data() const noexcept
Like std::vector::data(), const immutable pointer.
Definition: darray.hpp:790
constexpr darray & operator=(darray &&x) noexcept
Like std::vector::operator=(&&), move.
Definition: darray.hpp:588
constexpr const_iterator cbegin() const noexcept
Definition: darray.hpp:705
constexpr bool capacity_reached() const noexcept
Returns true if capacity has been reached and the next push_back() will grow the storage and invalida...
Definition: darray.hpp:758
constexpr pointer data() noexcept
Like std::vector::data(), mutable pointer.
Definition: darray.hpp:795
constexpr darray() noexcept
Default constructor, giving zero capacity and zero memory footprint.
Definition: darray.hpp:474
constexpr darray(size_type capacity, const float growth_factor=DEFAULT_GROWTH_FACTOR, const allocator_type &alloc=allocator_type())
Creating an empty instance with initial capacity and other (default) properties.
Definition: darray.hpp:486
constexpr float growth_factor() const noexcept
Definition: darray.hpp:731
constexpr darray(darray &&x, const float growth_factor, const allocator_type &alloc) noexcept
Definition: darray.hpp:575
constexpr iterator insert(const_iterator pos, const value_type &x)
Like std::vector::insert(), copy.
Definition: darray.hpp:996
constexpr iterator begin() noexcept
Definition: darray.hpp:701
constexpr void push_back_list(const Args &... args)
Like push_back(), but for more multiple const r-value to copy.
Definition: darray.hpp:1205
constexpr void push_back_list(Args &&... args)
Like push_back(), but for more multiple r-value references to move.
Definition: darray.hpp:1228
static constexpr const bool uses_realloc
Definition: darray.hpp:155
constexpr iterator erase(const_iterator pos)
Like std::vector::erase(), removes the elements at pos.
Definition: darray.hpp:940
std::make_signed< size_type >::type difference_type
Definition: darray.hpp:167
constexpr void clear() noexcept
Like std::vector::clear(), but ending with zero capacity.
Definition: darray.hpp:904
allocator_type get_allocator() const noexcept
Definition: darray.hpp:727
constexpr iterator erase(const size_type pos_idx)
Similar to std::vector::erase() using an index, removes the elements at pos_idx.
Definition: darray.hpp:972
constexpr bool push_back_unique(const value_type &x, equal_comparator comparator)
Like std::vector::push_back(), but only if the newly added element does not yet exist.
Definition: darray.hpp:1272
constexpr void shrink_to_fit()
Like std::vector::shrink_to_fit(), but ensured constexpr.
Definition: darray.hpp:852
constexpr iterator insert(const_iterator pos, value_type &&x)
Like std::vector::insert(), move.
Definition: darray.hpp:1037
constexpr const_reference front() const
Like std::vector::front(), immutable access.
Definition: darray.hpp:775
reference at(size_type i)
Like std::vector::at(size_type), mutable reference.
Definition: darray.hpp:824
static constexpr const bool uses_memmove
Definition: darray.hpp:153
constexpr iterator emplace(const_iterator pos, Args &&... args)
Like std::vector::emplace(), construct a new element in place.
Definition: darray.hpp:1070
const value_type & const_reference
Definition: darray.hpp:163
value_type & reference
Definition: darray.hpp:162
constexpr darray(const size_type _capacity, const_iterator first, const_iterator last, const float growth_factor=DEFAULT_GROWTH_FACTOR, const allocator_type &alloc=allocator_type())
Creates a new instance with custom initial storage capacity, copying all elements from the given cons...
Definition: darray.hpp:622
constexpr size_type max_size() const noexcept
Returns std::numeric_limits<difference_type>::max() as the maximum array size.
Definition: darray.hpp:697
std::string get_info() const noexcept
Definition: darray.hpp:1330
static constexpr const float DEFAULT_GROWTH_FACTOR
Default growth factor using the golden ratio 1.618.
Definition: darray.hpp:151
constexpr size_type get_grown_capacity() const noexcept
Return the current capacity() multiplied by the growth factor, minimum is max(capacity()+1,...
Definition: darray.hpp:743
constexpr reference back()
Like std::vector::back(), mutable access.
Definition: darray.hpp:780
constexpr darray(const size_type _capacity, InputIt first, InputIt last, const float growth_factor=DEFAULT_GROWTH_FACTOR, const allocator_type &alloc=allocator_type())
Creates a new instance with custom initial storage capacity, copying all elements from the given temp...
Definition: darray.hpp:644
constexpr void pop_back() noexcept
Like std::vector::pop_back().
Definition: darray.hpp:930
value_type * pointer
Definition: darray.hpp:160
constexpr const_reference back() const
Like std::vector::back(), immutable access.
Definition: darray.hpp:785
constexpr void push_front(const value_type &x)
Like std::vector::push_front(), copy.
Definition: darray.hpp:1148
constexpr darray(const darray &x, const size_type _capacity, const float growth_factor, const allocator_type &alloc)
Creates a new instance with custom initial storage capacity, copying all elements from the given darr...
Definition: darray.hpp:531
constexpr darray(const darray &x, const float growth_factor, const allocator_type &alloc)
Creates a new instance, copying all elements from the given darray.
Definition: darray.hpp:513
#define JAU_DARRAY_PRINTF(...)
Definition: darray.hpp:59
#define JAU_DARRAY_PRINTF0(...)
Definition: darray.hpp:52
constexpr UnaryFunction for_each_const(T &data, UnaryFunction f, std::enable_if_t< is_cow_type< T >::value, bool >=true) noexcept
std::string to_string(const endian_t v) noexcept
Return std::string representation of the given endian.
std::string to_string(const alphabet &v) noexcept
Definition: base_codec.hpp:97
#define PRAGMA_DISABLE_WARNING_PUSH
Definition: cpp_pragma.hpp:76
#define PRAGMA_DISABLE_WARNING_POP
Definition: cpp_pragma.hpp:77
#define PRAGMA_DISABLE_WARNING_NULL_DEREFERENCE
Definition: cpp_pragma.hpp:82
std::ostream & operator<<(std::ostream &out, const cow_darray< Value_type, Size_type, Alloc_type > &c)
void swap(darray< Value_type, Size_type, Alloc_type > &rhs, darray< Value_type, Size_type, Alloc_type > &lhs) noexcept
Definition: darray.hpp:1441
bool operator>=(const cow_darray< Value_type, Size_type, Alloc_type > &rhs, const cow_darray< Value_type, Size_type, Alloc_type > &lhs)
constexpr darray< First > make_darray(First &&arg1, Next &&... argsN)
Construct a darray<T> instance, initialized by move semantics from the variadic (template pack) argum...
Definition: darray.hpp:1373
bool operator>(const cow_darray< Value_type, Size_type, Alloc_type > &rhs, const cow_darray< Value_type, Size_type, Alloc_type > &lhs)
bool operator<(const cow_darray< Value_type, Size_type, Alloc_type > &rhs, const cow_darray< Value_type, Size_type, Alloc_type > &lhs)
void swap(cow_darray< Value_type, Size_type, Alloc_type > &rhs, cow_darray< Value_type, Size_type, Alloc_type > &lhs) noexcept
bool operator<=(const cow_darray< Value_type, Size_type, Alloc_type > &rhs, const cow_darray< Value_type, Size_type, Alloc_type > &lhs)
@ free
Denotes a func::free_target_t.
constexpr T max(const T x, const T y) noexcept
Returns the maximum of two integrals (w/ branching) in O(1)
Definition: base_math.hpp:191
std::string to_hexstring(value_type const &v) noexcept
Produce a lower-case hexadecimal string representation of the given pointer.
__pack(...): Produces MSVC, clang and gcc compatible lead-in and -out macros.
Definition: backtrace.hpp:32
bool operator==(const callocator< T1 > &lhs, const callocator< T2 > &rhs) noexcept
Definition: callocator.hpp:150
bool operator!=(const callocator< T1 > &lhs, const callocator< T2 > &rhs) noexcept
Definition: callocator.hpp:163
STL namespace.
A simple allocator using POSIX C functions: ::malloc(), ::free() and ::realloc().
Definition: callocator.hpp:50
template< class T > is_darray_type<T>::value compile-time Type Trait, determining whether the given t...
Definition: darray.hpp:1453
uint8_t Value_type