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