Gamp v0.0.7-54-gccdc599
Gamp: Graphics, Audio, Multimedia and Processing
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 *
374In copy constructor ‘std::__shared_count<_Lp>::__shared_count(const std::__shared_count<_Lp>&) [with __gnu_cxx::_Lock_policy _Lp = __gnu_cxx::_S_atomic]’,
375 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,
376 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,
377 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,
378 ...
379/usr/include/c++/12/bits/shared_ptr_base.h:1075:9: warning: potential null pointer dereference [-Wnull-dereference]
380 1075 | : _M_pi(__r._M_pi)
381 | ^~~~~~~~~~~~~~~~
382 */
385 for(; first < last; ++dest, ++first) {
386 new (const_cast<pointer_mutable>(dest)) value_type( *first ); // placement new / TODO: See above
387 }
389 }
390 constexpr pointer clone_range(iterator first, const_iterator last) {
391 JAU_DARRAY_PRINTF0("clone_range [0 .. %zd], count %zd\n", (last-first)-1, (last-first)-1);
392 pointer dest = allocStore(size_type(last-first));
393 ctor_copy_range(dest, first, last);
394 return dest;
395 }
396 constexpr pointer clone_range(const size_type dest_capacity, iterator first, const_iterator last) {
397 JAU_DARRAY_PRINTF0("clone_range [0 .. %zd], count %zd -> %d\n", (last-m_begin)-1, (last-first)-1, (int)dest_capacity);
398 pointer dest = allocStore(dest_capacity);
399 ctor_copy_range(dest, first, last);
400 return dest;
401 }
402 constexpr void ctor_copy_range_check(pointer dest, iterator first, const_iterator last) {
403 JAU_DARRAY_PRINTF0("ctor_copy_range_check [%zd .. %zd] -> ??, dist %zd\n", (first-m_begin), (last-m_begin)-1, (last-first)-1);
404 if( first > last ) {
405 throw jau::IllegalArgumentError("first "+toHexString(first)+" > last "+toHexString(last), E_FILE_LINE);
406 }
407 for(; first < last; ++dest, ++first) {
408 new (const_cast<pointer_mutable>(dest)) value_type( *first ); // placement new
409 }
410 }
411 constexpr pointer clone_range_check(const size_type dest_capacity, iterator first, const_iterator last) {
412 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);
413 if( dest_capacity < size_type(last-first) ) {
414 throw jau::IllegalArgumentError("capacity "+std::to_string(dest_capacity)+" < source range "+
415 std::to_string(difference_type(last-first)), E_FILE_LINE);
416 }
417 pointer dest = allocStore(dest_capacity);
418 ctor_copy_range_check(dest, first, last);
419 return dest;
420 }
421
422 constexpr void ctor_copy_value(pointer dest, size_type count, const value_type& val) {
423 if( m_begin > dest || dest + count > m_end ) {
424 throw jau::IllegalArgumentError("dest "+jau::to_string( dest )+" + "+jau::to_string( count )+" not within ["+
425 jau::to_string( m_begin )+".."+jau::to_string( m_end )+")", E_FILE_LINE);
426 }
427 if( 0 < count ) {
428 for(size_type i=0; i < count; ++i, ++dest) {
429 new (const_cast<pointer_mutable>(dest)) value_type( val ); // placement new // NOLINT(bugprone-multi-level-implicit-pointer-conversion): OK and intended
430 }
431 }
432 }
433 template< class InputIt >
434 constexpr static void ctor_copy_range_foreign(pointer dest, InputIt first, InputIt last) { // NOLINT(performance-unnecessary-value-param)
435 if( first > last ) {
436 throw jau::IllegalArgumentError("first "+jau::to_string( first )+" > last "+
437 jau::to_string( last ), E_FILE_LINE);
438 }
439 for(; first != last; ++dest, ++first) {
440 new (const_cast<pointer_mutable>(dest)) value_type( *first ); // placement new // NOLINT(bugprone-multi-level-implicit-pointer-conversion): OK and intended
441 }
442 }
443 template< class InputIt >
444 constexpr pointer clone_range_foreign(const size_t dest_capacity, InputIt first, InputIt last) { // NOLINT(performance-unnecessary-value-param)
445 if( dest_capacity > std::numeric_limits<size_type>::max() ) {
446 throw jau::IllegalArgumentError("capacity "+std::to_string(dest_capacity)+" > size_type max "+
447 std::to_string(std::numeric_limits<size_type>::max()), E_FILE_LINE);
448 }
449 if( dest_capacity < size_type(last-first) ) {
450 throw jau::IllegalArgumentError("capacity "+std::to_string(dest_capacity)+" < source range "+
451 std::to_string(difference_type(last-first)), E_FILE_LINE);
452 }
453 pointer dest = allocStore(size_type(dest_capacity));
454 ctor_copy_range_foreign(dest, first, last);
455 return dest;
456 }
457
458 constexpr void realloc_storage_move(const size_type new_capacity) {
459 if constexpr ( !uses_memmove ) {
460 pointer new_storage = allocStore(new_capacity);
461 {
462 iterator dest = new_storage;
463 iterator first = m_begin;
464 for(; first < m_end; ++dest, ++first) {
465 new (const_cast<pointer_mutable>(dest)) value_type( std::move( *first ) ); // placement new
466 dtor_one(first); // manual destruction, even after std::move (object still exists)
467 }
468 }
469 freeStoreCheck();
470 set_iterator(new_storage, size(), new_capacity);
471 } else if constexpr ( uses_realloc ) {
472 pointer new_storage = reallocStore<allocator_type>(new_capacity);
473 set_iterator(new_storage, size(), new_capacity);
474 } else {
475 pointer new_storage = allocStore(new_capacity);
476 ::memcpy(voidptr_cast(new_storage),
477 m_begin, (uint8_t*)m_end-(uint8_t*)m_begin); // we can simply copy the memory over, also no overlap
478 freeStoreCheck();
479 set_iterator(new_storage, size(), new_capacity);
480 }
481 }
482 constexpr void grow_storage_move(size_type add=1) {
483 realloc_storage_move( get_grown_capacity(add) );
484 }
485
486 constexpr void move_elements(iterator dest, const_iterator first, const difference_type count) {
487 // Debatable here: "Moved source array has been taken over, flush sources' pointer to avoid value_type dtor releasing taken resources!"
488 // Debatable, b/c is this even possible for user to hold an instance the way, that a dtor gets called? Probably not.
489 // Hence we leave it to 'uses_secmem' to zero_bytes_sec...
490 if constexpr ( uses_memmove ) {
491 // handles overlap
492 // NOLINTNEXTLINE(bugprone-undefined-memory-manipulation)
493 ::memmove(voidptr_cast(dest),
494 first, sizeof(value_type)*count);
495 if constexpr ( uses_secmem ) {
496 if( dest < first ) {
497 // move elems left
498 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));
499 zero_bytes_sec(voidptr_cast(dest+count), (first-dest)*sizeof(value_type));
500 } else {
501 // move elems right
502 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));
505 zero_bytes_sec(voidptr_cast(first), (dest-first)*sizeof(value_type)); // TODO: See above
507 }
508 }
509 } else {
510 if( dest < first ) {
511 // move elems left
512 const_iterator last = first + count;
513 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));
514 for(; first < last; ++dest, ++first ) {
515 new (const_cast<pointer_mutable>(dest)) value_type( std::move( *first ) ); // placement new
516 dtor_one( const_cast<value_type*>( first ) ); // manual destruction, even after std::move (object still exists)
517 }
518 } else {
519 // move elems right
520 iterator last = const_cast<iterator>(first + count);
521 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));
522 dest += count - 1;
523 for(--last; first <= last; --dest, --last ) {
524 new (const_cast<pointer_mutable>(dest)) value_type( std::move( *last ) ); // placement new
525 dtor_one( last ); // manual destruction, even after std::move (object still exists)
526 }
527 }
528 }
529 }
530
531
532 protected:
533 /** Slicing ctor. Marks the created buffer shared() and pinned() and the given parent pinned(). */
535 : m_alloc_inst( parent.m_alloc_inst ), m_growth_factor( /* shared+pinned */ -1 ),
536 m_begin( begin ), m_end( m_begin + size ), m_storage_end( m_begin + size ),
537 m_position(position), m_limit(limit)
538 {
539 if( m_begin > parent.end() || m_end > parent.end() || m_position > m_end || m_limit > m_end ) {
540 throw jau::IllegalArgumentError("Slice: Parent "+parent.getInfo()+", this "+getInfo(), E_FILE_LINE);
541 }
542 parent.m_growth_factor = 0; // pinned
543 JAU_DARRAY_PRINTF("ctor 3: %s\n", getInfo().c_str());
544 }
545
546 public:
547
548 // ctor w/o elements
549
550 /**
551 * Default constructor, giving zero capacity and zero memory footprint.
552 */
553 constexpr darray() noexcept
554 : m_alloc_inst(), m_growth_factor(DEFAULT_GROWTH_FACTOR),
555 m_begin( nullptr ), m_end( nullptr ), m_storage_end( nullptr ),
556 m_position(m_begin), m_limit(m_end)
557 {
558 JAU_DARRAY_PRINTF("ctor def: %s\n", getInfo().c_str());
559 }
560
561 /**
562 * Creating an empty instance with initial capacity and other (default) properties.
563 * @param capacity initial capacity of the new instance.
564 * @param growth_factor given growth factor, defaults to DEFAULT_GROWTH_FACTOR. See setGrowthFactor().
565 * @param alloc given allocator_type, defaults to allocator_type()
566 */
567 constexpr explicit darray(size_type capacity, const float growth_factor=DEFAULT_GROWTH_FACTOR, const allocator_type& alloc = allocator_type())
568 : m_alloc_inst( alloc ), m_growth_factor( growth_factor ),
569 m_begin( allocStore(capacity) ), m_end( m_begin ), m_storage_end( m_begin + capacity ),
570 m_position(m_begin), m_limit(m_end)
571 {
572 JAU_DARRAY_PRINTF("ctor 1: %s\n", getInfo().c_str());
573 }
574
575 /**
576 * Creating a `size`d instance with initial size elements with default `value`.
577 * @param std::nullptr_t argument to distinguish constructor argument overload
578 * @param size initial capacity and size of new instance
579 * @param value initial value for the size elements, defaults to value_type()
580 * @param growth_factor given growth factor, defaults to DEFAULT_GROWTH_FACTOR. See setGrowthFactor().
581 * @param alloc given allocator_type, defaults to allocator_type()
582 */
583 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())
584 : m_alloc_inst( alloc ), m_growth_factor( growth_factor ),
585 m_begin( allocStore(size) ), m_end( m_begin + size ), m_storage_end( m_begin + size ),
586 m_position(m_begin), m_limit(m_end)
587 {
588 ctor_copy_value(m_begin, size, value);
589 JAU_DARRAY_PRINTF("ctor 2: %s\n", getInfo().c_str());
590 }
591
592 // copy_ctor on darray elements
593
594 /**
595 * Creates a new instance, copying all elements from the given darray.<br>
596 * Capacity and size will equal the given array, i.e. the result is a trimmed jau::darray.
597 *
598 * Throws jau::IllegalStateError if the source instance is sliced, i.e. sharing memory
599 *
600 * @param x the given darray, all elements will be copied into the new instance.
601 */
602 constexpr darray(const darray& x)
603 : m_alloc_inst( x.m_alloc_inst ), m_growth_factor( x.m_growth_factor ),
604 m_begin( clone_range(x.m_begin, x.m_end) ), m_end( m_begin + x.size() ), m_storage_end( m_begin + x.size() ),
605 m_position(m_begin), m_limit(m_end)
606 {
607 JAU_DARRAY_PRINTF("ctor copy0: this %s\n", getInfo().c_str());
608 JAU_DARRAY_PRINTF("ctor copy0: x %s\n", x.getInfo().c_str());
609 }
610
611 /**
612 * Creates a new instance, copying all elements from the given darray.<br>
613 * Capacity and size will equal the given array, i.e. the result is a trimmed jau::darray.
614 *
615 * Throws jau::IllegalStateError if the source instance is sliced, i.e. sharing memory
616 *
617 * @param x the given darray, all elements will be copied into the new instance.
618 * @param growth_factor custom growth factor
619 * @param alloc custom allocator_type instance
620 */
621 constexpr explicit darray(const darray& x, const float growth_factor, const allocator_type& alloc)
622 : m_alloc_inst( alloc ), m_growth_factor( growth_factor ),
623 m_begin( clone_range(x.m_begin, x.m_end) ), m_end( m_begin + x.size() ), m_storage_end( m_begin + x.size() ),
624 m_position(m_begin), m_limit(m_end)
625 {
626 JAU_DARRAY_PRINTF("ctor copy1: this %s\n", getInfo().c_str());
627 JAU_DARRAY_PRINTF("ctor copy1: x %s\n", x.getInfo().c_str());
628 }
629
630 /**
631 * Creates a new instance with custom initial storage capacity, copying all elements from the given darray.<br>
632 * Size will equal the given array.
633 *
634 * Throws jau::IllegalArgumentException if `_capacity < x.size()`.
635 *
636 * @param x the given darray, all elements will be copied into the new instance.
637 * @param _capacity custom initial storage capacity
638 * @param growth_factor custom growth factor, see setGrowthFactor().
639 * @param alloc custom allocator_type instance
640 */
641 constexpr explicit darray(const darray& x, const size_type _capacity, const float growth_factor, const allocator_type& alloc)
642 : m_alloc_inst( alloc ), m_growth_factor( growth_factor ),
643 m_begin( clone_range( _capacity, x.m_begin, x.m_end) ), m_end( m_begin + x.size() ), m_storage_end( m_begin + _capacity ),
644 m_position(m_begin), m_limit(m_end)
645 {
646 JAU_DARRAY_PRINTF("ctor copy2: this %s\n", getInfo().c_str());
647 JAU_DARRAY_PRINTF("ctor copy2: x %s\n", x.getInfo().c_str());
648 }
649
650 /**
651 * Like std::vector::operator=(&), assignment
652 */
653 constexpr darray& operator=(const darray& x) {
654 JAU_DARRAY_PRINTF("assignment copy.0: this %s\n", getInfo().c_str());
655 JAU_DARRAY_PRINTF("assignment copy.0: x %s\n", x.getInfo().c_str());
656 if( this != &x ) {
657 const size_type capacity_ = capacity();
658 const size_type x_size_ = x.size();
659 dtor_range(m_begin, m_end);
660 m_growth_factor = x.m_growth_factor;
661 if( x_size_ > capacity_ ) {
662 const difference_type pos = std::min<difference_type>(x_size_, m_position - m_begin);
663 freeStoreCheck();
664 m_begin = clone_range(x_size_, x.m_begin, x.m_end);
665 m_position = m_begin + pos;
666 set_iterator_end(x_size_, x_size_);
667 } else {
668 ctor_copy_range(m_begin, x.m_begin, x.m_end);
669 set_iterator_end(x_size_, capacity_);
670 }
671 }
672 JAU_DARRAY_PRINTF("assignment copy.X: this %s\n", getInfo().c_str());
673 JAU_DARRAY_PRINTF("assignment copy.X: x %s\n", x.getInfo().c_str());
674 return *this;
675 }
676
677 // move_ctor on darray elements
678
679 constexpr darray(darray && x) noexcept
680 : m_alloc_inst( std::move(x.m_alloc_inst) ), m_growth_factor( x.m_growth_factor ),
681 m_begin( std::move(x.m_begin) ), m_end( std::move(x.m_end) ), m_storage_end( std::move(x.m_storage_end) ),
682 m_position( std::move(x.m_position) ), m_limit( std::move(x.m_limit) )
683 {
684 JAU_DARRAY_PRINTF("ctor move0: this %s\n", getInfo().c_str());
685 JAU_DARRAY_PRINTF("ctor move0: x %s\n", x.getInfo().c_str());
686 // Moved source array has been taken over, flush sources' pointer to avoid value_type dtor releasing taken resources!
687 x.clear_iterator();
688 }
689
690 constexpr explicit darray(darray && x, const float growth_factor, const allocator_type& alloc) noexcept
691 : m_alloc_inst( std::move(alloc) ), m_growth_factor( growth_factor ),
692 m_begin( std::move(x.m_begin) ), m_end( std::move(x.m_end) ), m_storage_end( std::move(x.m_storage_end) ),
693 m_position( std::move(x.m_position) ), m_limit( std::move(x.m_limit) )
694 {
695 JAU_DARRAY_PRINTF("ctor move1: this %s\n", getInfo().c_str());
696 JAU_DARRAY_PRINTF("ctor move1: x %s\n", x.getInfo().c_str());
697 // Moved source array has been taken over, flush sources' pointer to avoid value_type dtor releasing taken resources!
698 x.clear_iterator();
699 }
700
701 /**
702 * Like std::vector::operator=(&&), move.
703 */
704 constexpr darray& operator=(darray&& x) noexcept {
705 JAU_DARRAY_PRINTF("assignment move.0: this %s\n", getInfo().c_str());
706 JAU_DARRAY_PRINTF("assignment move.0: x %s\n", x.getInfo().c_str());
707 if( this != &x ) {
708 clear(true);
709 m_alloc_inst = std::move(x.m_alloc_inst);
710 m_growth_factor = x.m_growth_factor;
711 m_begin = std::move(x.m_begin);
712 m_end = std::move(x.m_end);
713 m_storage_end = std::move(x.m_storage_end);
714 m_position = std::move(x.m_position);
715 m_limit = std::move(x.m_limit);
716
717 // Moved source array has been taken over, flush sources' pointer to avoid value_type dtor releasing taken resources!
718 x.clear_iterator();
719 }
720 JAU_DARRAY_PRINTF("assignment move.X: this %s\n", getInfo().c_str());
721 JAU_DARRAY_PRINTF("assignment move.X: x %s\n", x.getInfo().c_str());
722 return *this;
723 }
724
725 // ctor on const_iterator and foreign template iterator
726
727 /**
728 * Creates a new instance with custom initial storage capacity,
729 * copying all elements from the given const_iterator value_type range [first, last).<br>
730 * Size will equal the range [first, last), i.e. `size_type(last-first)`.
731 *
732 * Throws jau::IllegalArgumentException if `_capacity < size_type(last - first)`.
733 *
734 * @param _capacity custom initial storage capacity
735 * @param first const_iterator to first element of value_type range [first, last)
736 * @param last const_iterator to last element of value_type range [first, last)
737 * @param growth_factor given growth factor, defaults to DEFAULT_GROWTH_FACTOR. See setGrowthFactor().
738 * @param alloc custom allocator_type instance
739 */
740 constexpr explicit darray(const size_type _capacity, const_iterator first, const_iterator last,
741 const float growth_factor=DEFAULT_GROWTH_FACTOR, const allocator_type& alloc = allocator_type())
742 : m_alloc_inst( alloc ), m_growth_factor( growth_factor ),
743 m_begin( clone_range_check(_capacity, first, last) ), m_end(m_begin + size_type(last - first) ), m_storage_end( m_begin + _capacity ),
744 m_position(m_begin), m_limit(m_end)
745 {
746 JAU_DARRAY_PRINTF("ctor iters0: %s\n", getInfo().c_str());
747 }
748
749 /**
750 * Creates a new instance with custom initial storage capacity,
751 * copying all elements from the given template input-iterator value_type range [first, last).<br>
752 * Size will equal the range [first, last), i.e. `size_type(last-first)`.
753 *
754 * Throws jau::IllegalArgumentException if `_capacity < size_type(last - first)`.
755 *
756 * @tparam InputIt template input-iterator custom type
757 * @param _capacity custom initial storage capacity
758 * @param first template input-iterator to first element of value_type range [first, last)
759 * @param last template input-iterator to last element of value_type range [first, last)
760 * @param growth_factor given growth factor, defaults to DEFAULT_GROWTH_FACTOR. See setGrowthFactor().
761 * @param alloc custom allocator_type instance
762 */
763 template< class InputIt >
764 constexpr explicit darray(const size_type _capacity, InputIt first, InputIt last, // NOLINT(performance-unnecessary-value-param)
765 const float growth_factor=DEFAULT_GROWTH_FACTOR, const allocator_type& alloc = allocator_type())
766 : m_alloc_inst( alloc ), m_growth_factor( growth_factor ),
767 m_begin( clone_range_foreign(_capacity, first, last) ), m_end(m_begin + size_type(last - first) ), m_storage_end( m_begin + _capacity ),
768 m_position(m_begin), m_limit(m_end)
769 {
770 JAU_DARRAY_PRINTF("ctor iters1: %s\n", getInfo().c_str());
771 }
772
773 /**
774 * Creates a new instance,
775 * copying all elements from the given template input-iterator value_type range [first, last).<br>
776 * Size will equal the range [first, last), i.e. `size_type(last-first)`.
777 * @tparam InputIt template input-iterator custom type
778 * @param first template input-iterator to first element of value_type range [first, last)
779 * @param last template input-iterator to last element of value_type range [first, last)
780 * @param alloc custom allocator_type instance
781 */
782 template< class InputIt >
783 constexpr darray(InputIt first, InputIt last, const allocator_type& alloc = allocator_type()) // NOLINT(performance-unnecessary-value-param)
784 : m_alloc_inst( alloc ), m_growth_factor( DEFAULT_GROWTH_FACTOR ),
785 m_begin( clone_range_foreign(size_type(last - first), first, last) ), m_end(m_begin + size_type(last - first) ),
786 m_storage_end( m_begin + size_type(last - first) ),
787 m_position(m_begin), m_limit(m_end)
788 {
789 JAU_DARRAY_PRINTF("ctor iters2: %s\n", getInfo().c_str());
790 }
791
792 /**
793 * Create a new instance from an initializer list.
794 *
795 * Using the `std::initializer_list` requires to *copy* the given value_type objects into this darray.
796 *
797 * To utilize more efficient move semantics, see push_back_list() and jau::make_darray().
798 *
799 * @param initlist initializer_list.
800 * @param alloc allocator
801 * @see push_back_list()
802 * @see jau::make_darray()
803 */
804 constexpr darray(std::initializer_list<value_type> initlist, const allocator_type& alloc = allocator_type())
805 : m_alloc_inst( alloc ), m_growth_factor( DEFAULT_GROWTH_FACTOR ),
806 m_begin( clone_range_foreign(initlist.size(), initlist.begin(), initlist.end()) ),
807 m_end(m_begin + initlist.size() ), m_storage_end( m_begin + initlist.size() ),
808 m_position(m_begin), m_limit(m_end)
809 {
810 JAU_DARRAY_PRINTF("ctor initlist: %s\n", getInfo().c_str());
811 }
812
813 ~darray() noexcept {
814 JAU_DARRAY_PRINTF("dtor: %s\n", getInfo().c_str());
815 clear(true);
816 }
817
818 /**
819 * Returns `std::numeric_limits<difference_type>::max()` as the maximum array size.
820 * <p>
821 * We rely on the signed `difference_type` for pointer arithmetic,
822 * deducing ranges from iterator.
823 * </p>
824 */
825 constexpr static size_type max_size() noexcept { return DIFF_MAX; }
826
827 // iterator
828
829 constexpr iterator begin() noexcept { return m_begin; }
830 constexpr const_iterator begin() const noexcept { return m_begin; }
831 constexpr const_iterator cbegin() const noexcept { return m_begin; }
832
833 constexpr iterator end() noexcept { return m_end; }
834 constexpr const_iterator end() const noexcept { return m_end; }
835 constexpr const_iterator cend() const noexcept { return m_end; }
836
837#if 0
838 constexpr iterator storage_end() noexcept { return m_storage_end; }
839 constexpr const_iterator storage_end() const noexcept { return m_storage_end; }
840 constexpr const_iterator cstorage_end() const noexcept { return m_storage_end; }
841#endif
842
843 //
844 // relative positional access
845 //
846
847 /** Next relative read/write element index, with 0 <= position <= limit.*/
848 constexpr size_type position() const noexcept { return size_type(m_position - m_begin); }
849 /** Pointer to mutable next relative read/write element index, with 0 <= position <= limit.*/
850 constexpr pointer position_ptr() noexcept { return m_position; }
851 /** Pointer to immutable next relative read/write element index, with 0 <= position <= limit.*/
852 constexpr const_pointer position_ptr() const noexcept { return m_position; }
853 /** Sets position. Throws exception if new position is > limit. */
855 pointer p = m_begin + v;
856 if(p > m_limit) {
857 throw jau::IndexOutOfBoundsError(std::to_string(v), std::to_string(limit()), E_FILE_LINE);
858 }
859 m_position = p;
860 return *this;
861 }
862
863 /** Read/write limit, one element beyond maximum index with limit <= size/end. */
864 constexpr size_type limit() const noexcept { return size_type(m_limit - m_begin); }
865 /** Pointer to immutable read/write limit, one element beyond maximum element with limit <= size/end. */
866 constexpr const_pointer limit_ptr() const noexcept { return m_limit; }
867 /** Sets new limit and adjusts position if new limit is below. Throws exception if new limit is > size/end. */
869 pointer p = m_begin + v;
870 if(p > m_end) {
871 throw jau::IndexOutOfBoundsError(std::to_string(v), std::to_string(size()), E_FILE_LINE);
872 }
873 m_limit = p;
874 if (m_position > p) { m_position = p; }
875 return *this;
876 }
877
878 /** Sets limit to position and position to zero. */
879 constexpr self_t& flip() noexcept {
880 m_limit = m_position;
881 m_position = m_begin;
882 return *this;
883 }
884
885 /** Sets position to zero. */
886 constexpr self_t& rewind() noexcept {
887 m_position = m_begin;
888 return *this;
889 }
890
891 /**
892 * Clears the relative position and limit w/o destructing elements nor mutating storage.
893 *
894 * Sets position to zero and limit to size.
895 *
896 * @see position()
897 * @see limit()
898 * @see put()
899 * @see get()
900 */
901 constexpr self_t& clearPosition() noexcept {
902 m_position = m_begin;
903 m_limit = m_end;
904 return *this;
905 }
906
907 /**
908 * Relative get() for single value reference, increasing position() by one.
909 *
910 * Throws if position() >= limit().
911 */
913 if( m_position < m_limit ) {
914 return *(m_position++);
915 }
917 }
918
919 /**
920 * Relative put() for single value, increasing position().
921 *
922 * Grows storage and/or moves limit if required and grow==True().
923 *
924 * Throws if position() == limit().
925 *
926 * @param v value to be written
927 * @param grow set to Bool::True if allowing to grow, otherwise exception is thrown if position() == limit(). Defaults to Bool::False.
928 * @see setGrowthFactor()
929 */
931 if( *grow && m_position == m_limit ) {
932 if( m_limit == m_storage_end ) {
933 grow_storage_move();
934 resize(size()+1);
935 } else if( limit() == size() ) {
936 resize(size()+1);
937 } else {
938 m_limit++;
939 }
940 }
941 if( m_position < m_limit ) {
942 *(m_position++) = v;
943 return *this;
944 }
946 }
947
948 /**
949 * Relative put() for an initializer list of same type, increasing position().
950 *
951 * Grows storage and/or moves limit if required and grow==True().
952 *
953 * Throws if position() + count > limit().
954 *
955 * @param initlist values to be written
956 * @param grow set to Bool::True if allowing to grow, otherwise exception is thrown if position() == limit(). Defaults to Bool::False.
957 * @see setGrowthFactor()
958 */
959 constexpr_cxx20 self_t& put(std::initializer_list<value_type> initlist, Bool grow=Bool::False)
960 {
961 if( initlist.size() > std::numeric_limits<size_type>::max() ) {
962 throw jau::IllegalArgumentError("capacity "+std::to_string(initlist.size())+" > size_type max "+
963 std::to_string(std::numeric_limits<size_type>::max()), E_FILE_LINE);
964 }
965 const size_type count1 = size_type(initlist.size()); // number of elements to put
966 if( *grow && m_position + count1 > m_limit ) {
967 const size_type count2 = position() + count1 - limit(); // number of newly required elements (space)
968 if( m_limit + count2 > m_storage_end ) {
969 grow_storage_move(limit() + count2 - capacity());
970 // resize(limit() + count2);
971 m_end += count2;
972 m_limit += count2;
973 } else if( m_limit + count2 > m_end ) {
974 // resize(limit() + count2);
975 m_end += count2;
976 m_limit += count2;
977 } else {
978 m_limit+=count2;
979 }
980 }
981 if( m_position + count1 - 1 < m_limit ) {
982 ctor_copy_range_foreign(m_position, initlist.begin(), initlist.end());
983 m_position+=count1;
984 return *this;
985 }
987 }
988
989 /**
990 * Relative put() for multiple value of an assignable type fitting into value_type, increasing position().
991 *
992 * Grows storage and/or moves limit if required and grow==True().
993 *
994 * Throws if position() + count > limit().
995 *
996 * @tparam Targs argument types, which must all be of the same type
997 * @param grow set to Bool::True if allowing to grow, otherwise exception is thrown if position() == limit(). Defaults to Bool::False.
998 * @param args values to be written
999 * @see setGrowthFactor()
1000 */
1001 template<typename... Targs,
1002 std::enable_if_t< jau::is_all_same_v<Targs...> &&
1003 sizeof(Value_type) >= sizeof(jau::first_type<Targs...>) && // NOLINT(bugprone-sizeof-expression)
1004 std::is_assignable_v<value_type&, jau::first_type<Targs...>>, bool> = true>
1005 constexpr_cxx20 self_t& putN(Bool grow, const Targs&...args) {
1006 const size_type count1 = sizeof...(args);
1007 if( *grow && m_position + count1 > m_limit ) {
1008 const size_type count2 = position() + count1 - limit();
1009 if( m_limit + count2 > m_storage_end ) {
1010 grow_storage_move(limit() + count2 - capacity());
1011 resize(limit() + count2);
1012 } else if( m_limit + count2 > m_end ) {
1013 resize(limit() + count2);
1014 } else {
1015 m_limit+=count2;
1016 }
1017 }
1018 if( m_position + count1 - 1 < m_limit ) {
1019 ( (*m_position++ = static_cast<Value_type>(args)), ... ); // NOLINT(bugprone-signed-char-misuse)
1020 return *this;
1021 }
1023 }
1024
1025 /**
1026 * Returns a sliced duplicate starting from this buffers' current position.
1027 *
1028 * This buffer is pinned() afterwards, to not allow storage mutation.
1029 *
1030 * Returned buffer is shared() and pinned(), i.e. shares the same storage of this buffer.
1031 * Its position is zero and limit set to this buffers' remaining elements.
1032 *
1033 * @see pinned()
1034 * @see shared()
1035 * @see setGrowthFactor()
1036 */
1038 const size_type new_size = size_type(m_limit - m_position);
1039 return darray(*this, m_position, m_position, m_position+new_size, new_size);
1040 }
1041
1042 /**
1043 * Returns a sliced duplicate starting from the given `idx`.
1044 *
1045 * This buffer is pinned() afterwards, to not allow storage mutation.
1046 *
1047 * Returned buffer is shared() and pinned(), i.e. shares the same storage of this buffer.
1048 * Its position is zero and limit set to the given `length`.
1049 */
1051 if(m_position + idx + length > m_limit) {
1052 throw jau::IndexOutOfBoundsError(std::to_string(position()), std::to_string(limit()), E_FILE_LINE);
1053 }
1054 return darray(*this, m_position+idx, m_position+idx, m_position+idx+length, length);
1055 }
1056
1057 /**
1058 * Returns a duplicate with same position and limit.
1059 *
1060 * This buffer is pinned() afterwards, to not allow storage mutation.
1061 *
1062 * Returned buffer is shared() and pinned(), i.e. shares the same storage of this buffer.
1063 * Its position and limit are same as with this buffer.
1064 */
1066 m_growth_factor = 0; // pinned
1067 return darray(*this, m_begin, m_position, m_limit, size());
1068 }
1069
1070 /** Returns limit - position. */
1071 constexpr size_type remaining() const noexcept { return size_type(m_limit - m_position); }
1072 /** Returns whether position < limit, i.e. has remaining elements. */
1073 constexpr bool hasRemaining() const noexcept { return m_position < m_limit; }
1074
1075 //
1076 // read access
1077 //
1078
1079 const allocator_type& get_allocator_ref() const noexcept {
1080 return m_alloc_inst;
1081 }
1082
1084 return allocator_type(m_alloc_inst);
1085 }
1086
1087 /**
1088 * Return the current capacity.
1089 */
1090 constexpr size_type capacity() const noexcept { return size_type(m_storage_end - m_begin); }
1091
1092 /**
1093 * Return the current capacity() multiplied by the growth factor, minimum is max(capacity()+add, 10).
1094 */
1095 constexpr size_type get_grown_capacity(size_type add=1) const noexcept {
1096 const size_type a_capacity = capacity();
1097 return std::max<size_type>( std::max<size_type>( MIN_SIZE_AT_GROW, a_capacity+add ),
1098 static_cast<size_type>( ::roundf(a_capacity * growthFactor()) ) );
1099 }
1100
1101 /**
1102 * Like std::vector::empty().
1103 */
1104 constexpr bool empty() const noexcept { return m_begin == m_end; }
1105
1106 /**
1107 * Returns true if capacity has been reached and the next push_back()
1108 * will grow the storage and invalidates all iterators and references.
1109 */
1110 constexpr bool capacity_reached() const noexcept { return m_end >= m_storage_end; }
1111
1112 /**
1113 * Like std::vector::size().
1114 */
1115 constexpr size_type size() const noexcept { return size_type(m_end - m_begin); }
1116
1117 // mixed mutable/immutable element access
1118
1119 /**
1120 * Like std::vector::front(), mutable access.
1121 */
1122 constexpr reference front() { return *m_begin; }
1123
1124 /**
1125 * Like std::vector::front(), immutable access.
1126 */
1127 constexpr const_reference front() const { return *m_begin; }
1128
1129 /**
1130 * Like std::vector::back(), mutable access.
1131 */
1132 constexpr reference back() { return *(m_end-1); }
1133
1134 /**
1135 * Like std::vector::back(), immutable access.
1136 */
1137 constexpr const_reference back() const { return *(m_end-1); }
1138
1139 /**
1140 * Like std::vector::data(), const immutable pointer
1141 */
1142 constexpr const_pointer data() const noexcept { return m_begin; }
1143
1144 /**
1145 * Like std::vector::data(), mutable pointer
1146 */
1147 constexpr pointer data() noexcept { return m_begin; }
1148
1149 /**
1150 * Like std::vector::operator[](size_type), immutable reference.
1151 */
1153 return *(m_begin+i);
1154 }
1155
1156 /**
1157 * Like std::vector::operator[](size_type), mutable reference.
1158 */
1160 return *(m_begin+i);
1161 }
1162
1163 /**
1164 * Like std::vector::at(size_type), immutable reference.
1165 */
1167 if( 0 <= i && i < size() ) {
1168 return *(m_begin+i);
1169 }
1171 }
1172
1173 /**
1174 * Like std::vector::at(size_type), mutable reference.
1175 */
1177 if( 0 <= i && i < size() ) {
1178 return *(m_begin+i);
1179 }
1181 }
1182
1183 // write access, mutable array operations
1184
1185 /**
1186 * Like std::vector::reserve(), increases this instance's capacity to `new_capacity`.
1187 * <p>
1188 * Only creates a new storage and invalidates iterators if `new_capacity`
1189 * is greater than the current jau::darray::capacity().
1190 * </p>
1191 */
1192 constexpr self_t& reserve(size_type new_capacity) {
1193 if( new_capacity > capacity() ) {
1194 realloc_storage_move(new_capacity);
1195 }
1196 return *this;
1197 }
1198
1199 /**
1200 * Like std::vector::resize(size_type, const value_type&)
1201 */
1202 constexpr self_t& resize(size_type new_size, const value_type& val) {
1203 const size_type sz = size();
1204 if( new_size > sz ) {
1205 if( new_size > capacity() ) {
1206 realloc_storage_move(new_size);
1207 }
1208 const size_type new_elem_count = new_size - sz;
1209 m_end += new_elem_count;
1210 m_limit += new_elem_count;
1211 ctor_copy_value(m_begin + sz, new_elem_count, val);
1212 } else if( new_size < sz ) {
1213 const size_type del_elem_count = dtor_range(m_begin + new_size, m_end);
1214 assert(sz - new_size == del_elem_count);
1215 m_end -= del_elem_count;
1216 }
1217 return *this;
1218 }
1219
1220 /**
1221 * Like std::vector::resize(size_type)
1222 */
1223 constexpr self_t& resize(size_type new_size) { return resize(new_size, value_type()); }
1224
1225 /**
1226 * Like std::vector::shrink_to_fit(), but ensured `constexpr`.
1227 *
1228 * If capacity() > size(), reallocate storage to size().
1229 */
1230 constexpr self_t& shrink_to_fit() {
1231 const size_type size_ = size();
1232 if( capacity() > size_ ) {
1233 realloc_storage_move(size_);
1234 }
1235 return *this;
1236 }
1237
1238 /**
1239 * Like std::vector::assign()
1240 * @tparam InputIt foreign input-iterator to range of value_type [first, last)
1241 * @param first first foreign input-iterator to range of value_type [first, last)
1242 * @param last last foreign input-iterator to range of value_type [first, last)
1243 */
1244 template< class InputIt >
1245 constexpr void assign( InputIt first, InputIt last ) { // NOLINT(performance-unnecessary-value-param)
1246 const size_type size_ = size();
1247 const size_type capacity_ = capacity();
1248 const size_type x_size_ = size_type(last - first);
1249 dtor_range(m_begin, m_end);
1250 if( x_size_ > capacity_ ) {
1251 const difference_type pos = std::min<difference_type>(x_size_, m_position - m_begin);
1252 freeStoreCheck();
1253 m_begin = clone_range_foreign(x_size_, first, last);
1254 m_position = m_begin + pos;
1255 set_iterator_end(x_size_, x_size_);
1256 } else {
1257 ctor_copy_range_foreign(m_begin, first, last);
1258 set_iterator_end(x_size_, capacity_);
1259 }
1260 }
1261 /**
1262 * Like std::vector::assign(), but non-template overload using const_iterator.
1263 * @param first first const_iterator to range of value_type [first, last)
1264 * @param last last const_iterator to range of value_type [first, last)
1265 */
1266 constexpr void assign( const_iterator first, const_iterator last ) {
1267 const size_type size_ = size();
1268 const size_type capacity_ = capacity();
1269 const size_type x_size_ = size_type(last - first);
1270 dtor_range(m_begin, m_end);
1271 if( x_size_ > capacity_ ) {
1272 const difference_type pos = std::min<difference_type>(x_size_, m_position - m_begin);
1273 freeStoreCheck();
1274 m_begin = clone_range_check(x_size_, first, last);
1275 m_position = m_begin + pos;
1276 set_iterator_end(x_size_, x_size_);
1277 } else {
1278 ctor_copy_range_check(m_begin, first, last);
1279 set_iterator_end(x_size_, capacity_);
1280 }
1281 }
1282
1283 /**
1284 * Like std::vector::clear(), calls destructor on all elements and leaving capacity unchanged.
1285 *
1286 * Sets position and limit to zero.
1287 *
1288 * Use clear(true) or subsequently shrink_to_fit() to release capacity (storage).
1289 *
1290 * @see clear(bool)
1291 * @see shrink_to_fit()
1292 */
1293 constexpr self_t& clear() noexcept {
1294 dtor_range(m_begin, m_end);
1295 m_end = m_begin;
1296 m_position = m_begin;
1297 m_limit = m_end;
1298 return *this;
1299 }
1300
1301 /**
1302 * Like std::vector::clear(), calls destructor on all elements.
1303 *
1304 *
1305 * Sets position and limit to zero.
1306 *
1307 * If `releaseMem` is `true`, releases capacity (memory),
1308 * otherwise leaves capacity unchanged and sets position to zero, limit to capacity.
1309 *
1310 * @see clear()
1311 */
1312 constexpr self_t& clear(bool releaseMem) noexcept {
1313 clear();
1314 if( releaseMem ) {
1315 if( !shared() ) {
1316 freeStore();
1317 }
1318 m_begin = nullptr;
1319 m_end = nullptr;
1320 m_storage_end = nullptr;
1321 m_position = nullptr;
1322 m_limit = nullptr;
1323 }
1324 return *this;
1325 }
1326
1327 /**
1328 * Like std::vector::swap().
1329 */
1330 constexpr void swap(darray& x) noexcept {
1331 JAU_DARRAY_PRINTF("swap.0: this %s\n", getInfo().c_str());
1332 JAU_DARRAY_PRINTF("swap.0: x %s\n", x.getInfo().c_str());
1333 std::swap(m_alloc_inst, x.m_alloc_inst);
1334 std::swap(m_growth_factor, x.m_growth_factor);
1335 std::swap(m_begin, x.m_begin);
1336 std::swap(m_end, x.m_end);
1337 std::swap(m_storage_end, x.m_storage_end);
1338 std::swap(m_position, x.m_position);
1339 std::swap(m_limit, x.m_limit);
1340 JAU_DARRAY_PRINTF("swap.X: this %s\n", getInfo().c_str());
1341 JAU_DARRAY_PRINTF("swap.X: x %s\n", x.getInfo().c_str());
1342 }
1343
1344 /**
1345 * Like std::vector::pop_back().
1346 *
1347 * Updates end, limit and adjusts position if > new limit.
1348 */
1349 constexpr void pop_back() noexcept {
1350 if( m_begin != m_end ) {
1351 dtor_one( --m_end );
1352 m_limit = m_end;
1353 if( m_position > m_limit ) { m_position = m_limit; }
1354 }
1355 }
1356
1357 /**
1358 * Like std::vector::erase(), removes the elements at pos.
1359 *
1360 * Updates end, limit and adjusts position if > > cpos
1361 *
1362 * @return iterator following the last removed element.
1363 */
1364 constexpr iterator erase (const_iterator cpos) {
1365 iterator pos = const_cast<iterator>(cpos);
1366 if( m_begin <= pos && pos < m_end ) {
1367 dtor_one( pos );
1368 const difference_type right_count = m_end - ( pos + 1 ); // pos is exclusive
1369 if( 0 < right_count ) {
1370 move_elements(pos, pos+1, right_count); // move right elems one left
1371 }
1372 m_limit = --m_end;
1373 if( m_position > m_begin && m_position > pos ) { --m_position; }
1374 }
1375 return m_begin <= pos && pos <= m_end ? pos : m_end;
1376 }
1377
1378 /**
1379 * Like std::vector::erase(), removes the elements in the range [first, last).
1380 *
1381 * Updates end, limit and adjusts position if > cfirst.
1382 *
1383 * @return iterator following the last removed element.
1384 */
1385 constexpr iterator erase (const_iterator cfirst, const_iterator clast) {
1386 iterator first = const_cast<iterator>(cfirst);
1387 const size_type count = dtor_range(first, clast);
1388 if( count > 0 ) {
1389 const difference_type right_count = m_end - clast; // last is exclusive
1390 if( 0 < right_count ) {
1391 move_elements(first, clast, right_count); // move right elems count left
1392 }
1393 m_end -= count;
1394 m_limit = m_end;
1395 if( m_position > m_begin && m_position > first )
1396 { m_position -= std::min(count, size_type(m_position - first)); }
1397 }
1398 return m_begin <= first && first <= m_end ? first : m_end;
1399 }
1400
1401 /**
1402 * Similar to std::vector::erase() using an index, removes the elements at pos_idx.
1403 *
1404 * Updates end, limit and adjusts position if > new limit.
1405 *
1406 * @return iterator following the last removed element.
1407 */
1408 constexpr iterator erase (const size_type pos_idx) {
1409 return erase(m_begin + pos_idx);
1410 }
1411
1412 /**
1413 * Similar to std::vector::erase() using indices, removes the elements in the range [first_idx, last_idx).
1414 *
1415 * Updates end, limit and adjusts position if > new limit.
1416 *
1417 * @return iterator following the last removed element.
1418 */
1419 constexpr iterator erase (const size_type first_idx, const size_type last_idx) {
1420 return erase(m_begin + first_idx, m_begin + last_idx);
1421 }
1422
1423 /**
1424 * Like std::vector::insert(), copy
1425 *
1426 * Inserts the element before pos
1427 * and moves all elements from there to the right beforehand.
1428 *
1429 * size/end and limit will be increased by one.
1430 *
1431 * @param pos iterator before which the content will be inserted. pos may be the end() iterator
1432 * @param x element value to insert
1433 */
1434 constexpr iterator insert(const_iterator pos, const value_type& x) {
1435 if( m_begin <= pos && pos <= m_end ) {
1436 if( m_end == m_storage_end ) {
1437 const size_type pos_idx = pos - m_begin;
1438 grow_storage_move();
1439 pos = m_begin + pos_idx;
1440 }
1441 const difference_type right_count = m_end - pos; // include original element at 'pos_new'
1442 if( 0 < right_count ) {
1443 move_elements(const_cast<iterator>(pos+1), pos, right_count); // move elems one right
1444 }
1445 new (const_cast<pointer_mutable>(pos)) value_type( x ); // placement new
1446 m_limit = ++m_end;
1447
1448 return m_begin <= pos && pos <= m_end ? const_cast<iterator>(pos) : m_end;
1449 } else {
1450 throw jau::IndexOutOfBoundsError(std::to_string(difference_type(pos - m_begin)), std::to_string(size()), E_FILE_LINE);
1451 }
1452 }
1453
1454 /**
1455 * Similar to std::vector::insert() using an index, copy
1456 * @param pos_idx index before which the content will be inserted. index may be the end size() index
1457 * @param x element value to insert
1458 * @see insert()
1459 */
1460 constexpr iterator insert(const size_type pos_idx, const value_type& x) {
1461 return insert(m_begin + pos_idx, x);
1462 }
1463
1464 /**
1465 * Like std::vector::insert(), move
1466 *
1467 * Inserts the element before the given position
1468 * and moves all elements from there to the right beforehand.
1469 *
1470 * size/end and limit will be increased by one.
1471 *
1472 * @param pos iterator before which the content will be inserted. pos may be the end() iterator
1473 * @param x element value to be moved into
1474 */
1476 if( m_begin <= pos && pos <= m_end ) {
1477 const size_type pos_idx = pos - m_begin;
1478 if( m_end == m_storage_end ) {
1479 grow_storage_move();
1480 }
1481 iterator pos_new = m_begin + pos_idx;
1482 const difference_type right_count = m_end - pos_new; // include original element at 'pos_new'
1483 if( 0 < right_count ) {
1484 move_elements(pos_new+1, pos_new, right_count); // move elems one right
1485 }
1486 new (const_cast<pointer_mutable>(pos_new)) value_type( std::move( x ) ); // placement new
1487 m_limit = ++m_end;
1488
1489 return m_begin <= pos_new && pos_new <= m_end ? pos_new : m_end;
1490 } else {
1491 throw jau::IndexOutOfBoundsError(std::to_string(difference_type(pos - m_begin)), std::to_string(size()), E_FILE_LINE);
1492 }
1493 }
1494
1495 /**
1496 * Like std::vector::emplace(), construct a new element in place.
1497 *
1498 * Constructs the element before the given position using placement new
1499 * and moves all elements from there to the right beforehand.
1500 *
1501 * size/end and limit will be increased by one.
1502 *
1503 * @param pos iterator before which the content will be inserted. pos may be the end() iterator
1504 * @param args arguments to forward to the constructor of the element
1505 */
1506 template<typename... Args>
1507 constexpr iterator emplace(const_iterator pos, Args&&... args) {
1508 if( m_begin <= pos && pos <= m_end ) {
1509 const size_type pos_idx = pos - m_begin;
1510 if( m_end == m_storage_end ) {
1511 grow_storage_move();
1512 }
1513 iterator pos_new = m_begin + pos_idx;
1514 const difference_type right_count = m_end - pos_new; // include original element at 'pos_new'
1515 if( 0 < right_count ) {
1516 move_elements(pos_new+1, pos_new, right_count); // move elems one right
1517 }
1518 new (const_cast<pointer_mutable>(pos_new)) value_type( std::forward<Args>(args)... ); // placement new, construct in-place
1519 m_limit = ++m_end;
1520
1521 return m_begin <= pos_new && pos_new <= m_end ? pos_new : m_end;
1522 } else {
1523 throw jau::IndexOutOfBoundsError(std::to_string(difference_type(pos - m_begin)), std::to_string(size()), E_FILE_LINE);
1524 }
1525 }
1526
1527 /**
1528 * Like std::vector::insert(), inserting the value_type range [first, last).
1529 *
1530 * size/end and limit will be increased by inserted elements.
1531 *
1532 * @tparam InputIt foreign input-iterator to range of value_type [first, last)
1533 * @param pos iterator before which the content will be inserted. pos may be the end() iterator
1534 * @param first first foreign input-iterator to range of value_type [first, last)
1535 * @param last last foreign input-iterator to range of value_type [first, last)
1536 * @return Iterator pointing to the first element inserted, or pos if first==last.
1537 */
1538 template< class InputIt >
1539 constexpr iterator insert( const_iterator pos, InputIt first, InputIt last ) { // NOLINT(performance-unnecessary-value-param)
1540 if( m_begin <= pos && pos <= m_end ) {
1541 const size_type new_elem_count = size_type(last - first);
1542 const size_type pos_idx = pos - m_begin;
1543 if( m_end + new_elem_count > m_storage_end ) {
1544 grow_storage_move(new_elem_count);
1545 }
1546 iterator pos_new = m_begin + pos_idx;
1547 const difference_type right_count = m_end - pos_new; // include original element at 'pos_new'
1548 if( 0 < right_count ) {
1549 move_elements(pos_new + new_elem_count, pos_new, right_count); // move elems count right
1550 }
1551 ctor_copy_range_foreign(pos_new, first, last);
1552 m_end += new_elem_count;
1553 m_limit = m_end;
1554
1555 return m_begin <= pos_new && pos_new <= m_end ? pos_new : m_end;
1556 } else {
1557 throw jau::IndexOutOfBoundsError(std::to_string(difference_type(pos - m_begin)), std::to_string(size()), E_FILE_LINE);
1558 }
1559 }
1560
1561 /**
1562 * Like std::vector::push_back(), copy
1563 *
1564 * size/end and limit will be increased by one, position set to limit/end.
1565 *
1566 * @param x the value to be added at the tail.
1567 */
1568 constexpr void push_back(const value_type& x) {
1569 if( m_end == m_storage_end ) {
1570 grow_storage_move();
1571 }
1572 new (const_cast<pointer_mutable>(m_end)) value_type( x ); // placement new
1573 m_limit = ++m_end;
1574 m_position = m_limit;
1575 }
1576
1577 /**
1578 * Like std::vector::push_back(), move
1579 *
1580 * size/end and limit will be increased by one, position set to limit/end.
1581 *
1582 * @param x the value to be added at the tail.
1583 */
1584 constexpr void push_back(value_type&& x) {
1585 if( m_end == m_storage_end ) {
1586 grow_storage_move();
1587 }
1588 new (const_cast<pointer_mutable>(m_end)) value_type( std::move(x) ); // placement new, just one element - no optimization
1589 m_limit = ++m_end;
1590 m_position = m_limit;
1591 }
1592
1593 /**
1594 * Like std::push_back(), but for an initializer list to copy.
1595 *
1596 * size/end and limit will be increased by inserted elements, position set to limit/end.
1597 *
1598 * @param initlist values to be written
1599 */
1600 constexpr_cxx20 void push_back(std::initializer_list<value_type> initlist)
1601 {
1602 if( initlist.size() > std::numeric_limits<size_type>::max() ) {
1603 throw jau::IllegalArgumentError("capacity "+std::to_string(initlist.size())+" > size_type max "+
1604 std::to_string(std::numeric_limits<size_type>::max()), E_FILE_LINE);
1605 }
1606 const size_type count1 = size_type(initlist.size());
1607 if( m_end + count1 > m_storage_end ) {
1608 const size_type epos = size_type(m_end - m_begin);
1609 const size_type spos = size_type(m_storage_end - m_begin);
1610 const size_type count2 = epos + count1 - spos;
1611 grow_storage_move(limit() + count2 - capacity());
1612 }
1613 ctor_copy_range_foreign(m_end, initlist.begin(), initlist.end());
1614 m_end += count1;
1615 m_limit = m_end;
1616 m_position = m_limit;
1617 }
1618
1619 /**
1620 * Like std::vector::push_front(), copy
1621 *
1622 * size/end and limit will be increased by one.
1623 *
1624 * @param x the value to be added at the front.
1625 */
1626 constexpr void push_front(const value_type& x) {
1627 insert(m_begin, x);
1628 }
1629
1630 /**
1631 * Like std::vector::push_front(), move
1632 *
1633 * size/end and limit will be increased by one.
1634 *
1635 * @param x the value to be added at the front.
1636 */
1637 constexpr void push_front(value_type&& x) {
1638 insert(m_begin, std::move(x));
1639 }
1640
1641 /**
1642 * Like std::vector::emplace_back(), construct a new element in place at the end().
1643 *
1644 * Constructs the element at the end() using placement new.
1645 *
1646 * size/end and limit will be increased by one, position set to limit/end.
1647 *
1648 * @param args arguments to forward to the constructor of the element
1649 */
1650 template<typename... Args>
1651 constexpr reference emplace_back(Args&&... args) {
1652 if( m_end == m_storage_end ) {
1653 grow_storage_move();
1654 }
1655 new (const_cast<pointer_mutable>(m_end)) value_type( std::forward<Args>(args)... ); // placement new, construct in-place
1656 reference res = *m_end;
1657 m_limit = ++m_end;
1658 m_position=m_limit;
1659 return res;
1660 }
1661
1662 /**
1663 * Like std::vector::push_back(), but appends the value_type range [first, last).
1664 *
1665 * size/end and limit will be increased by inserted elements, position set to limit/end.
1666 *
1667 * @tparam InputIt foreign input-iterator to range of value_type [first, last)
1668 * @param first first foreign input-iterator to range of value_type [first, last)
1669 * @param last last foreign input-iterator to range of value_type [first, last)
1670 */
1671 template< class InputIt >
1672 constexpr void push_back( InputIt first, InputIt last ) { // NOLINT(performance-unnecessary-value-param)
1673 const size_type count = size_type(last - first);
1674
1675 if( m_end + count > m_storage_end ) {
1676 grow_storage_move(count);
1677 }
1678 ctor_copy_range_foreign(m_end, first, last);
1679 m_end += count;
1680 m_limit = m_end;
1681 m_position=m_limit;
1682 }
1683
1684 /**
1685 * Like push_back(), but for more multiple const r-value to copy.
1686 *
1687 * size/end and limit will be increased by inserted elements, position set to limit/end.
1688 *
1689 * @tparam Args
1690 * @param args r-value references to copy into this storage
1691 */
1692 template <typename... Args>
1693 constexpr void push_back_list(const Args&... args)
1694 {
1695 const size_type count = sizeof...(Args);
1696
1697 JAU_DARRAY_PRINTF("push_back_list.copy.0: %zu elems: this %s\n", count, getInfo().c_str());
1698
1699 if( m_end + count > m_storage_end ) {
1700 grow_storage_move(count);
1701 }
1702 // C++17 fold expression on above C++11 template pack args
1703 ( new (const_cast<pointer_mutable>(m_end++)) value_type( args ), ... ); // @suppress("Syntax error")
1704 m_limit = m_end;
1705 m_position=m_limit;
1706
1707 JAU_DARRAY_PRINTF("push_back_list.copy.X: %zu elems: this %s\n", count, getInfo().c_str());
1708 }
1709
1710 /**
1711 * Like push_back(), but for more multiple r-value references to move.
1712 *
1713 * size/end and limit will be increased by inserted elements, position set to limit/end.
1714 *
1715 * @tparam Args
1716 * @param args r-value references to move into this storage
1717 * @see jau::make_darray()
1718 */
1719 template <typename... Args>
1720 constexpr void push_back_list(Args&&... args)
1721 {
1722 const size_type count = sizeof...(Args);
1723
1724 JAU_DARRAY_PRINTF("push_back_list.move.0: %zu elems: this %s\n", count, getInfo().c_str());
1725
1726 if( m_end + count > m_storage_end ) {
1727 grow_storage_move(count);
1728 }
1729 // C++17 fold expression on above C++11 template pack args
1730 ( new (const_cast<pointer_mutable>(m_end++)) value_type( std::move(args) ), ... ); // @suppress("Syntax error")
1731 m_limit = m_end;
1732 m_position=m_limit;
1733
1734 JAU_DARRAY_PRINTF("push_back_list.move.X: %zu elems: this %s\n", count, getInfo().c_str());
1735 }
1736
1737 /**
1738 * Generic value_type equal comparator to be user defined for e.g. jau::darray::push_back_unique().
1739 * @param a one element of the equality test.
1740 * @param b the other element of the equality test.
1741 * @return true if both are equal
1742 */
1743 typedef bool(*equal_comparator)(const value_type& a, const value_type& b);
1744
1745 /**
1746 * Like std::vector::push_back(), but only if the newly added element does not yet exist.
1747 * <p>
1748 * Examples
1749 * <pre>
1750 * static jau::darray<Thing>::equal_comparator thingEqComparator =
1751 * [](const Thing &a, const Thing &b) -> bool { return a == b; };
1752 * ...
1753 * jau::darray<Thing> list;
1754 *
1755 * bool added = list.push_back_unique(new_element, thingEqComparator);
1756 * ...
1757 * darray<std::shared_ptr<Thing>> listOfRefs;
1758 * bool added = listOfRefs.push_back_unique(new_element,
1759 * [](const std::shared_ptr<Thing> &a, const std::shared_ptr<Thing> &b) -> bool { return *a == *b; });
1760 * </pre>
1761 * </p>
1762 * @param x the value to be added at the tail, if not existing yet.
1763 * @param comparator the equal comparator to return true if both given elements are equal
1764 * @return true if the element has been uniquely added, otherwise false
1765 * @see push_back()
1766 */
1767 constexpr bool push_back_unique(const value_type& x, equal_comparator comparator) {
1768 for(auto it = m_begin; it != m_end; ) {
1769 if( comparator( *it, x ) ) {
1770 return false; // already included
1771 } else {
1772 ++it;
1773 }
1774 }
1775 push_back(x);
1776 return true;
1777 }
1778
1779 /**
1780 * Erase either the first matching element or all matching elements using erase().
1781 * <p>
1782 * Examples
1783 * <pre>
1784 * darray<Thing> list;
1785 * int count = list.erase_matching(element, true,
1786 * [](const Thing &a, const Thing &b) -> bool { return a == b; });
1787 * ...
1788 * static jau::darray<Thing>::equal_comparator thingRefEqComparator =
1789 * [](const std::shared_ptr<Thing> &a, const std::shared_ptr<Thing> &b) -> bool { return *a == *b; };
1790 * ...
1791 * darray<std::shared_ptr<Thing>> listOfRefs;
1792 * int count = listOfRefs.erase_matching(element, false, thingRefEqComparator);
1793 * </pre>
1794 * </p>
1795 * @param x the value to be added at the tail, if not existing yet.
1796 * @param all_matching if true, erase all matching elements, otherwise only the first matching element.
1797 * @param comparator the equal comparator to return true if both given elements are equal
1798 * @return number of erased elements
1799 * @see erase()
1800 */
1801 constexpr size_type erase_matching(const value_type& x, const bool all_matching, equal_comparator comparator) {
1802 size_type count = 0;
1803 for(auto it = m_end-1; m_begin <= it; --it) {
1804 if( comparator( *it, x ) ) {
1805 erase(it);
1806 ++count;
1807 if( !all_matching ) {
1808 break;
1809 }
1810 }
1811 }
1812 return count;
1813 }
1814
1815 /**
1816 * Erase either the first matching element or all matching elements.
1817 * <p>
1818 * Examples
1819 * <pre>
1820 * darray<Thing> list;
1821 * int count = list.erase_if(true,
1822 * [&element](const Thing &a) -> bool { return a == element; });
1823 * </pre>
1824 * </p>
1825 * @param all_matching if true, erase all matching elements, otherwise only the first matching element.
1826 * @param p the unary predicate test to return true if given elements shall be erased
1827 * @return number of erased elements
1828 */
1829 template<class UnaryPredicate>
1830 constexpr size_type erase_if(const bool all_matching, UnaryPredicate p) {
1831 size_type count = 0;
1832 for(auto it = m_end-1; m_begin <= it; --it) {
1833 if( p( *it ) ) {
1834 erase(it);
1835 ++count;
1836 if( !all_matching ) {
1837 break;
1838 }
1839 }
1840 }
1841 return count;
1842 }
1843
1844 std::string toString() const noexcept {
1845 std::string res("{ " + valueSignature().name() + ", " + std::to_string( size() ) + "/" + std::to_string( capacity() ) + ": ");
1846 int i=0;
1847 jau::for_each_const(*this, [&res, &i](const value_type & e) {
1848 if( 1 < ++i ) { res.append(", "); }
1849 res.append( jau::to_string(e) );
1850 } );
1851 res.append(" }");
1852 return res;
1853 }
1854
1855 std::string getInfo() const noexcept {
1856 difference_type cap_ = (m_storage_end-m_begin);
1857 difference_type size_ = (m_end-m_begin);
1858 std::string res("darray[this "+jau::toHexString(this)+
1859 ", " + valueSignature().name() +
1860 ", size "+std::to_string(size_)+" / "+std::to_string(cap_)+
1861 ", flags[");
1862 if( pinned() ) {
1863 res.append("pinned");
1864 if( shared() ) {
1865 res.append(", shared");
1866 }
1867 }
1868 res.append("], growth "+std::to_string(m_growth_factor)+
1869 ", type[integral "+std::to_string(std::is_integral_v<Value_type>)+
1870 ", trivialCpy "+std::to_string(std::is_trivially_copyable_v<Value_type>)+
1871 "], uses[mmove "+std::to_string(uses_memmove)+
1872 ", realloc "+std::to_string(uses_realloc)+
1873 ", smem "+std::to_string(uses_secmem)+
1874 "], begin "+jau::toHexString(m_begin)+
1875 ", [pos "+std::to_string(m_position-m_begin)+
1876 ", lim "+std::to_string(m_limit-m_begin)+
1877 ", end "+std::to_string(m_end-m_begin)+" "+jau::toHexString(m_end)+
1878 ", send "+std::to_string(m_storage_end-m_begin)+" "+jau::toHexString(m_storage_end)+
1879 "]");
1880 return res;
1881 }
1882 };
1883
1884 /**
1885 * Construct a darray<T> instance, initialized by move semantics from the variadic (template pack) argument list.
1886 *
1887 * std::initializer_list<T> enforces to copy the created instances into the container,
1888 * since its iterator references to `const` value_type.
1889 *
1890 * This alternative template passes the r-value argument references to darray::push_back_list(),
1891 * hence using `std::move` without copying semantics.
1892 *
1893 * All argument types must be of same type, i.e. std::is_same.
1894 * The deduced darray<T> instance also uses same type as its Value_type.
1895 *
1896 * @tparam First the first argument type, must be same
1897 * @tparam Next all other argument types, must be same
1898 * @tparam
1899 * @param arg1 the first r-value
1900 * @param argsN the other r-values
1901 * @return the new `darray`
1902 * @see darray::push_back_list()
1903 * @see make_darray()
1904 */
1905 template <typename First, typename... Next,
1906 // std::enable_if_t< ( std::is_same<First, Next>::value && ... ), bool> = true>
1907 std::enable_if_t< std::conjunction_v<std::is_same<First, Next>... >, bool> = true>
1908 constexpr darray< First > make_darray(First&& arg1, Next&&... argsN)
1909 {
1910 darray< First > d(1 + sizeof...(Next));
1911 // C++17 fold expression on above C++11 template pack arg1 and argsN
1912 // d.push_back_list( std::forward<First>(arg1), ( std::forward<Next>(argsN), ... ) ); // @suppress("Syntax error")
1913 d.push_back_list( arg1, argsN... ); // @suppress("Syntax error")
1914 return d;
1915 }
1916
1917 /**
1918 * Complement constructor for darray<T> instance, move semantics initializer for one argument.
1919 * @tparam First
1920 * @tparam Next
1921 * @param arg1
1922 * @return
1923 * @see darray::push_back()
1924 * @see darray::push_back_list()
1925 * @see make_darray()
1926 */
1927 template <typename First, typename... Next>
1928 constexpr darray< First > make_darray(First&& arg1)
1929 {
1930 darray< First > d(1);
1931 d.push_back( std::forward<First>(arg1) );
1932 return d;
1933 }
1934
1935 /****************************************************************************************
1936 ****************************************************************************************/
1937
1938 template<typename Value_type, typename Size_type, typename Alloc_type>
1939 std::ostream & operator << (std::ostream &out, const darray<Value_type, Size_type, Alloc_type> &c) {
1940 out << c.toString();
1941 return out;
1942 }
1943
1944 /****************************************************************************************
1945 ****************************************************************************************/
1946
1947 template<typename Value_type, typename Size_type, typename Alloc_type>
1949 if( &rhs == &lhs ) {
1950 return true;
1951 }
1952 return (rhs.size() == lhs.size() && std::equal(rhs.cbegin(), rhs.cend(), lhs.cbegin()));
1953 }
1954 template<typename Value_type, typename Size_type, typename Alloc_type>
1956 return !(rhs==lhs);
1957 }
1958
1959 template<typename Value_type, typename Size_type, typename Alloc_type>
1961 { return std::lexicographical_compare(rhs.cbegin(), rhs.cend(), lhs.cbegin(), lhs.cend()); }
1962
1963 template<typename Value_type, typename Size_type, typename Alloc_type>
1965 { return lhs < rhs; }
1966
1967 template<typename Value_type, typename Size_type, typename Alloc_type>
1969 { return !(lhs < rhs); }
1970
1971 template<typename Value_type, typename Size_type, typename Alloc_type>
1973 { return !(rhs < lhs); }
1974
1975 template<typename Value_type, typename Size_type, typename Alloc_type>
1977 { rhs.swap(lhs); }
1978
1979 /****************************************************************************************
1980 ****************************************************************************************/
1981
1982 /**
1983 * `template< class T > is_darray_type<T>::value` compile-time Type Trait,
1984 * determining whether the given template class is a - or has a darray type, e.g. jau::cow_darray,
1985 * jau::darray.
1986 */
1987 template< class, class = void >
1988 struct is_darray_type : std::false_type { };
1989
1990 /**
1991 * `template< class T > is_darray_type<T>::value` compile-time Type Trait,
1992 * determining whether the given template class is a - or has a darray type, e.g. jau::cow_darray,
1993 * jau::darray.
1994 */
1995 template< class T >
1996 struct is_darray_type<T, std::void_t<typename T::darray_tag>> : std::true_type { };
1997
1998 /**@}*/
1999
2000} /* namespace jau */
2001
2002#endif /* JAU_DYN_ARRAY_HPP_ */
#define E_FILE_LINE
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:886
constexpr void push_front(value_type &&x)
Like std::vector::push_front(), move.
Definition darray.hpp:1637
darray< value_type, size_type, allocator_type, use_memmove, use_secmem > self_t
Definition darray.hpp:180
static constexpr size_type max_size() noexcept
Returns std::numeric_limits<difference_type>::max() as the maximum array size.
Definition darray.hpp:825
constexpr_cxx20 self_t & put(const_reference v, Bool grow=Bool::False)
Relative put() for single value, increasing position().
Definition darray.hpp:930
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:1079
constexpr iterator insert(const_iterator pos, InputIt first, InputIt last)
Like std::vector::insert(), inserting the value_type range [first, last).
Definition darray.hpp:1539
constexpr void push_back(InputIt first, InputIt last)
Like std::vector::push_back(), but appends the value_type range [first, last).
Definition darray.hpp:1672
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:1830
constexpr void swap(darray &x) noexcept
Like std::vector::swap().
Definition darray.hpp:1330
constexpr void assign(InputIt first, InputIt last)
Like std::vector::assign()
Definition darray.hpp:1245
constexpr darray & operator=(const darray &x)
Like std::vector::operator=(&), assignment.
Definition darray.hpp:653
bool(* equal_comparator)(const value_type &a, const value_type &b)
Definition darray.hpp:1743
constexpr_cxx20 const_reference at(size_type i) const
Like std::vector::at(size_type), immutable reference.
Definition darray.hpp:1166
constexpr void assign(const_iterator first, const_iterator last)
Like std::vector::assign(), but non-template overload using const_iterator.
Definition darray.hpp:1266
constexpr_cxx20 const_reference operator[](size_type i) const noexcept
Like std::vector::operator[](size_type), immutable reference.
Definition darray.hpp:1152
constexpr iterator erase(const_iterator cpos)
Like std::vector::erase(), removes the elements at pos.
Definition darray.hpp:1364
constexpr reference front()
Like std::vector::front(), mutable access.
Definition darray.hpp:1122
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:804
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:959
constexpr const_iterator begin() const noexcept
Definition darray.hpp:830
constexpr_cxx20 const_reference get()
Relative get() for single value reference, increasing position() by one.
Definition darray.hpp:912
constexpr iterator end() noexcept
Definition darray.hpp:833
constexpr_cxx20 reference at(size_type i)
Like std::vector::at(size_type), mutable reference.
Definition darray.hpp:1176
~darray() noexcept
Definition darray.hpp:813
constexpr const_iterator cend() const noexcept
Definition darray.hpp:835
constexpr size_type size() const noexcept
Like std::vector::size().
Definition darray.hpp:1115
constexpr void push_back(value_type &&x)
Like std::vector::push_back(), move.
Definition darray.hpp:1584
constexpr const_iterator end() const noexcept
Definition darray.hpp:834
constexpr void push_back(const value_type &x)
Like std::vector::push_back(), copy.
Definition darray.hpp:1568
constexpr darray(darray &&x) noexcept
Definition darray.hpp:679
constexpr self_t & resize(size_type new_size)
Like std::vector::resize(size_type)
Definition darray.hpp:1223
constexpr reference emplace_back(Args &&... args)
Like std::vector::emplace_back(), construct a new element in place at the end().
Definition darray.hpp:1651
constexpr self_t & clear() noexcept
Like std::vector::clear(), calls destructor on all elements and leaving capacity unchanged.
Definition darray.hpp:1293
constexpr darray(const darray &x)
Creates a new instance, copying all elements from the given darray.
Definition darray.hpp:602
constexpr iterator erase(const_iterator cfirst, const_iterator clast)
Like std::vector::erase(), removes the elements in the range [first, last).
Definition darray.hpp:1385
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:1419
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:1801
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:783
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:1460
std::string toString() const noexcept
Definition darray.hpp:1844
constexpr bool empty() const noexcept
Like std::vector::empty().
Definition darray.hpp:1104
constexpr const_pointer data() const noexcept
Like std::vector::data(), const immutable pointer.
Definition darray.hpp:1142
constexpr darray & operator=(darray &&x) noexcept
Like std::vector::operator=(&&), move.
Definition darray.hpp:704
constexpr const_iterator cbegin() const noexcept
Definition darray.hpp:831
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:1110
self_t & setLimit(size_type v)
Sets new limit and adjusts position if new limit is below.
Definition darray.hpp:868
constexpr pointer data() noexcept
Like std::vector::data(), mutable pointer.
Definition darray.hpp:1147
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:866
constexpr darray() noexcept
Default constructor, giving zero capacity and zero memory footprint.
Definition darray.hpp:553
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:567
constexpr darray(darray &parent, pointer begin, pointer position, pointer limit, size_type size)
Slicing ctor.
Definition darray.hpp:534
constexpr_cxx20 reference operator[](size_type i) noexcept
Like std::vector::operator[](size_type), mutable reference.
Definition darray.hpp:1159
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:583
self_t & setPosition(size_type v)
Sets position.
Definition darray.hpp:854
constexpr darray(darray &&x, const float growth_factor, const allocator_type &alloc) noexcept
Definition darray.hpp:690
constexpr iterator insert(const_iterator pos, const value_type &x)
Like std::vector::insert(), copy.
Definition darray.hpp:1434
constexpr void push_back_list(const Args &... args)
Like push_back(), but for more multiple const r-value to copy.
Definition darray.hpp:1693
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:1005
constexpr void push_back_list(Args &&... args)
Like push_back(), but for more multiple r-value references to move.
Definition darray.hpp:1720
constexpr pointer position_ptr() noexcept
Pointer to mutable next relative read/write element index, with 0 <= position <= limit.
Definition darray.hpp:850
constexpr self_t & clearPosition() noexcept
Clears the relative position and limit w/o destructing elements nor mutating storage.
Definition darray.hpp:901
allocator_type get_allocator() const noexcept
Definition darray.hpp:1083
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:1408
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:1767
constexpr bool hasRemaining() const noexcept
Returns whether position < limit, i.e.
Definition darray.hpp:1073
self_t slice()
Returns a sliced duplicate starting from this buffers' current position.
Definition darray.hpp:1037
constexpr self_t & reserve(size_type new_capacity)
Like std::vector::reserve(), increases this instance's capacity to new_capacity.
Definition darray.hpp:1192
constexpr iterator insert(const_iterator pos, value_type &&x)
Like std::vector::insert(), move.
Definition darray.hpp:1475
constexpr const_reference front() const
Like std::vector::front(), immutable access.
Definition darray.hpp:1127
constexpr self_t & shrink_to_fit()
Like std::vector::shrink_to_fit(), but ensured constexpr.
Definition darray.hpp:1230
self_t slice(size_type idx, size_type length)
Returns a sliced duplicate starting from the given idx.
Definition darray.hpp:1050
constexpr iterator emplace(const_iterator pos, Args &&... args)
Like std::vector::emplace(), construct a new element in place.
Definition darray.hpp:1507
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:740
constexpr self_t & resize(size_type new_size, const value_type &val)
Like std::vector::resize(size_type, const value_type&)
Definition darray.hpp:1202
constexpr const_pointer position_ptr() const noexcept
Pointer to immutable next relative read/write element index, with 0 <= position <= limit.
Definition darray.hpp:852
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:1600
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:1312
constexpr reference back()
Like std::vector::back(), mutable access.
Definition darray.hpp:1132
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:764
constexpr void pop_back() noexcept
Like std::vector::pop_back().
Definition darray.hpp:1349
constexpr const_reference back() const
Like std::vector::back(), immutable access.
Definition darray.hpp:1137
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:1095
constexpr size_type remaining() const noexcept
Returns limit - position.
Definition darray.hpp:1071
constexpr void push_front(const value_type &x)
Like std::vector::push_front(), copy.
Definition darray.hpp:1626
self_t duplicate()
Returns a duplicate with same position and limit.
Definition darray.hpp:1065
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:641
constexpr self_t & flip() noexcept
Sets limit to position and position to zero.
Definition darray.hpp:879
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:621
std::string getInfo() const noexcept
Definition darray.hpp:1855
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::ostream & operator<<(std::ostream &out, const bitfield_t< StorageType, BitSize > &v)
Definition bitfield.hpp:486
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
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:1908
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
std::string to_string(const bit_order_t v) noexcept
Return std::string representation of the given bit_order_t.
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:1988
uint8_t Value_type