jaulib v1.5.0
Jau Support Library (C++, Java, ..)
Loading...
Searching...
No Matches
darray.hpp
Go to the documentation of this file.
1/*
2 * Author: Sven Gothel <sgothel@jausoft.com>
3 * Copyright (c) 2020-2024 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 <algorithm>
29#include <cmath>
30#include <cstddef>
31#include <cstdint>
32#include <cstring>
33#include <initializer_list>
34#include <limits>
35#include <numbers>
36#include <string>
37
38#include <jau/basic_algos.hpp>
39#include <jau/basic_types.hpp>
40#include <jau/callocator.hpp>
41#include <jau/cpp_lang_util.hpp>
42#include <jau/debug.hpp>
44#include <jau/secmem.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, including relative positional access.
69 *
70 * Goals are to support a high-performance CoW dynamic array implementation, jau::cow_darray,<br>
71 * exposing fine grained control over its underlying storage facility.<br>
72 * Further, jau::darray provides high-performance and efficient storage properties on its own.
73 *
74 * This class shall be compliant with <i>C++ named requirements for Container</i>.
75 *
76 * API and design differences to std::vector
77 * - jau::darray adds a parameterized <i>growth factor</i> aspect, see setGrowthFactor(). Defaults to golden ration jau::darray::DEFAULT_GROWTH_FACTOR.
78 * - <i>capacity</i> control via constructor and operations, related to *growth factor*.
79 * - Iterator jau::darray::const_iterator .. are harmonized with jau::cow_ro_iterator .. used in jau:cow_darray.
80 * - ...
81 * - Custom constructor and operations, supporting a more efficient jau::cow_darray implementation.
82 * - Custom template typename Size_type, defaults to jau::nsize_t.
83 * - ...
84 * - <b>TODO</b>: std::initializer_list<T> methods, ctor is provided.
85 *
86 * Implementation differences to std::vector and some details
87 * - Using zero overhead <i>value_type*</i> as iterator type.
88 * - ...
89 * - Storage is operated on three iterator: *begin* <= *end* <= *storage_end*.
90 * - Constructs and destructs value_type via *placement new* within the pre-allocated array capacity. Latter is managed via allocator_type.
91 *
92 * ### Relative access
93 * Additionally to the ransom access, relative positional access via get(), put(), putN()
94 * and position(), limit(), remaining(), clearPosition() is supported, as well as slice() and duplicate().
95 *
96 * 0 <= *position* <= *limit* <= *size* <= *capacity*
97 *
98 * All mutable relative accessors validate range and throw jau::IndexOutOfBoundsError, similar to the at() method.
99 *
100 * @anchor darray_ntt_params
101 * ### Non-Type Template Parameter (NTTP) controlling Value_type memory
102 * @anchor darray_memmove
103 * #### use_memmove
104 * `use_memmove` can be overriden and defaults to `std::is_trivially_copyable_v<Value_type>`.
105 *
106 * 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).
107 *
108 * See [Trivial destructor](https://en.cppreference.com/w/cpp/language/destructor#Trivial_destructor)
109 * being key requirement to [TriviallyCopyable](https://en.cppreference.com/w/cpp/named_req/TriviallyCopyable).
110 * > A trivial destructor is a destructor that performs no action.
111 * > Objects with trivial destructors don't require a delete-expression and may be disposed of by simply deallocating their storage.
112 * > All data types compatible with the C language (POD types) are trivially destructible.`
113 *
114 * However, since the destructor is not being called when using `memmove` on elements within this container,
115 * the requirements are more relaxed and *TriviallyCopyable* not required nor guaranteed, see below.
116 *
117 * `memmove` will be used to move an object inside this container memory only,
118 * i.e. where construction and destruction is controlled.
119 * Not requiring *TriviallyCopyable* constraints `memmove` as follows:
120 * - We can't `memmove` one or more objects into this container, even with an `rvalue` reference.
121 * The `rvalue`'s destructor will be called and potential acquired resources are lost.
122 * - We can `memmove` an object around within this container, i.e. when growing or shrinking the array. (*Used*)
123 * - We can `memmove` an object out of this container to the user. (*Unused*)
124 *
125 * Relaxed requirements for `use_memmove` are:
126 * - Not using inner class pointer to inner class fields or methods (like launching a thread).
127 * - TBD ???
128 *
129 * Since element pointer and iterator are always invalidated for container after storage mutation,
130 * above constraints are not really anything novel and go along with normal std::vector.
131 *
132 * Users may include `typedef container_memmove_compliant` in their Value_type class
133 * to enforce `use_memmove` as follows:
134 * - `typedef std::true_type container_memmove_compliant;`
135 *
136 * @anchor darray_secmem
137 * #### use_secmem
138 * `use_secmem` can be overriden and defaults to `false`.
139 *
140 * `use_secmem`, if enabled, ensures that the underlying memory will be zeroed out
141 * after use and element erasure.
142 *
143 * Users may include `typedef enforce_secmem` in their Value_type class
144 * to enforce `use_secmem` as follows:
145 * - `typedef std::true_type enforce_secmem;`
146 *
147 * @see cow_darray
148 */
149 template <typename Value_type, typename Size_type = jau::nsize_t, typename Alloc_type = jau::callocator<Value_type>,
150 bool use_memmove = std::is_trivially_copyable_v<Value_type> || is_container_memmove_compliant_v<Value_type>,
151 bool use_secmem = is_enforcing_secmem_v<Value_type>
152 >
153 class darray
154 {
155 public:
156 /** Default growth factor using the golden ratio 1.618. See setGrowthFactor(). */
157 constexpr static const float DEFAULT_GROWTH_FACTOR = std::numbers::phi_v<float>; // 1.618f;
158
159 constexpr static const bool uses_memmove = use_memmove;
160 constexpr static const bool uses_secmem = use_secmem;
161 constexpr static const bool uses_realloc = use_memmove && std::is_base_of_v<jau::callocator<Value_type>, Alloc_type>;
162
163 // typedefs' for C++ named requirements: Container
164
167 typedef const value_type* const_pointer;
172 typedef Size_type size_type;
173 typedef std::make_signed_t<size_type> difference_type;
174 // typedef std::reverse_iterator<iterator> reverse_iterator;
175 // typedef std::reverse_iterator<const_iterator> const_reverse_iterator;
176 typedef Alloc_type allocator_type;
177
178 typedef darray<value_type, size_type,
180 use_memmove, use_secmem> self_t;
181
182 /** Used to determine whether this type is a darray or has a darray, see ::is_darray_type<T> */
183 typedef bool darray_tag;
184
185 private:
186 typedef std::remove_const_t<Value_type> value_type_mutable;
187 /** Required to create and move immutable elements, aka const */
188 typedef value_type_mutable* pointer_mutable;
189
190 static constexpr void* voidptr_cast(const_pointer p) { return reinterpret_cast<void*>( const_cast<pointer_mutable>( p ) ); }
191
192 constexpr static size_type DIFF_MAX = std::numeric_limits<difference_type>::max();
193 constexpr static size_type MIN_SIZE_AT_GROW = 10;
194
195 allocator_type m_alloc_inst;
196 float m_growth_factor;
197 pointer m_begin;
198 pointer m_end;
199 pointer m_storage_end;
200 pointer m_position, m_limit;
201
202 public:
203 /** Returns type signature of implementing class's stored value type. */
204 const jau::type_info& valueSignature() const noexcept {
206 }
207
208 /** Returns type signature of implementing class. */
209 const jau::type_info& classSignature() const noexcept {
211 }
212
213 /** Returns growth factor, see setGrowthFactor() for semantics. */
214 constexpr float growthFactor() const noexcept { return m_growth_factor; }
215
216 /**
217 * Sets the growth factor when size() == capacity() is reached for growing operations,
218 * defaults to golden ratio DEFAULT_GROWTH_FACTOR.
219 *
220 * A growth factor of > 1 will grow storage by `max(required_elements, growth_factor*capacity)`,
221 * to give room for further elements (efficiency).
222 *
223 * A growth factor of 1 would only grow storage by required elements.
224 *
225 * A growth factor of [0..1) disables growing the storage, i.e. pins storage, see pinned().
226 *
227 * A growth factor of < 0 denotes storage is pinned() and shared with a parent instance, see slice().
228 * Use has to ensure that the parent storage owner outlives this instance.
229 *
230 * @see pinned()
231 * @see shared()
232 * @see slice()
233 */
234 constexpr void setGrowthFactor(float v) noexcept { m_growth_factor = v; }
235 /** Returns true if growthFactor() < 1, otherwise false. See setGrowthFactor(). */
236 constexpr bool pinned() const { return m_growth_factor < 1.0f; }
237 /** Returns true if growthFactor() < 0, otherwise false. See setGrowthFactor(). */
238 constexpr bool shared() const { return m_growth_factor < 0.0f; }
239
240 private:
241 /**
242 * Allocates a new store using allocator_type.
243 *
244 * Throws jau::IllegalArgumentException if `size_ > std::numeric_limits<difference_type>::max()`, i.e. difference_type maximum.
245 *
246 * Throws jau::OutOfMemoryError if allocator_type::allocate() returns nullptr.
247 *
248 * @param alloc the allocator_type instance
249 * @param size_ the element count, must be <= `std::numeric_limits<difference_type>::max()`
250 * @return nullptr if given `0 == size_` or the newly allocated memory
251 */
252 [[nodiscard]] constexpr value_type * allocStore(const size_type size_) {
253 if( 0 != size_ ) {
254 if( size_ > DIFF_MAX ) {
255 throw jau::IllegalArgumentError("alloc "+std::to_string(size_)+" > difference_type max "+
256 std::to_string(DIFF_MAX), E_FILE_LINE);
257 }
258 if( pinned() ) {
259 throw jau::IllegalStateError("alloc "+std::to_string(size_)+" elements * "+
260 std::to_string(sizeof(value_type))+" bytes/element = "+ // NOLINT(bugprone-sizeof-expression)
261 std::to_string(size_ * sizeof(value_type))+" bytes -> pinned: "+getInfo(), E_FILE_LINE); // NOLINT(bugprone-sizeof-expression)
262 return nullptr;
263 }
264 value_type * m = m_alloc_inst.allocate(size_);
265 if( nullptr == m && size_ > 0 ) {
266 // NOLINTBEGIN(bugprone-sizeof-expression)
267 throw jau::OutOfMemoryError("alloc "+std::to_string(size_)+" elements * "+
268 std::to_string(sizeof(value_type))+" bytes/element = "+
269 std::to_string(size_ * sizeof(value_type))+" bytes -> nullptr", E_FILE_LINE);
270 // NOLINTEND(bugprone-sizeof-expression)
271 }
272 return m;
273 }
274 return nullptr;
275 }
276
277 template<class _Alloc_type>
278 [[nodiscard]] constexpr value_type * reallocStore(const size_type new_capacity_,
279 std::enable_if_t< std::is_base_of_v<jau::callocator<value_type>, _Alloc_type>, bool > = true )
280 {
281 if( pinned() ) {
282 throw jau::IllegalStateError("realloc "+std::to_string(new_capacity_)+" elements * "+
283 std::to_string(sizeof(value_type))+" bytes/element = "+
284 std::to_string(new_capacity_ * sizeof(value_type))+" bytes -> pinned: "+getInfo(), E_FILE_LINE);
285 return nullptr;
286 }
287 if( new_capacity_ > DIFF_MAX ) {
288 throw jau::IllegalArgumentError("realloc "+std::to_string(new_capacity_)+" > difference_type max "+
289 std::to_string(DIFF_MAX), E_FILE_LINE);
290 }
291 value_type * m = m_alloc_inst.reallocate(m_begin, m_storage_end-m_begin, new_capacity_);
292 if( nullptr == m && new_capacity_ > 0 ) {
293 free(const_cast<pointer_mutable>(m_begin)); // has not been touched by realloc
294 throw jau::OutOfMemoryError("realloc "+std::to_string(new_capacity_)+" elements * "+
295 std::to_string(sizeof(value_type))+" bytes/element = "+
296 std::to_string(new_capacity_ * sizeof(value_type))+" bytes -> nullptr", E_FILE_LINE);
297 }
298 return m;
299 }
300 template<class _Alloc_type>
301 [[nodiscard]] constexpr value_type * reallocStore(const size_type new_capacity_,
302 std::enable_if_t< !std::is_base_of_v<jau::callocator<value_type>, _Alloc_type>, bool > = true )
303 {
304 (void)new_capacity_;
305 throw jau::UnsupportedOperationException("realloc not supported on non allocator_type not based upon jau::callocator", E_FILE_LINE);
306 }
307
308 constexpr void freeStoreCheck() {
309 if( shared() ) {
310 throw jau::IllegalStateError("freeStore -> shared: "+getInfo(), E_FILE_LINE);
311 }
312 if( m_begin && !shared() ) {
313 m_alloc_inst.deallocate(m_begin, m_storage_end-m_begin);
314 }
315 }
316 constexpr void freeStore() noexcept {
317 if( m_begin && !shared() ) {
318 m_alloc_inst.deallocate(m_begin, m_storage_end-m_begin);
319 }
320 }
321
322 constexpr void clear_iterator() noexcept {
323 m_begin = nullptr;
324 m_end = nullptr;
325 m_storage_end = nullptr;
326 m_position = nullptr;
327 m_limit = nullptr;
328 }
329
330 constexpr void set_iterator(pointer new_storage_, difference_type size_, difference_type capacity_) noexcept {
331 const difference_type pos = std::min<difference_type>(size_, m_position - m_begin);
332 m_begin = new_storage_;
333 m_end = new_storage_+size_;
334 m_storage_end = new_storage_+capacity_;
335 m_position = m_begin + pos;
336 m_limit = m_end;
337 }
338
339 constexpr void set_iterator_end(difference_type size_, difference_type capacity_) noexcept {
340 m_end = m_begin+size_;
341 m_storage_end = m_begin+capacity_;
342 m_limit = m_end;
343 if( m_position > m_limit) { m_position = m_limit; }
344 }
345
346 constexpr void dtor_one(iterator pos) {
347 JAU_DARRAY_PRINTF0("dtor [%zd], count 1\n", (pos-m_begin));
348 ( pos )->~value_type(); // placement new -> manual destruction!
349 if constexpr ( uses_secmem ) {
350 zero_bytes_sec(voidptr_cast(pos), sizeof(value_type));
351 }
352 }
353
354 constexpr size_type dtor_range(iterator first, const_iterator last) {
355 size_type count=0;
356 JAU_DARRAY_PRINTF0("dtor [%zd .. %zd], count %zd\n", (first-m_begin), (last-m_begin)-1, (last-first)-1);
357 for(; first < last; ++first, ++count ) {
358 ( first )->~value_type(); // placement new -> manual destruction!
359 }
360 if constexpr ( uses_secmem ) {
361 zero_bytes_sec(voidptr_cast(last-count), count*sizeof(value_type));
362 }
363 return count;
364 }
365
366 constexpr void ctor_copy_range(pointer dest, iterator first, const_iterator last) {
367 JAU_DARRAY_PRINTF0("ctor_copy_range [%zd .. %zd] -> ??, dist %zd\n", (first-m_begin), (last-m_begin)-1, (last-first)-1);
368 /**
369 * TODO
370 *
371 * g++ (Debian 12.2.0-3) 12.2.0, Debian 12 Bookworm 2022-10-17
372 * g++ bug: False positive of '-Wnull-dereference'
373 * See <https://gcc.gnu.org/bugzilla/show_bug.cgi?id=86172>
374 *
375In copy constructor ‘std::__shared_count<_Lp>::__shared_count(const std::__shared_count<_Lp>&) [with __gnu_cxx::_Lock_policy _Lp = __gnu_cxx::_S_atomic]’,
376 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,
377 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,
378 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,
379 ...
380/usr/include/c++/12/bits/shared_ptr_base.h:1075:9: warning: potential null pointer dereference [-Wnull-dereference]
381 1075 | : _M_pi(__r._M_pi)
382 | ^~~~~~~~~~~~~~~~
383 */
386 for(; first < last; ++dest, ++first) {
387 new (const_cast<pointer_mutable>(dest)) value_type( *first ); // placement new / TODO: See above
388 }
390 }
391 constexpr pointer clone_range(iterator first, const_iterator last) {
392 JAU_DARRAY_PRINTF0("clone_range [0 .. %zd], count %zd\n", (last-first)-1, (last-first)-1);
393 pointer dest = allocStore(size_type(last-first));
394 ctor_copy_range(dest, first, last);
395 return dest;
396 }
397 constexpr pointer clone_range(const size_type dest_capacity, iterator first, const_iterator last) {
398 JAU_DARRAY_PRINTF0("clone_range [0 .. %zd], count %zd -> %d\n", (last-m_begin)-1, (last-first)-1, (int)dest_capacity);
399 pointer dest = allocStore(dest_capacity);
400 ctor_copy_range(dest, first, last);
401 return dest;
402 }
403 constexpr void ctor_copy_range_check(pointer dest, iterator first, const_iterator last) {
404 JAU_DARRAY_PRINTF0("ctor_copy_range_check [%zd .. %zd] -> ??, dist %zd\n", (first-m_begin), (last-m_begin)-1, (last-first)-1);
405 if( first > last ) {
406 throw jau::IllegalArgumentError("first "+toHexString(first)+" > last "+toHexString(last), E_FILE_LINE);
407 }
408 for(; first < last; ++dest, ++first) {
409 new (const_cast<pointer_mutable>(dest)) value_type( *first ); // placement new
410 }
411 }
412 constexpr pointer clone_range_check(const size_type dest_capacity, iterator first, const_iterator last) {
413 JAU_DARRAY_PRINTF0("clone_range_check [%zd .. %zd], count %zd -> %d\n", (first-m_begin), (last-m_begin)-1, (last-first)-1, (int)dest_capacity);
414 if( dest_capacity < size_type(last-first) ) {
415 throw jau::IllegalArgumentError("capacity "+std::to_string(dest_capacity)+" < source range "+
416 std::to_string(difference_type(last-first)), E_FILE_LINE);
417 }
418 pointer dest = allocStore(dest_capacity);
419 ctor_copy_range_check(dest, first, last);
420 return dest;
421 }
422
423 constexpr void ctor_copy_value(pointer dest, size_type count, const value_type& val) {
424 if( m_begin > dest || dest + count > m_end ) {
425 throw jau::IllegalArgumentError("dest "+jau::to_string( dest )+" + "+jau::to_string( count )+" not within ["+
426 jau::to_string( m_begin )+".."+jau::to_string( m_end )+")", E_FILE_LINE);
427 }
428 if( 0 < count ) {
429 for(size_type i=0; i < count; ++i, ++dest) {
430 new (const_cast<pointer_mutable>(dest)) value_type( val ); // placement new // NOLINT(bugprone-multi-level-implicit-pointer-conversion): OK and intended
431 }
432 }
433 }
434 template< class InputIt >
435 constexpr static void ctor_copy_range_foreign(pointer dest, InputIt first, InputIt last) { // NOLINT(performance-unnecessary-value-param)
436 if( first > last ) {
437 throw jau::IllegalArgumentError("first "+jau::to_string( first )+" > last "+
438 jau::to_string( last ), E_FILE_LINE);
439 }
440 for(; first != last; ++dest, ++first) {
441 new (const_cast<pointer_mutable>(dest)) value_type( *first ); // placement new // NOLINT(bugprone-multi-level-implicit-pointer-conversion): OK and intended
442 }
443 }
444 template< class InputIt >
445 constexpr pointer clone_range_foreign(const size_t dest_capacity, InputIt first, InputIt last) { // NOLINT(performance-unnecessary-value-param)
446 if( dest_capacity > std::numeric_limits<size_type>::max() ) {
447 throw jau::IllegalArgumentError("capacity "+std::to_string(dest_capacity)+" > size_type max "+
448 std::to_string(std::numeric_limits<size_type>::max()), E_FILE_LINE);
449 }
450 if( dest_capacity < size_type(last-first) ) {
451 throw jau::IllegalArgumentError("capacity "+std::to_string(dest_capacity)+" < source range "+
452 std::to_string(difference_type(last-first)), E_FILE_LINE);
453 }
454 pointer dest = allocStore(size_type(dest_capacity));
455 ctor_copy_range_foreign(dest, first, last);
456 return dest;
457 }
458
459 constexpr void realloc_storage_move(const size_type new_capacity) {
460 if constexpr ( !uses_memmove ) {
461 pointer new_storage = allocStore(new_capacity);
462 {
463 iterator dest = new_storage;
464 iterator first = m_begin;
465 for(; first < m_end; ++dest, ++first) {
466 new (const_cast<pointer_mutable>(dest)) value_type( std::move( *first ) ); // placement new
467 dtor_one(first); // manual destruction, even after std::move (object still exists)
468 }
469 }
470 freeStoreCheck();
471 set_iterator(new_storage, size(), new_capacity);
472 } else if constexpr ( uses_realloc ) {
473 pointer new_storage = reallocStore<allocator_type>(new_capacity);
474 set_iterator(new_storage, size(), new_capacity);
475 } else {
476 pointer new_storage = allocStore(new_capacity);
477 ::memcpy(voidptr_cast(new_storage),
478 m_begin, (uint8_t*)m_end-(uint8_t*)m_begin); // we can simply copy the memory over, also no overlap
479 freeStoreCheck();
480 set_iterator(new_storage, size(), new_capacity);
481 }
482 }
483 constexpr void grow_storage_move(size_type add=1) {
484 realloc_storage_move( get_grown_capacity(add) );
485 }
486
487 constexpr void move_elements(iterator dest, const_iterator first, const difference_type count) {
488 // Debatable here: "Moved source array has been taken over, flush sources' pointer to avoid value_type dtor releasing taken resources!"
489 // Debatable, b/c is this even possible for user to hold an instance the way, that a dtor gets called? Probably not.
490 // Hence we leave it to 'uses_secmem' to zero_bytes_sec...
491 if constexpr ( uses_memmove ) {
492 // handles overlap
493 // NOLINTNEXTLINE(bugprone-undefined-memory-manipulation)
494 ::memmove(voidptr_cast(dest),
495 first, sizeof(value_type)*count);
496 if constexpr ( uses_secmem ) {
497 if( dest < first ) {
498 // move elems left
499 JAU_DARRAY_PRINTF0("move_elements.mmm.left [%zd .. %zd] -> %zd, dist %zd\n", (first-m_begin), ((first + count)-m_begin)-1, (dest-m_begin), (first-dest));
500 zero_bytes_sec(voidptr_cast(dest+count), (first-dest)*sizeof(value_type));
501 } else {
502 // move elems right
503 JAU_DARRAY_PRINTF0("move_elements.mmm.right [%zd .. %zd] -> %zd, dist %zd, size %zu\n", (first-m_begin), ((first + count)-m_begin)-1, (dest-m_begin), (dest-first), (dest-first)*sizeof(value_type));
506 zero_bytes_sec(voidptr_cast(first), (dest-first)*sizeof(value_type)); // TODO: See above
508 }
509 }
510 } else {
511 if( dest < first ) {
512 // move elems left
513 const_iterator last = first + count;
514 JAU_DARRAY_PRINTF0("move_elements.def.left [%zd .. %zd] -> %zd, dist %zd\n", (first-m_begin), (last-m_begin)-1, (dest-m_begin), (first-dest));
515 for(; first < last; ++dest, ++first ) {
516 new (const_cast<pointer_mutable>(dest)) value_type( std::move( *first ) ); // placement new
517 dtor_one( const_cast<value_type*>( first ) ); // manual destruction, even after std::move (object still exists)
518 }
519 } else {
520 // move elems right
521 iterator last = const_cast<iterator>(first + count);
522 JAU_DARRAY_PRINTF0("move_elements.def.right [%zd .. %zd] -> %zd, dist %zd\n", (first-m_begin), (last-m_begin)-1, (dest-m_begin), (dest-first));
523 dest += count - 1;
524 for(--last; first <= last; --dest, --last ) {
525 new (const_cast<pointer_mutable>(dest)) value_type( std::move( *last ) ); // placement new
526 dtor_one( last ); // manual destruction, even after std::move (object still exists)
527 }
528 }
529 }
530 }
531
532
533 protected:
534 /** Slicing ctor. Marks the created buffer shared() and pinned() and the given parent pinned(). */
536 : m_alloc_inst( parent.m_alloc_inst ), m_growth_factor( /* shared+pinned */ -1 ),
537 m_begin( begin ), m_end( m_begin + size ), m_storage_end( m_begin + size ),
538 m_position(position), m_limit(limit)
539 {
540 if( m_begin > parent.end() || m_end > parent.end() || m_position > m_end || m_limit > m_end ) {
541 throw jau::IllegalArgumentError("Slice: Parent "+parent.getInfo()+", this "+getInfo(), E_FILE_LINE);
542 }
543 parent.m_growth_factor = 0; // pinned
544 JAU_DARRAY_PRINTF("ctor 3: %s\n", getInfo().c_str());
545 }
546
547 public:
548
549 // ctor w/o elements
550
551 /**
552 * Default constructor, giving zero capacity and zero memory footprint.
553 */
554 constexpr darray() noexcept
555 : m_alloc_inst(), m_growth_factor(DEFAULT_GROWTH_FACTOR),
556 m_begin( nullptr ), m_end( nullptr ), m_storage_end( nullptr ),
557 m_position(m_begin), m_limit(m_end)
558 {
559 JAU_DARRAY_PRINTF("ctor def: %s\n", getInfo().c_str());
560 }
561
562 /**
563 * Creating an empty instance with initial capacity and other (default) properties.
564 * @param capacity initial capacity of the new instance.
565 * @param growth_factor given growth factor, defaults to DEFAULT_GROWTH_FACTOR. See setGrowthFactor().
566 * @param alloc given allocator_type, defaults to allocator_type()
567 */
568 constexpr explicit darray(size_type capacity, const float growth_factor=DEFAULT_GROWTH_FACTOR, const allocator_type& alloc = allocator_type())
569 : m_alloc_inst( alloc ), m_growth_factor( growth_factor ),
570 m_begin( allocStore(capacity) ), m_end( m_begin ), m_storage_end( m_begin + capacity ),
571 m_position(m_begin), m_limit(m_end)
572 {
573 JAU_DARRAY_PRINTF("ctor 1: %s\n", getInfo().c_str());
574 }
575
576 /**
577 * Creating a `size`d instance with initial size elements with default `value`.
578 * @param std::nullptr_t argument to distinguish constructor argument overload
579 * @param size initial capacity and size of new instance
580 * @param value initial value for the size elements, defaults to value_type()
581 * @param growth_factor given growth factor, defaults to DEFAULT_GROWTH_FACTOR. See setGrowthFactor().
582 * @param alloc given allocator_type, defaults to allocator_type()
583 */
584 constexpr explicit darray(std::nullptr_t /*dummy*/, size_type size, value_type value=value_type(), const float growth_factor=DEFAULT_GROWTH_FACTOR, const allocator_type& alloc = allocator_type())
585 : m_alloc_inst( alloc ), m_growth_factor( growth_factor ),
586 m_begin( allocStore(size) ), m_end( m_begin + size ), m_storage_end( m_begin + size ),
587 m_position(m_begin), m_limit(m_end)
588 {
589 ctor_copy_value(m_begin, size, value);
590 JAU_DARRAY_PRINTF("ctor 2: %s\n", getInfo().c_str());
591 }
592
593 // copy_ctor on darray elements
594
595 /**
596 * Creates a new instance, copying all elements from the given darray.<br>
597 * Capacity and size will equal the given array, i.e. the result is a trimmed jau::darray.
598 *
599 * Throws jau::IllegalStateError if the source instance is sliced, i.e. sharing memory
600 *
601 * @param x the given darray, all elements will be copied into the new instance.
602 */
603 constexpr darray(const darray& x)
604 : m_alloc_inst( x.m_alloc_inst ), m_growth_factor( x.m_growth_factor ),
605 m_begin( clone_range(x.m_begin, x.m_end) ), m_end( m_begin + x.size() ), m_storage_end( m_begin + x.size() ),
606 m_position(m_begin), m_limit(m_end)
607 {
608 JAU_DARRAY_PRINTF("ctor copy0: this %s\n", getInfo().c_str());
609 JAU_DARRAY_PRINTF("ctor copy0: x %s\n", x.getInfo().c_str());
610 }
611
612 /**
613 * Creates a new instance, copying all elements from the given darray.<br>
614 * Capacity and size will equal the given array, i.e. the result is a trimmed jau::darray.
615 *
616 * Throws jau::IllegalStateError if the source instance is sliced, i.e. sharing memory
617 *
618 * @param x the given darray, all elements will be copied into the new instance.
619 * @param growth_factor custom growth factor
620 * @param alloc custom allocator_type instance
621 */
622 constexpr explicit darray(const darray& x, const float growth_factor, const allocator_type& alloc)
623 : m_alloc_inst( alloc ), m_growth_factor( growth_factor ),
624 m_begin( clone_range(x.m_begin, x.m_end) ), m_end( m_begin + x.size() ), m_storage_end( m_begin + x.size() ),
625 m_position(m_begin), m_limit(m_end)
626 {
627 JAU_DARRAY_PRINTF("ctor copy1: this %s\n", getInfo().c_str());
628 JAU_DARRAY_PRINTF("ctor copy1: x %s\n", x.getInfo().c_str());
629 }
630
631 /**
632 * Creates a new instance with custom initial storage capacity, copying all elements from the given darray.<br>
633 * Size will equal the given array.
634 *
635 * Throws jau::IllegalArgumentException if `_capacity < x.size()`.
636 *
637 * @param x the given darray, all elements will be copied into the new instance.
638 * @param _capacity custom initial storage capacity
639 * @param growth_factor custom growth factor, see setGrowthFactor().
640 * @param alloc custom allocator_type instance
641 */
642 constexpr explicit darray(const darray& x, const size_type _capacity, const float growth_factor, const allocator_type& alloc)
643 : m_alloc_inst( alloc ), m_growth_factor( growth_factor ),
644 m_begin( clone_range( _capacity, x.m_begin, x.m_end) ), m_end( m_begin + x.size() ), m_storage_end( m_begin + _capacity ),
645 m_position(m_begin), m_limit(m_end)
646 {
647 JAU_DARRAY_PRINTF("ctor copy2: this %s\n", getInfo().c_str());
648 JAU_DARRAY_PRINTF("ctor copy2: x %s\n", x.getInfo().c_str());
649 }
650
651 /**
652 * Like std::vector::operator=(&), assignment
653 */
654 constexpr darray& operator=(const darray& x) {
655 JAU_DARRAY_PRINTF("assignment copy.0: this %s\n", getInfo().c_str());
656 JAU_DARRAY_PRINTF("assignment copy.0: x %s\n", x.getInfo().c_str());
657 if( this != &x ) {
658 const size_type capacity_ = capacity();
659 const size_type x_size_ = x.size();
660 dtor_range(m_begin, m_end);
661 m_growth_factor = x.m_growth_factor;
662 if( x_size_ > capacity_ ) {
663 const difference_type pos = std::min<difference_type>(x_size_, m_position - m_begin);
664 freeStoreCheck();
665 m_begin = clone_range(x_size_, x.m_begin, x.m_end);
666 m_position = m_begin + pos;
667 set_iterator_end(x_size_, x_size_);
668 } else {
669 ctor_copy_range(m_begin, x.m_begin, x.m_end);
670 set_iterator_end(x_size_, capacity_);
671 }
672 }
673 JAU_DARRAY_PRINTF("assignment copy.X: this %s\n", getInfo().c_str());
674 JAU_DARRAY_PRINTF("assignment copy.X: x %s\n", x.getInfo().c_str());
675 return *this;
676 }
677
678 // move_ctor on darray elements
679
680 constexpr darray(darray && x) noexcept
681 : m_alloc_inst( std::move(x.m_alloc_inst) ), m_growth_factor( x.m_growth_factor ),
682 m_begin( std::move(x.m_begin) ), m_end( std::move(x.m_end) ), m_storage_end( std::move(x.m_storage_end) ),
683 m_position( std::move(x.m_position) ), m_limit( std::move(x.m_limit) )
684 {
685 JAU_DARRAY_PRINTF("ctor move0: this %s\n", getInfo().c_str());
686 JAU_DARRAY_PRINTF("ctor move0: x %s\n", x.getInfo().c_str());
687 // Moved source array has been taken over, flush sources' pointer to avoid value_type dtor releasing taken resources!
688 x.clear_iterator();
689 }
690
691 constexpr explicit darray(darray && x, const float growth_factor, const allocator_type& alloc) noexcept
692 : m_alloc_inst( std::move(alloc) ), m_growth_factor( growth_factor ),
693 m_begin( std::move(x.m_begin) ), m_end( std::move(x.m_end) ), m_storage_end( std::move(x.m_storage_end) ),
694 m_position( std::move(x.m_position) ), m_limit( std::move(x.m_limit) )
695 {
696 JAU_DARRAY_PRINTF("ctor move1: this %s\n", getInfo().c_str());
697 JAU_DARRAY_PRINTF("ctor move1: x %s\n", x.getInfo().c_str());
698 // Moved source array has been taken over, flush sources' pointer to avoid value_type dtor releasing taken resources!
699 x.clear_iterator();
700 }
701
702 /**
703 * Like std::vector::operator=(&&), move.
704 */
705 constexpr darray& operator=(darray&& x) noexcept {
706 JAU_DARRAY_PRINTF("assignment move.0: this %s\n", getInfo().c_str());
707 JAU_DARRAY_PRINTF("assignment move.0: x %s\n", x.getInfo().c_str());
708 if( this != &x ) {
709 clear(true);
710 m_alloc_inst = std::move(x.m_alloc_inst);
711 m_growth_factor = x.m_growth_factor;
712 m_begin = std::move(x.m_begin);
713 m_end = std::move(x.m_end);
714 m_storage_end = std::move(x.m_storage_end);
715 m_position = std::move(x.m_position);
716 m_limit = std::move(x.m_limit);
717
718 // Moved source array has been taken over, flush sources' pointer to avoid value_type dtor releasing taken resources!
719 x.clear_iterator();
720 }
721 JAU_DARRAY_PRINTF("assignment move.X: this %s\n", getInfo().c_str());
722 JAU_DARRAY_PRINTF("assignment move.X: x %s\n", x.getInfo().c_str());
723 return *this;
724 }
725
726 // ctor on const_iterator and foreign template iterator
727
728 /**
729 * Creates a new instance with custom initial storage capacity,
730 * copying all elements from the given const_iterator value_type range [first, last).<br>
731 * Size will equal the range [first, last), i.e. `size_type(last-first)`.
732 *
733 * Throws jau::IllegalArgumentException if `_capacity < size_type(last - first)`.
734 *
735 * @param _capacity custom initial storage capacity
736 * @param first const_iterator to first element of value_type range [first, last)
737 * @param last const_iterator to last element of value_type range [first, last)
738 * @param growth_factor given growth factor, defaults to DEFAULT_GROWTH_FACTOR. See setGrowthFactor().
739 * @param alloc custom allocator_type instance
740 */
741 constexpr explicit darray(const size_type _capacity, const_iterator first, const_iterator last,
742 const float growth_factor=DEFAULT_GROWTH_FACTOR, const allocator_type& alloc = allocator_type())
743 : m_alloc_inst( alloc ), m_growth_factor( growth_factor ),
744 m_begin( clone_range_check(_capacity, first, last) ), m_end(m_begin + size_type(last - first) ), m_storage_end( m_begin + _capacity ),
745 m_position(m_begin), m_limit(m_end)
746 {
747 JAU_DARRAY_PRINTF("ctor iters0: %s\n", getInfo().c_str());
748 }
749
750 /**
751 * Creates a new instance with custom initial storage capacity,
752 * copying all elements from the given template input-iterator value_type range [first, last).<br>
753 * Size will equal the range [first, last), i.e. `size_type(last-first)`.
754 *
755 * Throws jau::IllegalArgumentException if `_capacity < size_type(last - first)`.
756 *
757 * @tparam InputIt template input-iterator custom type
758 * @param _capacity custom initial storage capacity
759 * @param first template input-iterator to first element of value_type range [first, last)
760 * @param last template input-iterator to last element of value_type range [first, last)
761 * @param growth_factor given growth factor, defaults to DEFAULT_GROWTH_FACTOR. See setGrowthFactor().
762 * @param alloc custom allocator_type instance
763 */
764 template< class InputIt >
765 constexpr explicit darray(const size_type _capacity, InputIt first, InputIt last, // NOLINT(performance-unnecessary-value-param)
766 const float growth_factor=DEFAULT_GROWTH_FACTOR, const allocator_type& alloc = allocator_type())
767 : m_alloc_inst( alloc ), m_growth_factor( growth_factor ),
768 m_begin( clone_range_foreign(_capacity, first, last) ), m_end(m_begin + size_type(last - first) ), m_storage_end( m_begin + _capacity ),
769 m_position(m_begin), m_limit(m_end)
770 {
771 JAU_DARRAY_PRINTF("ctor iters1: %s\n", getInfo().c_str());
772 }
773
774 /**
775 * Creates a new instance,
776 * copying all elements from the given template input-iterator value_type range [first, last).<br>
777 * Size will equal the range [first, last), i.e. `size_type(last-first)`.
778 * @tparam InputIt template input-iterator custom type
779 * @param first template input-iterator to first element of value_type range [first, last)
780 * @param last template input-iterator to last element of value_type range [first, last)
781 * @param alloc custom allocator_type instance
782 */
783 template< class InputIt >
784 constexpr darray(InputIt first, InputIt last, const allocator_type& alloc = allocator_type()) // NOLINT(performance-unnecessary-value-param)
785 : m_alloc_inst( alloc ), m_growth_factor( DEFAULT_GROWTH_FACTOR ),
786 m_begin( clone_range_foreign(size_type(last - first), first, last) ), m_end(m_begin + size_type(last - first) ),
787 m_storage_end( m_begin + size_type(last - first) ),
788 m_position(m_begin), m_limit(m_end)
789 {
790 JAU_DARRAY_PRINTF("ctor iters2: %s\n", getInfo().c_str());
791 }
792
793 /**
794 * Create a new instance from an initializer list.
795 *
796 * Using the `std::initializer_list` requires to *copy* the given value_type objects into this darray.
797 *
798 * To utilize more efficient move semantics, see push_back_list() and jau::make_darray().
799 *
800 * @param initlist initializer_list.
801 * @param alloc allocator
802 * @see push_back_list()
803 * @see jau::make_darray()
804 */
805 constexpr darray(std::initializer_list<value_type> initlist, const allocator_type& alloc = allocator_type())
806 : m_alloc_inst( alloc ), m_growth_factor( DEFAULT_GROWTH_FACTOR ),
807 m_begin( clone_range_foreign(initlist.size(), initlist.begin(), initlist.end()) ),
808 m_end(m_begin + initlist.size() ), m_storage_end( m_begin + initlist.size() ),
809 m_position(m_begin), m_limit(m_end)
810 {
811 JAU_DARRAY_PRINTF("ctor initlist: %s\n", getInfo().c_str());
812 }
813
814 ~darray() noexcept {
815 JAU_DARRAY_PRINTF("dtor: %s\n", getInfo().c_str());
816 clear(true);
817 }
818
819 /**
820 * Returns `std::numeric_limits<difference_type>::max()` as the maximum array size.
821 * <p>
822 * We rely on the signed `difference_type` for pointer arithmetic,
823 * deducing ranges from iterator.
824 * </p>
825 */
826 constexpr static size_type max_size() noexcept { return DIFF_MAX; }
827
828 // iterator
829
830 constexpr iterator begin() noexcept { return m_begin; }
831 constexpr const_iterator begin() const noexcept { return m_begin; }
832 constexpr const_iterator cbegin() const noexcept { return m_begin; }
833
834 constexpr iterator end() noexcept { return m_end; }
835 constexpr const_iterator end() const noexcept { return m_end; }
836 constexpr const_iterator cend() const noexcept { return m_end; }
837
838#if 0
839 constexpr iterator storage_end() noexcept { return m_storage_end; }
840 constexpr const_iterator storage_end() const noexcept { return m_storage_end; }
841 constexpr const_iterator cstorage_end() const noexcept { return m_storage_end; }
842#endif
843
844 //
845 // relative positional access
846 //
847
848 /** Next relative read/write element index, with 0 <= position <= limit.*/
849 constexpr size_type position() const noexcept { return size_type(m_position - m_begin); }
850 /** Pointer to mutable next relative read/write element index, with 0 <= position <= limit.*/
851 constexpr pointer position_ptr() noexcept { return m_position; }
852 /** Pointer to immutable next relative read/write element index, with 0 <= position <= limit.*/
853 constexpr const_pointer position_ptr() const noexcept { return m_position; }
854 /** Sets position. Throws exception if new position is > limit. */
856 pointer p = m_begin + v;
857 if(p > m_limit) {
858 throw jau::IndexOutOfBoundsError(std::to_string(v), std::to_string(limit()), E_FILE_LINE);
859 }
860 m_position = p;
861 return *this;
862 }
863
864 /** Read/write limit, one element beyond maximum index with limit <= size/end. */
865 constexpr size_type limit() const noexcept { return size_type(m_limit - m_begin); }
866 /** Pointer to immutable read/write limit, one element beyond maximum element with limit <= size/end. */
867 constexpr const_pointer limit_ptr() const noexcept { return m_limit; }
868 /** Sets new limit and adjusts position if new limit is below. Throws exception if new limit is > size/end. */
870 pointer p = m_begin + v;
871 if(p > m_end) {
872 throw jau::IndexOutOfBoundsError(std::to_string(v), std::to_string(size()), E_FILE_LINE);
873 }
874 m_limit = p;
875 if (m_position > p) { m_position = p; }
876 return *this;
877 }
878
879 /** Sets limit to position and position to zero. */
880 constexpr self_t& flip() noexcept {
881 m_limit = m_position;
882 m_position = m_begin;
883 return *this;
884 }
885
886 /** Sets position to zero. */
887 constexpr self_t& rewind() noexcept {
888 m_position = m_begin;
889 return *this;
890 }
891
892 /**
893 * Clears the relative position and limit w/o destructing elements nor mutating storage.
894 *
895 * Sets position to zero and limit to size.
896 *
897 * @see position()
898 * @see limit()
899 * @see put()
900 * @see get()
901 */
902 constexpr self_t& clearPosition() noexcept {
903 m_position = m_begin;
904 m_limit = m_end;
905 return *this;
906 }
907
908 /**
909 * Relative get() for single value reference, increasing position() by one.
910 *
911 * Throws if position() >= limit().
912 */
914 if( m_position < m_limit ) {
915 return *(m_position++);
916 }
918 }
919
920 /**
921 * Relative put() for single value, increasing position().
922 *
923 * Grows storage and/or moves limit if required and grow==True().
924 *
925 * Throws if position() == limit().
926 *
927 * @param v value to be written
928 * @param grow set to Bool::True if allowing to grow, otherwise exception is thrown if position() == limit(). Defaults to Bool::False.
929 * @see setGrowthFactor()
930 */
932 if( *grow && m_position == m_limit ) {
933 if( m_limit == m_storage_end ) {
934 grow_storage_move();
935 resize(size()+1);
936 } else if( limit() == size() ) {
937 resize(size()+1);
938 } else {
939 m_limit++;
940 }
941 }
942 if( m_position < m_limit ) {
943 *(m_position++) = v;
944 return *this;
945 }
947 }
948
949 /**
950 * Relative put() for an initializer list of same type, increasing position().
951 *
952 * Grows storage and/or moves limit if required and grow==True().
953 *
954 * Throws if position() + count > limit().
955 *
956 * @param initlist values to be written
957 * @param grow set to Bool::True if allowing to grow, otherwise exception is thrown if position() == limit(). Defaults to Bool::False.
958 * @see setGrowthFactor()
959 */
960 constexpr_cxx20 self_t& put(std::initializer_list<value_type> initlist, Bool grow=Bool::False)
961 {
962 if( initlist.size() > std::numeric_limits<size_type>::max() ) {
963 throw jau::IllegalArgumentError("capacity "+std::to_string(initlist.size())+" > size_type max "+
964 std::to_string(std::numeric_limits<size_type>::max()), E_FILE_LINE);
965 }
966 const size_type count1 = size_type(initlist.size()); // number of elements to put
967 if( *grow && m_position + count1 > m_limit ) {
968 const size_type count2 = position() + count1 - limit(); // number of newly required elements (space)
969 if( m_limit + count2 > m_storage_end ) {
970 grow_storage_move(limit() + count2 - capacity());
971 // resize(limit() + count2);
972 m_end += count2;
973 m_limit += count2;
974 } else if( m_limit + count2 > m_end ) {
975 // resize(limit() + count2);
976 m_end += count2;
977 m_limit += count2;
978 } else {
979 m_limit+=count2;
980 }
981 }
982 if( m_position + count1 - 1 < m_limit ) {
983 ctor_copy_range_foreign(m_position, initlist.begin(), initlist.end());
984 m_position+=count1;
985 return *this;
986 }
988 }
989
990 /**
991 * Relative put() for multiple value of an assignable type fitting into value_type, increasing position().
992 *
993 * Grows storage and/or moves limit if required and grow==True().
994 *
995 * Throws if position() + count > limit().
996 *
997 * @tparam Targs argument types, which must all be of the same type
998 * @param grow set to Bool::True if allowing to grow, otherwise exception is thrown if position() == limit(). Defaults to Bool::False.
999 * @param args values to be written
1000 * @see setGrowthFactor()
1001 */
1002 template<typename... Targs,
1003 std::enable_if_t< jau::is_all_same_v<Targs...> &&
1004 sizeof(Value_type) >= sizeof(jau::first_type<Targs...>) && // NOLINT(bugprone-sizeof-expression)
1005 std::is_assignable_v<value_type&, jau::first_type<Targs...>>, bool> = true>
1006 constexpr_cxx20 self_t& putN(Bool grow, const Targs&...args) {
1007 const size_type count1 = sizeof...(args);
1008 if( *grow && m_position + count1 > m_limit ) {
1009 const size_type count2 = position() + count1 - limit();
1010 if( m_limit + count2 > m_storage_end ) {
1011 grow_storage_move(limit() + count2 - capacity());
1012 resize(limit() + count2);
1013 } else if( m_limit + count2 > m_end ) {
1014 resize(limit() + count2);
1015 } else {
1016 m_limit+=count2;
1017 }
1018 }
1019 if( m_position + count1 - 1 < m_limit ) {
1020 ( (*m_position++ = static_cast<Value_type>(args)), ... ); // NOLINT(bugprone-signed-char-misuse)
1021 return *this;
1022 }
1024 }
1025
1026 /**
1027 * Returns a sliced duplicate starting from this buffers' current position.
1028 *
1029 * This buffer is pinned() afterwards, to not allow storage mutation.
1030 *
1031 * Returned buffer is shared() and pinned(), i.e. shares the same storage of this buffer.
1032 * Its position is zero and limit set to this buffers' remaining elements.
1033 *
1034 * @see pinned()
1035 * @see shared()
1036 * @see setGrowthFactor()
1037 */
1039 const size_type new_size = size_type(m_limit - m_position);
1040 return darray(*this, m_position, m_position, m_position+new_size, new_size);
1041 }
1042
1043 /**
1044 * Returns a sliced duplicate starting from the given `idx`.
1045 *
1046 * This buffer is pinned() afterwards, to not allow storage mutation.
1047 *
1048 * Returned buffer is shared() and pinned(), i.e. shares the same storage of this buffer.
1049 * Its position is zero and limit set to the given `length`.
1050 */
1052 if(m_position + idx + length > m_limit) {
1053 throw jau::IndexOutOfBoundsError(std::to_string(position()), std::to_string(limit()), E_FILE_LINE);
1054 }
1055 return darray(*this, m_position+idx, m_position+idx, m_position+idx+length, length);
1056 }
1057
1058 /**
1059 * Returns a duplicate with same position and limit.
1060 *
1061 * This buffer is pinned() afterwards, to not allow storage mutation.
1062 *
1063 * Returned buffer is shared() and pinned(), i.e. shares the same storage of this buffer.
1064 * Its position and limit are same as with this buffer.
1065 */
1067 m_growth_factor = 0; // pinned
1068 return darray(*this, m_begin, m_position, m_limit, size());
1069 }
1070
1071 /** Returns limit - position. */
1072 constexpr size_type remaining() const noexcept { return size_type(m_limit - m_position); }
1073 /** Returns whether position < limit, i.e. has remaining elements. */
1074 constexpr bool hasRemaining() const noexcept { return m_position < m_limit; }
1075
1076 //
1077 // read access
1078 //
1079
1080 const allocator_type& get_allocator_ref() const noexcept {
1081 return m_alloc_inst;
1082 }
1083
1085 return allocator_type(m_alloc_inst);
1086 }
1087
1088 /**
1089 * Return the current capacity.
1090 */
1091 constexpr size_type capacity() const noexcept { return size_type(m_storage_end - m_begin); }
1092
1093 /**
1094 * Return the current capacity() multiplied by the growth factor, minimum is max(capacity()+add, 10).
1095 */
1096 constexpr size_type get_grown_capacity(size_type add=1) const noexcept {
1097 const size_type a_capacity = capacity();
1098 return std::max<size_type>( std::max<size_type>( MIN_SIZE_AT_GROW, a_capacity+add ),
1099 static_cast<size_type>( ::roundf(a_capacity * growthFactor()) ) );
1100 }
1101
1102 /**
1103 * Like std::vector::empty().
1104 */
1105 constexpr bool empty() const noexcept { return m_begin == m_end; }
1106
1107 /**
1108 * Returns true if capacity has been reached and the next push_back()
1109 * will grow the storage and invalidates all iterators and references.
1110 */
1111 constexpr bool capacity_reached() const noexcept { return m_end >= m_storage_end; }
1112
1113 /**
1114 * Like std::vector::size().
1115 */
1116 constexpr size_type size() const noexcept { return size_type(m_end - m_begin); }
1117
1118 // mixed mutable/immutable element access
1119
1120 /**
1121 * Like std::vector::front(), mutable access.
1122 */
1123 constexpr reference front() { return *m_begin; }
1124
1125 /**
1126 * Like std::vector::front(), immutable access.
1127 */
1128 constexpr const_reference front() const { return *m_begin; }
1129
1130 /**
1131 * Like std::vector::back(), mutable access.
1132 */
1133 constexpr reference back() { return *(m_end-1); }
1134
1135 /**
1136 * Like std::vector::back(), immutable access.
1137 */
1138 constexpr const_reference back() const { return *(m_end-1); }
1139
1140 /**
1141 * Like std::vector::data(), const immutable pointer
1142 */
1143 constexpr const_pointer data() const noexcept { return m_begin; }
1144
1145 /**
1146 * Like std::vector::data(), mutable pointer
1147 */
1148 constexpr pointer data() noexcept { return m_begin; }
1149
1150 /**
1151 * Like std::vector::operator[](size_type), immutable reference.
1152 */
1154 return *(m_begin+i);
1155 }
1156
1157 /**
1158 * Like std::vector::operator[](size_type), mutable reference.
1159 */
1161 return *(m_begin+i);
1162 }
1163
1164 /**
1165 * Like std::vector::at(size_type), immutable reference.
1166 */
1168 if( 0 <= i && i < size() ) {
1169 return *(m_begin+i);
1170 }
1172 }
1173
1174 /**
1175 * Like std::vector::at(size_type), mutable reference.
1176 */
1178 if( 0 <= i && i < size() ) {
1179 return *(m_begin+i);
1180 }
1182 }
1183
1184 // write access, mutable array operations
1185
1186 /**
1187 * Like std::vector::reserve(), increases this instance's capacity to `new_capacity`.
1188 * <p>
1189 * Only creates a new storage and invalidates iterators if `new_capacity`
1190 * is greater than the current jau::darray::capacity().
1191 * </p>
1192 */
1193 constexpr self_t& reserve(size_type new_capacity) {
1194 if( new_capacity > capacity() ) {
1195 realloc_storage_move(new_capacity);
1196 }
1197 return *this;
1198 }
1199
1200 /**
1201 * Like std::vector::resize(size_type, const value_type&)
1202 */
1203 constexpr self_t& resize(size_type new_size, const value_type& val) {
1204 const size_type sz = size();
1205 if( new_size > sz ) {
1206 if( new_size > capacity() ) {
1207 realloc_storage_move(new_size);
1208 }
1209 const size_type new_elem_count = new_size - sz;
1210 m_end += new_elem_count;
1211 m_limit += new_elem_count;
1212 ctor_copy_value(m_begin + sz, new_elem_count, val);
1213 } else if( new_size < sz ) {
1214 const size_type del_elem_count = dtor_range(m_begin + new_size, m_end);
1215 assert(sz - new_size == del_elem_count);
1216 m_end -= del_elem_count;
1217 }
1218 return *this;
1219 }
1220
1221 /**
1222 * Like std::vector::resize(size_type)
1223 */
1224 constexpr self_t& resize(size_type new_size) { return resize(new_size, value_type()); }
1225
1226 /**
1227 * Like std::vector::shrink_to_fit(), but ensured `constexpr`.
1228 *
1229 * If capacity() > size(), reallocate storage to size().
1230 */
1231 constexpr self_t& shrink_to_fit() {
1232 const size_type size_ = size();
1233 if( capacity() > size_ ) {
1234 realloc_storage_move(size_);
1235 }
1236 return *this;
1237 }
1238
1239 /**
1240 * Like std::vector::assign()
1241 * @tparam InputIt foreign input-iterator to range of value_type [first, last)
1242 * @param first first foreign input-iterator to range of value_type [first, last)
1243 * @param last last foreign input-iterator to range of value_type [first, last)
1244 */
1245 template< class InputIt >
1246 constexpr void assign( InputIt first, InputIt last ) { // NOLINT(performance-unnecessary-value-param)
1247 const size_type size_ = size();
1248 const size_type capacity_ = capacity();
1249 const size_type x_size_ = size_type(last - first);
1250 dtor_range(m_begin, m_end);
1251 if( x_size_ > capacity_ ) {
1252 const difference_type pos = std::min<difference_type>(x_size_, m_position - m_begin);
1253 freeStoreCheck();
1254 m_begin = clone_range_foreign(x_size_, first, last);
1255 m_position = m_begin + pos;
1256 set_iterator_end(x_size_, x_size_);
1257 } else {
1258 ctor_copy_range_foreign(m_begin, first, last);
1259 set_iterator_end(x_size_, capacity_);
1260 }
1261 }
1262 /**
1263 * Like std::vector::assign(), but non-template overload using const_iterator.
1264 * @param first first const_iterator to range of value_type [first, last)
1265 * @param last last const_iterator to range of value_type [first, last)
1266 */
1267 constexpr void assign( const_iterator first, const_iterator last ) {
1268 const size_type size_ = size();
1269 const size_type capacity_ = capacity();
1270 const size_type x_size_ = size_type(last - first);
1271 dtor_range(m_begin, m_end);
1272 if( x_size_ > capacity_ ) {
1273 const difference_type pos = std::min<difference_type>(x_size_, m_position - m_begin);
1274 freeStoreCheck();
1275 m_begin = clone_range_check(x_size_, first, last);
1276 m_position = m_begin + pos;
1277 set_iterator_end(x_size_, x_size_);
1278 } else {
1279 ctor_copy_range_check(m_begin, first, last);
1280 set_iterator_end(x_size_, capacity_);
1281 }
1282 }
1283
1284 /**
1285 * Like std::vector::clear(), calls destructor on all elements and leaving capacity unchanged.
1286 *
1287 * Sets position and limit to zero.
1288 *
1289 * Use clear(true) or subsequently shrink_to_fit() to release capacity (storage).
1290 *
1291 * @see clear(bool)
1292 * @see shrink_to_fit()
1293 */
1294 constexpr self_t& clear() noexcept {
1295 dtor_range(m_begin, m_end);
1296 m_end = m_begin;
1297 m_position = m_begin;
1298 m_limit = m_end;
1299 return *this;
1300 }
1301
1302 /**
1303 * Like std::vector::clear(), calls destructor on all elements.
1304 *
1305 *
1306 * Sets position and limit to zero.
1307 *
1308 * If `releaseMem` is `true`, releases capacity (memory),
1309 * otherwise leaves capacity unchanged and sets position to zero, limit to capacity.
1310 *
1311 * @see clear()
1312 */
1313 constexpr self_t& clear(bool releaseMem) noexcept {
1314 clear();
1315 if( releaseMem ) {
1316 if( !shared() ) {
1317 freeStore();
1318 }
1319 m_begin = nullptr;
1320 m_end = nullptr;
1321 m_storage_end = nullptr;
1322 m_position = nullptr;
1323 m_limit = nullptr;
1324 }
1325 return *this;
1326 }
1327
1328 /**
1329 * Like std::vector::swap().
1330 */
1331 constexpr void swap(darray& x) noexcept {
1332 JAU_DARRAY_PRINTF("swap.0: this %s\n", getInfo().c_str());
1333 JAU_DARRAY_PRINTF("swap.0: x %s\n", x.getInfo().c_str());
1334 std::swap(m_alloc_inst, x.m_alloc_inst);
1335 std::swap(m_growth_factor, x.m_growth_factor);
1336 std::swap(m_begin, x.m_begin);
1337 std::swap(m_end, x.m_end);
1338 std::swap(m_storage_end, x.m_storage_end);
1339 std::swap(m_position, x.m_position);
1340 std::swap(m_limit, x.m_limit);
1341 JAU_DARRAY_PRINTF("swap.X: this %s\n", getInfo().c_str());
1342 JAU_DARRAY_PRINTF("swap.X: x %s\n", x.getInfo().c_str());
1343 }
1344
1345 /**
1346 * Like std::vector::pop_back().
1347 *
1348 * Updates end, limit and adjusts position if > new limit.
1349 */
1350 constexpr void pop_back() noexcept {
1351 if( m_begin != m_end ) {
1352 dtor_one( --m_end );
1353 m_limit = m_end;
1354 if( m_position > m_limit ) { m_position = m_limit; }
1355 }
1356 }
1357
1358 /**
1359 * Like std::vector::erase(), removes the elements at pos.
1360 *
1361 * Updates end, limit and adjusts position if > > cpos
1362 *
1363 * @return iterator following the last removed element.
1364 */
1365 constexpr iterator erase (const_iterator cpos) {
1366 iterator pos = const_cast<iterator>(cpos);
1367 if( m_begin <= pos && pos < m_end ) {
1368 dtor_one( pos );
1369 const difference_type right_count = m_end - ( pos + 1 ); // pos is exclusive
1370 if( 0 < right_count ) {
1371 move_elements(pos, pos+1, right_count); // move right elems one left
1372 }
1373 m_limit = --m_end;
1374 if( m_position > m_begin && m_position > pos ) { --m_position; }
1375 }
1376 return m_begin <= pos && pos <= m_end ? pos : m_end;
1377 }
1378
1379 /**
1380 * Like std::vector::erase(), removes the elements in the range [first, last).
1381 *
1382 * Updates end, limit and adjusts position if > cfirst.
1383 *
1384 * @return iterator following the last removed element.
1385 */
1386 constexpr iterator erase (const_iterator cfirst, const_iterator clast) {
1387 iterator first = const_cast<iterator>(cfirst);
1388 const size_type count = dtor_range(first, clast);
1389 if( count > 0 ) {
1390 const difference_type right_count = m_end - clast; // last is exclusive
1391 if( 0 < right_count ) {
1392 move_elements(first, clast, right_count); // move right elems count left
1393 }
1394 m_end -= count;
1395 m_limit = m_end;
1396 if( m_position > m_begin && m_position > first )
1397 { m_position -= std::min(count, size_type(m_position - first)); }
1398 }
1399 return m_begin <= first && first <= m_end ? first : m_end;
1400 }
1401
1402 /**
1403 * Similar to std::vector::erase() using an index, removes the elements at pos_idx.
1404 *
1405 * Updates end, limit and adjusts position if > new limit.
1406 *
1407 * @return iterator following the last removed element.
1408 */
1409 constexpr iterator erase (const size_type pos_idx) {
1410 return erase(m_begin + pos_idx);
1411 }
1412
1413 /**
1414 * Similar to std::vector::erase() using indices, removes the elements in the range [first_idx, last_idx).
1415 *
1416 * Updates end, limit and adjusts position if > new limit.
1417 *
1418 * @return iterator following the last removed element.
1419 */
1420 constexpr iterator erase (const size_type first_idx, const size_type last_idx) {
1421 return erase(m_begin + first_idx, m_begin + last_idx);
1422 }
1423
1424 /**
1425 * Like std::vector::insert(), copy
1426 *
1427 * Inserts the element before pos
1428 * and moves all elements from there to the right beforehand.
1429 *
1430 * size/end and limit will be increased by one.
1431 *
1432 * @param pos iterator before which the content will be inserted. pos may be the end() iterator
1433 * @param x element value to insert
1434 */
1435 constexpr iterator insert(const_iterator pos, const value_type& x) {
1436 if( m_begin <= pos && pos <= m_end ) {
1437 if( m_end == m_storage_end ) {
1438 const size_type pos_idx = pos - m_begin;
1439 grow_storage_move();
1440 pos = m_begin + pos_idx;
1441 }
1442 const difference_type right_count = m_end - pos; // include original element at 'pos_new'
1443 if( 0 < right_count ) {
1444 move_elements(const_cast<iterator>(pos+1), pos, right_count); // move elems one right
1445 }
1446 new (const_cast<pointer_mutable>(pos)) value_type( x ); // placement new
1447 m_limit = ++m_end;
1448
1449 return m_begin <= pos && pos <= m_end ? const_cast<iterator>(pos) : m_end;
1450 } else {
1451 throw jau::IndexOutOfBoundsError(std::to_string(difference_type(pos - m_begin)), std::to_string(size()), E_FILE_LINE);
1452 }
1453 }
1454
1455 /**
1456 * Similar to std::vector::insert() using an index, copy
1457 * @param pos_idx index before which the content will be inserted. index may be the end size() index
1458 * @param x element value to insert
1459 * @see insert()
1460 */
1461 constexpr iterator insert(const size_type pos_idx, const value_type& x) {
1462 return insert(m_begin + pos_idx, x);
1463 }
1464
1465 /**
1466 * Like std::vector::insert(), move
1467 *
1468 * Inserts the element before the given position
1469 * and moves all elements from there to the right beforehand.
1470 *
1471 * size/end and limit will be increased by one.
1472 *
1473 * @param pos iterator before which the content will be inserted. pos may be the end() iterator
1474 * @param x element value to be moved into
1475 */
1477 if( m_begin <= pos && pos <= m_end ) {
1478 const size_type pos_idx = pos - m_begin;
1479 if( m_end == m_storage_end ) {
1480 grow_storage_move();
1481 }
1482 iterator pos_new = m_begin + pos_idx;
1483 const difference_type right_count = m_end - pos_new; // include original element at 'pos_new'
1484 if( 0 < right_count ) {
1485 move_elements(pos_new+1, pos_new, right_count); // move elems one right
1486 }
1487 new (const_cast<pointer_mutable>(pos_new)) value_type( std::move( x ) ); // placement new
1488 m_limit = ++m_end;
1489
1490 return m_begin <= pos_new && pos_new <= m_end ? pos_new : m_end;
1491 } else {
1492 throw jau::IndexOutOfBoundsError(std::to_string(difference_type(pos - m_begin)), std::to_string(size()), E_FILE_LINE);
1493 }
1494 }
1495
1496 /**
1497 * Like std::vector::emplace(), construct a new element in place.
1498 *
1499 * Constructs the element before the given position using placement new
1500 * and moves all elements from there to the right beforehand.
1501 *
1502 * size/end and limit will be increased by one.
1503 *
1504 * @param pos iterator before which the content will be inserted. pos may be the end() iterator
1505 * @param args arguments to forward to the constructor of the element
1506 */
1507 template<typename... Args>
1508 constexpr iterator emplace(const_iterator pos, Args&&... args) {
1509 if( m_begin <= pos && pos <= m_end ) {
1510 const size_type pos_idx = pos - m_begin;
1511 if( m_end == m_storage_end ) {
1512 grow_storage_move();
1513 }
1514 iterator pos_new = m_begin + pos_idx;
1515 const difference_type right_count = m_end - pos_new; // include original element at 'pos_new'
1516 if( 0 < right_count ) {
1517 move_elements(pos_new+1, pos_new, right_count); // move elems one right
1518 }
1519 new (const_cast<pointer_mutable>(pos_new)) value_type( std::forward<Args>(args)... ); // placement new, construct in-place
1520 m_limit = ++m_end;
1521
1522 return m_begin <= pos_new && pos_new <= m_end ? pos_new : m_end;
1523 } else {
1524 throw jau::IndexOutOfBoundsError(std::to_string(difference_type(pos - m_begin)), std::to_string(size()), E_FILE_LINE);
1525 }
1526 }
1527
1528 /**
1529 * Like std::vector::insert(), inserting the value_type range [first, last).
1530 *
1531 * size/end and limit will be increased by inserted elements.
1532 *
1533 * @tparam InputIt foreign input-iterator to range of value_type [first, last)
1534 * @param pos iterator before which the content will be inserted. pos may be the end() iterator
1535 * @param first first foreign input-iterator to range of value_type [first, last)
1536 * @param last last foreign input-iterator to range of value_type [first, last)
1537 * @return Iterator pointing to the first element inserted, or pos if first==last.
1538 */
1539 template< class InputIt >
1540 constexpr iterator insert( const_iterator pos, InputIt first, InputIt last ) { // NOLINT(performance-unnecessary-value-param)
1541 if( m_begin <= pos && pos <= m_end ) {
1542 const size_type new_elem_count = size_type(last - first);
1543 const size_type pos_idx = pos - m_begin;
1544 if( m_end + new_elem_count > m_storage_end ) {
1545 grow_storage_move(new_elem_count);
1546 }
1547 iterator pos_new = m_begin + pos_idx;
1548 const difference_type right_count = m_end - pos_new; // include original element at 'pos_new'
1549 if( 0 < right_count ) {
1550 move_elements(pos_new + new_elem_count, pos_new, right_count); // move elems count right
1551 }
1552 ctor_copy_range_foreign(pos_new, first, last);
1553 m_end += new_elem_count;
1554 m_limit = m_end;
1555
1556 return m_begin <= pos_new && pos_new <= m_end ? pos_new : m_end;
1557 } else {
1558 throw jau::IndexOutOfBoundsError(std::to_string(difference_type(pos - m_begin)), std::to_string(size()), E_FILE_LINE);
1559 }
1560 }
1561
1562 /**
1563 * Like std::vector::push_back(), copy
1564 *
1565 * size/end and limit will be increased by one, position set to limit/end.
1566 *
1567 * @param x the value to be added at the tail.
1568 */
1569 constexpr void push_back(const value_type& x) {
1570 if( m_end == m_storage_end ) {
1571 grow_storage_move();
1572 }
1573 new (const_cast<pointer_mutable>(m_end)) value_type( x ); // placement new
1574 m_limit = ++m_end;
1575 m_position = m_limit;
1576 }
1577
1578 /**
1579 * Like std::vector::push_back(), move
1580 *
1581 * size/end and limit will be increased by one, position set to limit/end.
1582 *
1583 * @param x the value to be added at the tail.
1584 */
1585 constexpr void push_back(value_type&& x) {
1586 if( m_end == m_storage_end ) {
1587 grow_storage_move();
1588 }
1589 new (const_cast<pointer_mutable>(m_end)) value_type( std::move(x) ); // placement new, just one element - no optimization
1590 m_limit = ++m_end;
1591 m_position = m_limit;
1592 }
1593
1594 /**
1595 * Like std::push_back(), but for an initializer list to copy.
1596 *
1597 * size/end and limit will be increased by inserted elements, position set to limit/end.
1598 *
1599 * @param initlist values to be written
1600 */
1601 constexpr_cxx20 void push_back(std::initializer_list<value_type> initlist)
1602 {
1603 if( initlist.size() > std::numeric_limits<size_type>::max() ) {
1604 throw jau::IllegalArgumentError("capacity "+std::to_string(initlist.size())+" > size_type max "+
1605 std::to_string(std::numeric_limits<size_type>::max()), E_FILE_LINE);
1606 }
1607 const size_type count1 = size_type(initlist.size());
1608 if( m_end + count1 > m_storage_end ) {
1609 const size_type epos = size_type(m_end - m_begin);
1610 const size_type spos = size_type(m_storage_end - m_begin);
1611 const size_type count2 = epos + count1 - spos;
1612 grow_storage_move(limit() + count2 - capacity());
1613 }
1614 ctor_copy_range_foreign(m_end, initlist.begin(), initlist.end());
1615 m_end += count1;
1616 m_limit = m_end;
1617 m_position = m_limit;
1618 }
1619
1620 /**
1621 * Like std::vector::push_front(), copy
1622 *
1623 * size/end and limit will be increased by one.
1624 *
1625 * @param x the value to be added at the front.
1626 */
1627 constexpr void push_front(const value_type& x) {
1628 insert(m_begin, x);
1629 }
1630
1631 /**
1632 * Like std::vector::push_front(), move
1633 *
1634 * size/end and limit will be increased by one.
1635 *
1636 * @param x the value to be added at the front.
1637 */
1638 constexpr void push_front(value_type&& x) {
1639 insert(m_begin, std::move(x));
1640 }
1641
1642 /**
1643 * Like std::vector::emplace_back(), construct a new element in place at the end().
1644 *
1645 * Constructs the element at the end() using placement new.
1646 *
1647 * size/end and limit will be increased by one, position set to limit/end.
1648 *
1649 * @param args arguments to forward to the constructor of the element
1650 */
1651 template<typename... Args>
1652 constexpr reference emplace_back(Args&&... args) {
1653 if( m_end == m_storage_end ) {
1654 grow_storage_move();
1655 }
1656 new (const_cast<pointer_mutable>(m_end)) value_type( std::forward<Args>(args)... ); // placement new, construct in-place
1657 reference res = *m_end;
1658 m_limit = ++m_end;
1659 m_position=m_limit;
1660 return res;
1661 }
1662
1663 /**
1664 * Like std::vector::push_back(), but appends the value_type range [first, last).
1665 *
1666 * size/end and limit will be increased by inserted elements, position set to limit/end.
1667 *
1668 * @tparam InputIt foreign input-iterator to range of value_type [first, last)
1669 * @param first first foreign input-iterator to range of value_type [first, last)
1670 * @param last last foreign input-iterator to range of value_type [first, last)
1671 */
1672 template< class InputIt >
1673 constexpr void push_back( InputIt first, InputIt last ) { // NOLINT(performance-unnecessary-value-param)
1674 const size_type count = size_type(last - first);
1675
1676 if( m_end + count > m_storage_end ) {
1677 grow_storage_move(count);
1678 }
1679 ctor_copy_range_foreign(m_end, first, last);
1680 m_end += count;
1681 m_limit = m_end;
1682 m_position=m_limit;
1683 }
1684
1685 /**
1686 * Like push_back(), but for more multiple const r-value to copy.
1687 *
1688 * size/end and limit will be increased by inserted elements, position set to limit/end.
1689 *
1690 * @tparam Args
1691 * @param args r-value references to copy into this storage
1692 */
1693 template <typename... Args>
1694 constexpr void push_back_list(const Args&... args)
1695 {
1696 const size_type count = sizeof...(Args);
1697
1698 JAU_DARRAY_PRINTF("push_back_list.copy.0: %zu elems: this %s\n", count, getInfo().c_str());
1699
1700 if( m_end + count > m_storage_end ) {
1701 grow_storage_move(count);
1702 }
1703 // C++17 fold expression on above C++11 template pack args
1704 ( new (const_cast<pointer_mutable>(m_end++)) value_type( args ), ... ); // @suppress("Syntax error")
1705 m_limit = m_end;
1706 m_position=m_limit;
1707
1708 JAU_DARRAY_PRINTF("push_back_list.copy.X: %zu elems: this %s\n", count, getInfo().c_str());
1709 }
1710
1711 /**
1712 * Like push_back(), but for more multiple r-value references to move.
1713 *
1714 * size/end and limit will be increased by inserted elements, position set to limit/end.
1715 *
1716 * @tparam Args
1717 * @param args r-value references to move into this storage
1718 * @see jau::make_darray()
1719 */
1720 template <typename... Args>
1721 constexpr void push_back_list(Args&&... args)
1722 {
1723 const size_type count = sizeof...(Args);
1724
1725 JAU_DARRAY_PRINTF("push_back_list.move.0: %zu elems: this %s\n", count, getInfo().c_str());
1726
1727 if( m_end + count > m_storage_end ) {
1728 grow_storage_move(count);
1729 }
1730 // C++17 fold expression on above C++11 template pack args
1731 ( new (const_cast<pointer_mutable>(m_end++)) value_type( std::move(args) ), ... ); // @suppress("Syntax error")
1732 m_limit = m_end;
1733 m_position=m_limit;
1734
1735 JAU_DARRAY_PRINTF("push_back_list.move.X: %zu elems: this %s\n", count, getInfo().c_str());
1736 }
1737
1738 /**
1739 * Generic value_type equal comparator to be user defined for e.g. jau::darray::push_back_unique().
1740 * @param a one element of the equality test.
1741 * @param b the other element of the equality test.
1742 * @return true if both are equal
1743 */
1744 typedef bool(*equal_comparator)(const value_type& a, const value_type& b);
1745
1746 /**
1747 * Like std::vector::push_back(), but only if the newly added element does not yet exist.
1748 * <p>
1749 * Examples
1750 * <pre>
1751 * static jau::darray<Thing>::equal_comparator thingEqComparator =
1752 * [](const Thing &a, const Thing &b) -> bool { return a == b; };
1753 * ...
1754 * jau::darray<Thing> list;
1755 *
1756 * bool added = list.push_back_unique(new_element, thingEqComparator);
1757 * ...
1758 * darray<std::shared_ptr<Thing>> listOfRefs;
1759 * bool added = listOfRefs.push_back_unique(new_element,
1760 * [](const std::shared_ptr<Thing> &a, const std::shared_ptr<Thing> &b) -> bool { return *a == *b; });
1761 * </pre>
1762 * </p>
1763 * @param x the value to be added at the tail, if not existing yet.
1764 * @param comparator the equal comparator to return true if both given elements are equal
1765 * @return true if the element has been uniquely added, otherwise false
1766 * @see push_back()
1767 */
1768 constexpr bool push_back_unique(const value_type& x, equal_comparator comparator) {
1769 for(auto it = m_begin; it != m_end; ) {
1770 if( comparator( *it, x ) ) {
1771 return false; // already included
1772 } else {
1773 ++it;
1774 }
1775 }
1776 push_back(x);
1777 return true;
1778 }
1779
1780 /**
1781 * Erase either the first matching element or all matching elements using erase().
1782 * <p>
1783 * Examples
1784 * <pre>
1785 * darray<Thing> list;
1786 * int count = list.erase_matching(element, true,
1787 * [](const Thing &a, const Thing &b) -> bool { return a == b; });
1788 * ...
1789 * static jau::darray<Thing>::equal_comparator thingRefEqComparator =
1790 * [](const std::shared_ptr<Thing> &a, const std::shared_ptr<Thing> &b) -> bool { return *a == *b; };
1791 * ...
1792 * darray<std::shared_ptr<Thing>> listOfRefs;
1793 * int count = listOfRefs.erase_matching(element, false, thingRefEqComparator);
1794 * </pre>
1795 * </p>
1796 * @param x the value to be added at the tail, if not existing yet.
1797 * @param all_matching if true, erase all matching elements, otherwise only the first matching element.
1798 * @param comparator the equal comparator to return true if both given elements are equal
1799 * @return number of erased elements
1800 * @see erase()
1801 */
1802 constexpr size_type erase_matching(const value_type& x, const bool all_matching, equal_comparator comparator) {
1803 size_type count = 0;
1804 for(auto it = m_end-1; m_begin <= it; --it) {
1805 if( comparator( *it, x ) ) {
1806 erase(it);
1807 ++count;
1808 if( !all_matching ) {
1809 break;
1810 }
1811 }
1812 }
1813 return count;
1814 }
1815
1816 /**
1817 * Erase either the first matching element or all matching elements.
1818 * <p>
1819 * Examples
1820 * <pre>
1821 * darray<Thing> list;
1822 * int count = list.erase_if(true,
1823 * [&element](const Thing &a) -> bool { return a == element; });
1824 * </pre>
1825 * </p>
1826 * @param all_matching if true, erase all matching elements, otherwise only the first matching element.
1827 * @param p the unary predicate test to return true if given elements shall be erased
1828 * @return number of erased elements
1829 */
1830 template<class UnaryPredicate>
1831 constexpr size_type erase_if(const bool all_matching, UnaryPredicate p) {
1832 size_type count = 0;
1833 for(auto it = m_end-1; m_begin <= it; --it) {
1834 if( p( *it ) ) {
1835 erase(it);
1836 ++count;
1837 if( !all_matching ) {
1838 break;
1839 }
1840 }
1841 }
1842 return count;
1843 }
1844
1845 std::string toString() const {
1846 std::string res("{ " + valueSignature().name() + ", " + std::to_string( size() ) + "/" + std::to_string( capacity() ) + ": ");
1847 int i=0;
1848 jau::for_each_const(*this, [&res, &i](const value_type & e) {
1849 if( 1 < ++i ) { res.append(", "); }
1850 res.append( jau::to_string(e) );
1851 } );
1852 res.append(" }");
1853 return res;
1854 }
1855
1856 std::string getInfo() const {
1857 difference_type cap_ = (m_storage_end-m_begin);
1858 difference_type size_ = (m_end-m_begin);
1859 std::string res("darray[this "+jau::toHexString(this)+
1860 ", " + valueSignature().name() +
1861 ", size "+std::to_string(size_)+" / "+std::to_string(cap_)+
1862 ", flags[");
1863 if( pinned() ) {
1864 res.append("pinned");
1865 if( shared() ) {
1866 res.append(", shared");
1867 }
1868 }
1869 res.append("], growth "+std::to_string(m_growth_factor)+
1870 ", type[integral "+std::to_string(std::is_integral_v<Value_type>)+
1871 ", trivialCpy "+std::to_string(std::is_trivially_copyable_v<Value_type>)+
1872 "], uses[mmove "+std::to_string(uses_memmove)+
1873 ", realloc "+std::to_string(uses_realloc)+
1874 ", smem "+std::to_string(uses_secmem)+
1875 "], begin "+jau::toHexString(m_begin)+
1876 ", [pos "+std::to_string(m_position-m_begin)+
1877 ", lim "+std::to_string(m_limit-m_begin)+
1878 ", end "+std::to_string(m_end-m_begin)+" "+jau::toHexString(m_end)+
1879 ", send "+std::to_string(m_storage_end-m_begin)+" "+jau::toHexString(m_storage_end)+
1880 "]");
1881 return res;
1882 }
1883 };
1884
1885 /**
1886 * Construct a darray<T> instance, initialized by move semantics from the variadic (template pack) argument list.
1887 *
1888 * std::initializer_list<T> enforces to copy the created instances into the container,
1889 * since its iterator references to `const` value_type.
1890 *
1891 * This alternative template passes the r-value argument references to darray::push_back_list(),
1892 * hence using `std::move` without copying semantics.
1893 *
1894 * All argument types must be of same type, i.e. std::is_same.
1895 * The deduced darray<T> instance also uses same type as its Value_type.
1896 *
1897 * @tparam First the first argument type, must be same
1898 * @tparam Next all other argument types, must be same
1899 * @tparam
1900 * @param arg1 the first r-value
1901 * @param argsN the other r-values
1902 * @return the new `darray`
1903 * @see darray::push_back_list()
1904 * @see make_darray()
1905 */
1906 template <typename First, typename... Next,
1907 // std::enable_if_t< ( std::is_same<First, Next>::value && ... ), bool> = true>
1908 std::enable_if_t< std::conjunction_v<std::is_same<First, Next>... >, bool> = true>
1909 constexpr darray< First > make_darray(First&& arg1, Next&&... argsN)
1910 {
1911 darray< First > d(1 + sizeof...(Next));
1912 // C++17 fold expression on above C++11 template pack arg1 and argsN
1913 // d.push_back_list( std::forward<First>(arg1), ( std::forward<Next>(argsN), ... ) ); // @suppress("Syntax error")
1914 d.push_back_list( arg1, argsN... ); // @suppress("Syntax error")
1915 return d;
1916 }
1917
1918 /**
1919 * Complement constructor for darray<T> instance, move semantics initializer for one argument.
1920 * @tparam First
1921 * @tparam Next
1922 * @param arg1
1923 * @return
1924 * @see darray::push_back()
1925 * @see darray::push_back_list()
1926 * @see make_darray()
1927 */
1928 template <typename First, typename... Next>
1929 constexpr darray< First > make_darray(First&& arg1)
1930 {
1931 darray< First > d(1);
1932 d.push_back( std::forward<First>(arg1) );
1933 return d;
1934 }
1935
1936 /****************************************************************************************
1937 ****************************************************************************************/
1938
1939 template<typename Value_type, typename Size_type, typename Alloc_type>
1940 std::ostream & operator << (std::ostream &out, const darray<Value_type, Size_type, Alloc_type> &c) {
1941 out << c.toString();
1942 return out;
1943 }
1944
1945 /****************************************************************************************
1946 ****************************************************************************************/
1947
1948 template<typename Value_type, typename Size_type, typename Alloc_type>
1950 if( &rhs == &lhs ) {
1951 return true;
1952 }
1953 return (rhs.size() == lhs.size() && std::equal(rhs.cbegin(), rhs.cend(), lhs.cbegin()));
1954 }
1955 template<typename Value_type, typename Size_type, typename Alloc_type>
1957 return !(rhs==lhs);
1958 }
1959
1960 template<typename Value_type, typename Size_type, typename Alloc_type>
1962 { return std::lexicographical_compare(rhs.cbegin(), rhs.cend(), lhs.cbegin(), lhs.cend()); }
1963
1964 template<typename Value_type, typename Size_type, typename Alloc_type>
1966 { return lhs < rhs; }
1967
1968 template<typename Value_type, typename Size_type, typename Alloc_type>
1970 { return !(lhs < rhs); }
1971
1972 template<typename Value_type, typename Size_type, typename Alloc_type>
1974 { return !(rhs < lhs); }
1975
1976 template<typename Value_type, typename Size_type, typename Alloc_type>
1978 { rhs.swap(lhs); }
1979
1980 /****************************************************************************************
1981 ****************************************************************************************/
1982
1983 /**
1984 * `template< class T > is_darray_type<T>::value` compile-time Type Trait,
1985 * determining whether the given template class is a - or has a darray type, e.g. jau::cow_darray,
1986 * jau::darray.
1987 */
1988 template< class, class = void >
1989 struct is_darray_type : std::false_type { };
1990
1991 /**
1992 * `template< class T > is_darray_type<T>::value` compile-time Type Trait,
1993 * determining whether the given template class is a - or has a darray type, e.g. jau::cow_darray,
1994 * jau::darray.
1995 */
1996 template< class T >
1997 struct is_darray_type<T, std::void_t<typename T::darray_tag>> : std::true_type { };
1998
1999 /**@}*/
2000
2001} /* namespace jau */
2002
2003#endif /* JAU_DYN_ARRAY_HPP_ */
Implementation of a dynamic linear array storage, aka vector, including relative positional access.
Definition darray.hpp:154
constexpr self_t & rewind() noexcept
Sets position to zero.
Definition darray.hpp:887
constexpr void push_front(value_type &&x)
Like std::vector::push_front(), move.
Definition darray.hpp:1638
darray< value_type, size_type, allocator_type, use_memmove, use_secmem > self_t
Definition darray.hpp:180
std::string toString() const
Definition darray.hpp:1845
static constexpr size_type max_size() noexcept
Returns std::numeric_limits<difference_type>::max() as the maximum array size.
Definition darray.hpp:826
constexpr_cxx20 self_t & put(const_reference v, Bool grow=Bool::False)
Relative put() for single value, increasing position().
Definition darray.hpp:931
const jau::type_info & valueSignature() const noexcept
Returns type signature of implementing class's stored value type.
Definition darray.hpp:204
const allocator_type & get_allocator_ref() const noexcept
Definition darray.hpp:1080
constexpr iterator insert(const_iterator pos, InputIt first, InputIt last)
Like std::vector::insert(), inserting the value_type range [first, last).
Definition darray.hpp:1540
constexpr void push_back(InputIt first, InputIt last)
Like std::vector::push_back(), but appends the value_type range [first, last).
Definition darray.hpp:1673
const jau::type_info & classSignature() const noexcept
Returns type signature of implementing class.
Definition darray.hpp:209
constexpr size_type erase_if(const bool all_matching, UnaryPredicate p)
Erase either the first matching element or all matching elements.
Definition darray.hpp:1831
constexpr void swap(darray &x) noexcept
Like std::vector::swap().
Definition darray.hpp:1331
constexpr void assign(InputIt first, InputIt last)
Like std::vector::assign()
Definition darray.hpp:1246
constexpr darray & operator=(const darray &x)
Like std::vector::operator=(&), assignment.
Definition darray.hpp:654
bool(* equal_comparator)(const value_type &a, const value_type &b)
Definition darray.hpp:1744
constexpr_cxx20 const_reference at(size_type i) const
Like std::vector::at(size_type), immutable reference.
Definition darray.hpp:1167
constexpr void assign(const_iterator first, const_iterator last)
Like std::vector::assign(), but non-template overload using const_iterator.
Definition darray.hpp:1267
constexpr_cxx20 const_reference operator[](size_type i) const noexcept
Like std::vector::operator[](size_type), immutable reference.
Definition darray.hpp:1153
constexpr iterator erase(const_iterator cpos)
Like std::vector::erase(), removes the elements at pos.
Definition darray.hpp:1365
constexpr reference front()
Like std::vector::front(), mutable access.
Definition darray.hpp:1123
std::string getInfo() const
Definition darray.hpp:1856
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:805
constexpr_cxx20 self_t & put(std::initializer_list< value_type > initlist, Bool grow=Bool::False)
Relative put() for an initializer list of same type, increasing position().
Definition darray.hpp:960
constexpr const_iterator begin() const noexcept
Definition darray.hpp:831
constexpr_cxx20 const_reference get()
Relative get() for single value reference, increasing position() by one.
Definition darray.hpp:913
constexpr iterator end() noexcept
Definition darray.hpp:834
constexpr_cxx20 reference at(size_type i)
Like std::vector::at(size_type), mutable reference.
Definition darray.hpp:1177
~darray() noexcept
Definition darray.hpp:814
constexpr const_iterator cend() const noexcept
Definition darray.hpp:836
constexpr size_type size() const noexcept
Like std::vector::size().
Definition darray.hpp:1116
constexpr void push_back(value_type &&x)
Like std::vector::push_back(), move.
Definition darray.hpp:1585
constexpr const_iterator end() const noexcept
Definition darray.hpp:835
constexpr void push_back(const value_type &x)
Like std::vector::push_back(), copy.
Definition darray.hpp:1569
constexpr darray(darray &&x) noexcept
Definition darray.hpp:680
constexpr self_t & resize(size_type new_size)
Like std::vector::resize(size_type)
Definition darray.hpp:1224
constexpr reference emplace_back(Args &&... args)
Like std::vector::emplace_back(), construct a new element in place at the end().
Definition darray.hpp:1652
constexpr self_t & clear() noexcept
Like std::vector::clear(), calls destructor on all elements and leaving capacity unchanged.
Definition darray.hpp:1294
constexpr darray(const darray &x)
Creates a new instance, copying all elements from the given darray.
Definition darray.hpp:603
constexpr iterator erase(const_iterator cfirst, const_iterator clast)
Like std::vector::erase(), removes the elements in the range [first, last).
Definition darray.hpp:1386
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:1420
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 using erase().
Definition darray.hpp:1802
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:784
constexpr bool pinned() const
Returns true if growthFactor() < 1, otherwise false.
Definition darray.hpp:236
constexpr bool shared() const
Returns true if growthFactor() < 0, otherwise false.
Definition darray.hpp:238
constexpr iterator insert(const size_type pos_idx, const value_type &x)
Similar to std::vector::insert() using an index, copy.
Definition darray.hpp:1461
constexpr bool empty() const noexcept
Like std::vector::empty().
Definition darray.hpp:1105
constexpr const_pointer data() const noexcept
Like std::vector::data(), const immutable pointer.
Definition darray.hpp:1143
constexpr darray & operator=(darray &&x) noexcept
Like std::vector::operator=(&&), move.
Definition darray.hpp:705
constexpr const_iterator cbegin() const noexcept
Definition darray.hpp:832
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:1111
self_t & setLimit(size_type v)
Sets new limit and adjusts position if new limit is below.
Definition darray.hpp:869
constexpr pointer data() noexcept
Like std::vector::data(), mutable pointer.
Definition darray.hpp:1148
constexpr const_pointer limit_ptr() const noexcept
Pointer to immutable read/write limit, one element beyond maximum element with limit <= size/end.
Definition darray.hpp:867
constexpr darray() noexcept
Default constructor, giving zero capacity and zero memory footprint.
Definition darray.hpp:554
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:568
constexpr darray(darray &parent, pointer begin, pointer position, pointer limit, size_type size)
Slicing ctor.
Definition darray.hpp:535
constexpr_cxx20 reference operator[](size_type i) noexcept
Like std::vector::operator[](size_type), mutable reference.
Definition darray.hpp:1160
constexpr darray(std::nullptr_t, size_type size, value_type value=value_type(), const float growth_factor=DEFAULT_GROWTH_FACTOR, const allocator_type &alloc=allocator_type())
Creating a sized instance with initial size elements with default value.
Definition darray.hpp:584
self_t & setPosition(size_type v)
Sets position.
Definition darray.hpp:855
constexpr darray(darray &&x, const float growth_factor, const allocator_type &alloc) noexcept
Definition darray.hpp:691
constexpr iterator insert(const_iterator pos, const value_type &x)
Like std::vector::insert(), copy.
Definition darray.hpp:1435
constexpr void push_back_list(const Args &... args)
Like push_back(), but for more multiple const r-value to copy.
Definition darray.hpp:1694
constexpr_cxx20 self_t & putN(Bool grow, const Targs &...args)
Relative put() for multiple value of an assignable type fitting into value_type, increasing position(...
Definition darray.hpp:1006
constexpr void push_back_list(Args &&... args)
Like push_back(), but for more multiple r-value references to move.
Definition darray.hpp:1721
constexpr pointer position_ptr() noexcept
Pointer to mutable next relative read/write element index, with 0 <= position <= limit.
Definition darray.hpp:851
constexpr self_t & clearPosition() noexcept
Clears the relative position and limit w/o destructing elements nor mutating storage.
Definition darray.hpp:902
allocator_type get_allocator() const noexcept
Definition darray.hpp:1084
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:1409
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:1768
constexpr bool hasRemaining() const noexcept
Returns whether position < limit, i.e.
Definition darray.hpp:1074
self_t slice()
Returns a sliced duplicate starting from this buffers' current position.
Definition darray.hpp:1038
constexpr self_t & reserve(size_type new_capacity)
Like std::vector::reserve(), increases this instance's capacity to new_capacity.
Definition darray.hpp:1193
constexpr iterator insert(const_iterator pos, value_type &&x)
Like std::vector::insert(), move.
Definition darray.hpp:1476
constexpr const_reference front() const
Like std::vector::front(), immutable access.
Definition darray.hpp:1128
constexpr self_t & shrink_to_fit()
Like std::vector::shrink_to_fit(), but ensured constexpr.
Definition darray.hpp:1231
self_t slice(size_type idx, size_type length)
Returns a sliced duplicate starting from the given idx.
Definition darray.hpp:1051
constexpr iterator emplace(const_iterator pos, Args &&... args)
Like std::vector::emplace(), construct a new element in place.
Definition darray.hpp:1508
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:741
constexpr self_t & resize(size_type new_size, const value_type &val)
Like std::vector::resize(size_type, const value_type&)
Definition darray.hpp:1203
constexpr const_pointer position_ptr() const noexcept
Pointer to immutable next relative read/write element index, with 0 <= position <= limit.
Definition darray.hpp:853
constexpr_cxx20 void push_back(std::initializer_list< value_type > initlist)
Like std::push_back(), but for an initializer list to copy.
Definition darray.hpp:1601
constexpr void setGrowthFactor(float v) noexcept
Sets the growth factor when size() == capacity() is reached for growing operations,...
Definition darray.hpp:234
constexpr float growthFactor() const noexcept
Returns growth factor, see setGrowthFactor() for semantics.
Definition darray.hpp:214
constexpr self_t & clear(bool releaseMem) noexcept
Like std::vector::clear(), calls destructor on all elements.
Definition darray.hpp:1313
constexpr reference back()
Like std::vector::back(), mutable access.
Definition darray.hpp:1133
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:765
constexpr void pop_back() noexcept
Like std::vector::pop_back().
Definition darray.hpp:1350
constexpr const_reference back() const
Like std::vector::back(), immutable access.
Definition darray.hpp:1138
constexpr size_type get_grown_capacity(size_type add=1) const noexcept
Return the current capacity() multiplied by the growth factor, minimum is max(capacity()+add,...
Definition darray.hpp:1096
constexpr size_type remaining() const noexcept
Returns limit - position.
Definition darray.hpp:1072
constexpr void push_front(const value_type &x)
Like std::vector::push_front(), copy.
Definition darray.hpp:1627
self_t duplicate()
Returns a duplicate with same position and limit.
Definition darray.hpp:1066
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:642
constexpr self_t & flip() noexcept
Sets limit to position and position to zero.
Definition darray.hpp:880
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:622
Generic type information using either Runtime type information (RTTI) or Compile time type informatio...
#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_view to_string(const endian_t v) noexcept
Return std::string representation of the given endian.
std::ostream & operator<<(std::ostream &out, const bitfield_t< StorageType, BitSize > &v)
Definition bitfield.hpp:495
std::tuple_element_t< 0, std::tuple< Ts... > > first_type
#define constexpr_cxx20
constexpr qualifier replacement for C++20 constexpr.
constexpr bool value(const Bool rhs) noexcept
#define PRAGMA_DISABLE_WARNING_PUSH
#define E_FILE_LINE
constexpr bool is_all_same_v
#define PRAGMA_DISABLE_WARNING_STRINGOP_OVERFLOW
const jau::type_info & static_ctti() noexcept
Returns a static global reference of make_ctti<T>(true) w/ identity instance.
constexpr std::string_view name(const Bool v) noexcept
#define PRAGMA_DISABLE_WARNING_POP
#define PRAGMA_DISABLE_WARNING_NULL_DEREFERENCE
Bool
Boolean type without implicit conversion, safe for function parameter.
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:1909
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.
void zero_bytes_sec(void *s, size_t n) noexcept __attrdecl_no_optimize__
Wrapper to ::explicit_bzero(), ::bzero() or ::memset(), whichever is available in that order.
std::string toHexString(const void *data, const nsize_t length, const lb_endian_t byteOrder=lb_endian_t::big, const LoUpCase capitalization=LoUpCase::lower, const PrefixOpt prefix=PrefixOpt::prefix) noexcept
Produce a hexadecimal string representation of the given lsb-first byte values.
__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
bool operator!=(const callocator< T1 > &lhs, const callocator< T2 > &rhs) noexcept
STL namespace.
template< class T > is_darray_type<T>::value compile-time Type Trait, determining whether the given t...
Definition darray.hpp:1989
uint8_t Value_type