Direct-BT  2.3.1
Direct-BT - Direct Bluetooth Programming.
cow_darray.hpp
Go to the documentation of this file.
1 /*
2  * Author: Sven Gothel <sgothel@jausoft.com>
3  * Copyright (c) 2020 Gothel Software e.K.
4  *
5  * Permission is hereby granted, free of charge, to any person obtaining
6  * a copy of this software and associated documentation files (the
7  * "Software"), to deal in the Software without restriction, including
8  * without limitation the rights to use, copy, modify, merge, publish,
9  * distribute, sublicense, and/or sell copies of the Software, and to
10  * permit persons to whom the Software is furnished to do so, subject to
11  * the following conditions:
12  *
13  * The above copyright notice and this permission notice shall be
14  * included in all copies or substantial portions of the Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20  * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21  * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22  * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23  */
24 
25 #ifndef JAU_COW_DARRAY_HPP_
26 #define JAU_COW_DARRAY_HPP_
27 
28 #include <cstring>
29 #include <string>
30 #include <cstdint>
31 #include <limits>
32 #include <atomic>
33 #include <memory>
34 #include <mutex>
35 #include <condition_variable>
36 #include <algorithm>
37 
38 #include <jau/cpp_lang_util.hpp>
39 #include <jau/debug.hpp>
40 #include <jau/darray.hpp>
41 #include <jau/basic_types.hpp>
42 #include <jau/ordered_atomic.hpp>
43 #include <jau/cow_iterator.hpp>
44 #include <jau/callocator.hpp>
45 
46 namespace jau {
47 
48  /**
49  * Implementation of a Copy-On-Write (CoW) using jau::darray as the underlying storage,
50  * exposing <i>lock-free</i> read operations using SC-DRF atomic synchronization.
51  * <p>
52  * This class shall be compliant with <i>C++ named requirements for Container</i>.
53  * </p>
54  * <p>
55  * The store is owned using a shared reference to the data structure,
56  * allowing its replacement on Copy-On-Write (CoW).
57  * </p>
58  * <p>
59  * Writing to the store utilizes a mutex lock to avoid data races
60  * on the instances' write operations only, leaving read operations <i>lock-free</i>.<br>
61  * Write operations replace the store reference with a new instance using
62  * jau::sc_atomic_critical to synchronize with read operations.
63  * </p>
64  * <p>
65  * Reading from the store is <i>lock-free</i> and accesses the store reference using
66  * jau::sc_atomic_critical to synchronizing with write operations.
67  * </p>
68  * <p>
69  * Immutable storage const_iterators are supported via jau::cow_ro_iterator,
70  * which are constructed <i>lock-free</i>.<br>
71  * jau::cow_ro_iterator holds a snapshot retrieved via jau::cow_darray::snapshot()
72  * until its destruction.
73  * </p>
74  * <p>
75  * Mutable storage iterators are supported via jau::cow_rw_iterator,
76  * which holds a copy of this CoW storage and locks its write mutex until
77  * jau::cow_rw_iterator::write_back() or its destruction.<br>
78  * After completing all mutable operations but before this iterator's destruction,
79  * the user might want to write back this iterators' storage to this CoW
80  * using jau::cow_rw_iterator::write_back().
81  * </p>
82  * <p>
83  * Both, jau::cow_ro_iterator and jau::cow_rw_iterator are harmonized
84  * to work with jau::darray::const_iterator and jau::darray::iterator
85  * for all iterator based operations.
86  * </p>
87  * <p>
88  * Index operation via ::operator[](size_t) or ::at(size_t) are not supported,
89  * since they would be only valid if value_type itself is a std::shared_ptr
90  * and hence prohibit the destruction of the object if mutating the storage,
91  * e.g. via jau::cow_darray::push_back().
92  * </p>
93  * <p>
94  * Custom mutable write operations are also supported via
95  * jau::cow_darray::get_write_mutex(), jau::cow_darray::copy_store() and jau::cow_darray::set_store().<br>
96  * See example in jau::cow_darray::set_store()
97  * </p>
98  * <p>
99  * To allow data-race free operations using iterators from a potentially mutated CoW,
100  * only one cow_darray::begin() const_iterator or iterator should be retrieved from this CoW
101  * and all further operations shall use its
102  * jau::cow_ro_iterator::size(), jau::cow_ro_iterator::begin() and jau::cow_ro_iterator::end()
103  * - or its respective variant from jau::cow_rw_iterator.
104  * </p>
105  * <p>
106  * Non-Type Template Parameter <code>use_memmove</code> can be overriden by the user
107  * and has its default value <code>std::is_trivially_copyable_v<Value_type></code>.<br>
108  * The default value has been chosen with care, see C++ Standard section 6.9 Types <i>trivially copyable</i>.<br>
109  * However, one can set <code>use_memmove</code> to true even without the value_type being <i>trivially copyable</i>,
110  * as long certain memory side-effects can be excluded (TBD).
111  * </p>
112  * See also:
113  * <pre>
114  * - Sequentially Consistent (SC) ordering or SC-DRF (data race free) <https://en.cppreference.com/w/cpp/atomic/memory_order#Sequentially-consistent_ordering>
115  * - std::memory_order <https://en.cppreference.com/w/cpp/atomic/memory_order>
116  * </pre>
117  *
118  * @see jau::darray
119  * @see jau::cow_ro_iterator
120  * @see jau::for_each_fidelity
121  * @see jau::cow_rw_iterator
122  * @see jau::cow_rw_iterator::write_back()
123  */
124  template <typename Value_type, typename Alloc_type = jau::callocator<Value_type>, typename Size_type = jau::nsize_t,
125  bool use_memmove = std::is_trivially_copyable_v<Value_type>,
126  bool use_realloc = std::is_base_of_v<jau::callocator<Value_type>, Alloc_type>,
127  bool sec_mem = false
128  >
130  {
131  public:
132  /** Default growth factor using the golden ratio 1.618 */
133  constexpr static const float DEFAULT_GROWTH_FACTOR = 1.618f;
134 
135  constexpr static const bool uses_memmove = use_memmove;
136  constexpr static const bool uses_realloc = use_realloc;
137  constexpr static const bool uses_secmem = sec_mem;
138 
139  // typedefs' for C++ named requirements: Container
140 
141  typedef Value_type value_type;
142  typedef value_type* pointer;
143  typedef const value_type* const_pointer;
145  typedef const value_type& const_reference;
146  typedef Size_type size_type;
147  typedef typename std::make_signed<size_type>::type difference_type;
148  typedef Alloc_type allocator_type;
149 
151  size_type,
152  use_memmove, use_realloc, sec_mem> storage_t;
153  typedef std::shared_ptr<storage_t> storage_ref_t;
154 
155  /** Used to determine whether this type is a darray or has a darray, see ::is_darray_type<T> */
156  typedef bool darray_tag;
157 
159  size_type, use_memmove,
160  use_realloc, sec_mem> cow_container_t;
161 
162  /**
163  * Immutable, read-only const_iterator, lock-free,
164  * holding the current shared store reference until destruction.
165  * <p>
166  * Using jau::cow_darray::snapshot() at construction.
167  * </p>
168  * <p>
169  * This iterator is the preferred choice if no mutations are made to the elements state
170  * itself, or all changes can be discarded after the iterator's destruction.<br>
171  * This avoids the costly mutex lock and storage copy of jau::cow_rw_iterator.<br>
172  * Also see jau::for_each_fidelity to iterate through in this good faith fashion.
173  * </p>
174  * @see jau::cow_ro_iterator
175  * @see jau::cow_ro_iterator::size()
176  * @see jau::cow_ro_iterator::begin()
177  * @see jau::cow_ro_iterator::end()
178  * @see jau::for_each_fidelity
179  * @see jau::cow_rw_iterator
180  */
182 
183  /**
184  * Mutable, read-write iterator, holding the write-lock and a store copy until destruction.
185  * <p>
186  * Using jau::cow_darray::get_write_mutex(), jau::cow_darray::copy_store() at construction<br>
187  * and jau::cow_darray::set_store() at destruction.
188  * </p>
189  * <p>
190  * Due to the costly nature of mutable CoW resource management,
191  * consider using jau::cow_ro_iterator if elements won't get mutated
192  * or any changes can be discarded.
193  * </p>
194  * @see jau::cow_rw_iterator
195  * @see jau::cow_rw_iterator::size()
196  * @see jau::cow_rw_iterator::begin()
197  * @see jau::cow_rw_iterator::end()
198  * @see jau::cow_ro_iterator
199  */
201 
202  // typedef std::reverse_iterator<iterator> reverse_iterator;
203  // typedef std::reverse_iterator<const_iterator> const_reverse_iterator;
204 
205  private:
206  static constexpr size_type DIFF_MAX = std::numeric_limits<difference_type>::max();
207 
208  storage_ref_t store_ref;
209  mutable sc_atomic_bool sync_atomic;
210  mutable std::recursive_mutex mtx_write;
211 
212  public:
213  // ctor w/o elements
214 
215  /**
216  * Default constructor, giving almost zero capacity and zero memory footprint, but the shared empty jau::darray
217  */
218  constexpr cow_darray() noexcept
219  : store_ref(std::make_shared<storage_t>()), sync_atomic(false) {
220  DARRAY_PRINTF("ctor def: %s\n", get_info().c_str());
221  }
222 
223  /**
224  * Creating an empty instance with initial capacity and other (default) properties.
225  * @param capacity initial capacity of the new instance.
226  * @param growth_factor given growth factor
227  * @param alloc given allocator_type
228  */
230  : store_ref(std::make_shared<storage_t>(capacity, growth_factor, alloc)), sync_atomic(false) {
231  DARRAY_PRINTF("ctor 1: %s\n", get_info().c_str());
232  }
233 
234  // conversion ctor on storage_t elements
235 
236  constexpr cow_darray(const storage_t& x)
237  : store_ref(std::make_shared<storage_t>(x)), sync_atomic(false) {
238  DARRAY_PRINTF("ctor copy_0: this %s\n", get_info().c_str());
239  DARRAY_PRINTF("ctor copy_0: x %s\n", x.get_info().c_str());
240  }
241 
242  constexpr explicit cow_darray(const storage_t& x, const float growth_factor, const allocator_type& alloc)
243  : store_ref(std::make_shared<storage_t>(x, growth_factor, alloc)), sync_atomic(false) {
244  DARRAY_PRINTF("ctor copy_1: this %s\n", get_info().c_str());
245  DARRAY_PRINTF("ctor copy_1: x %s\n", x.get_info().c_str());
246  }
247 
248  /**
249  * Like std::vector::operator=(&), assignment, but copying from the underling jau::darray
250  * <p>
251  * This write operation uses a mutex lock and is blocking this instances' write operations only.
252  * </p>
253  */
255  std::lock_guard<std::recursive_mutex> lock(mtx_write);
256  DARRAY_PRINTF("assignment copy_0: this %s\n", get_info().c_str());
257  DARRAY_PRINTF("assignment copy_0: x %s\n", x.get_info().c_str());
258  {
259  sc_atomic_critical sync(sync_atomic);
260  store_ref = std::move( std::make_shared<storage_t>( x ) );
261  }
262  return *this;
263  }
264 
265  constexpr cow_darray(storage_t && x) noexcept
266  : store_ref(std::make_shared<storage_t>(std::move(x))), sync_atomic(false) {
267  DARRAY_PRINTF("ctor move_0: this %s\n", get_info().c_str());
268  DARRAY_PRINTF("ctor move_0: x %s\n", x.get_info().c_str());
269  // Moved source array has been taken over. darray's move-operator has flushed source
270  }
271 
272  constexpr explicit cow_darray(storage_t && x, const float growth_factor, const allocator_type& alloc) noexcept
273  : store_ref(std::make_shared<storage_t>(std::move(x), growth_factor, alloc)), sync_atomic(false) {
274  DARRAY_PRINTF("ctor move_1: this %s\n", get_info().c_str());
275  DARRAY_PRINTF("ctor move_1: x %s\n", x.get_info().c_str());
276  // Moved source array has been taken over. darray's move-operator has flushed source
277  }
278 
279  /**
280  * Like std::vector::operator=(&&), move, but taking the underling jau::darray
281  * <p>
282  * This write operation uses a mutex lock and is blocking this instances' write operations only.
283  * </p>
284  */
286  std::lock_guard<std::recursive_mutex> lock(mtx_write);
287  DARRAY_PRINTF("assignment move_0: this %s\n", get_info().c_str());
288  DARRAY_PRINTF("assignment move_0: x %s\n", x.get_info().c_str());
289  {
290  sc_atomic_critical sync(sync_atomic);
291  store_ref = std::move( std::make_shared<storage_t>( std::move(x) ) );
292  // Moved source array has been taken over. darray's move-operator has flushed source
293  }
294  return *this;
295  }
296 
297  // copy_ctor on cow_darray elements
298 
299  /**
300  * Creates a new instance, copying all elements from the given array.<br>
301  * Capacity and size will equal the given array, i.e. the result is a trimmed array.
302  * @param x the given cow_darray, all elements will be copied into the new instance.
303  */
306  : sync_atomic(false) {
307  storage_ref_t x_store_ref;
308  {
309  sc_atomic_critical sync_x( x.sync_atomic );
310  DARRAY_PRINTF("ctor copy.0: this %s\n", get_info().c_str());
311  DARRAY_PRINTF("ctor copy.0: x %s\n", x.get_info().c_str());
312  x_store_ref = x.store_ref;
313  }
314  store_ref = std::make_shared<storage_t>( *x_store_ref );
315  }
316 
317  /**
318  * Creates a new instance, copying all elements from the given array.<br>
319  * Capacity and size will equal the given array, i.e. the result is a trimmed array.
320  * @param x the given cow_darray, all elements will be copied into the new instance.
321  * @param growth_factor custom growth factor
322  * @param alloc custom allocator_type instance
323  */
325  explicit cow_darray(const cow_darray& x, const float growth_factor, const allocator_type& alloc)
326  : sync_atomic(false) {
327  storage_ref_t x_store_ref;
328  {
329  sc_atomic_critical sync_x( x.sync_atomic );
330  DARRAY_PRINTF("ctor copy.1: this %s\n", get_info().c_str());
331  DARRAY_PRINTF("ctor copy.1: x %s\n", x.get_info().c_str());
332  x_store_ref = x.store_ref;
333  }
334  store_ref = std::make_shared<storage_t>( *x_store_ref, growth_factor, alloc );
335  }
336 
337  /**
338  * Creates a new instance with custom initial storage capacity, copying all elements from the given array.<br>
339  * Size will equal the given array.
340  * <p>
341  * Throws jau::IllegalArgumentException() if <code>_capacity < x.size()</code>.
342  * </p>
343  * @param x the given cow_darray, all elements will be copied into the new instance.
344  * @param _capacity custom initial storage capacity
345  * @param growth_factor custom growth factor
346  * @param alloc custom allocator_type instance
347  */
349  explicit cow_darray(const cow_darray& x, const size_type _capacity, const float growth_factor, const allocator_type& alloc)
350  : sync_atomic(false) {
351  storage_ref_t x_store_ref;
352  {
353  sc_atomic_critical sync_x( x.sync_atomic );
354  DARRAY_PRINTF("ctor copy.2: this %s\n", get_info().c_str());
355  DARRAY_PRINTF("ctor copy.2: x %s\n", x.get_info().c_str());
356  x_store_ref = x.store_ref;
357  }
358  store_ref = std::make_shared<storage_t>( *x_store_ref, _capacity, growth_factor, alloc );
359  }
360 
361  /**
362  * Like std::vector::operator=(&), assignment
363  * <p>
364  * This write operation uses a mutex lock and is blocking this instances' write operations only.
365  * </p>
366  */
369  std::lock_guard<std::recursive_mutex> lock(mtx_write);
370  storage_ref_t x_store_ref;
371  {
372  sc_atomic_critical sync_x( x.sync_atomic );
373  DARRAY_PRINTF("assignment copy.0: this %s\n", get_info().c_str());
374  DARRAY_PRINTF("assignment copy.0: x %s\n", x.get_info().c_str());
375  x_store_ref = x.store_ref;
376  }
377  storage_ref_t new_store_ref = std::make_shared<storage_t>( *x_store_ref );
378  {
379  sc_atomic_critical sync(sync_atomic);
380  store_ref = std::move(new_store_ref);
381  }
382  return *this;
383  }
384 
385  // move_ctor on cow_darray elements
386 
388  cow_darray(cow_darray && x) noexcept {
389  // Strategy-1: Acquire lock, blocking
390  // - If somebody else holds the lock, we wait.
391  // - Then we own the lock
392  // - Post move-op, the source object does not exist anymore
393  std::unique_lock<std::recursive_mutex> lock(x.mtx_write); // *this doesn't exist yet, not locking ourselves
394  {
395  DARRAY_PRINTF("ctor move.0: this %s\n", get_info().c_str());
396  DARRAY_PRINTF("ctor move.0: x %s\n", x.get_info().c_str());
397  store_ref = std::move(x.store_ref);
398  // sync_atomic = std::move(x.sync_atomic); // issues w/ g++ 8.3 (move marked as deleted)
399  // mtx_write will be a fresh one, but we hold the source's lock
400 
401  // Moved source array has been taken over, null its store_ref
402  x.store_ref = nullptr;
403  }
404  }
405 
406  /**
407  * Like std::vector::operator=(&&), move.
408  * <p>
409  * This write operation uses a mutex lock and is blocking both cow_vector instance's write operations.
410  * </p>
411  */
413  cow_darray& operator=(cow_darray&& x) noexcept {
414  // Strategy-2: Acquire locks of both, blocking
415  // - If somebody else holds the lock, we wait.
416  // - Then we own the lock for both instances
417  // - Post move-op, the source object does not exist anymore
418  std::unique_lock<std::recursive_mutex> lock1(x.mtx_write, std::defer_lock); // utilize std::lock(r, w), allowing mixed order waiting on read/write ops
419  std::unique_lock<std::recursive_mutex> lock2( mtx_write, std::defer_lock); // otherwise RAII-style relinquish via destructor
420  std::lock(lock1, lock2);
421  {
422  sc_atomic_critical sync_x( x.sync_atomic );
423  sc_atomic_critical sync ( sync_atomic );
424  DARRAY_PRINTF("assignment move.0: this %s\n", get_info().c_str());
425  DARRAY_PRINTF("assignment move.0: x %s\n", x.get_info().c_str());
426  store_ref = std::move(x.store_ref);
427  // mtx_write and the atomic will be kept as is, but we hold the source's lock
428 
429  // Moved source array has been taken over, null its store_ref
430  x.store_ref = nullptr;
431  }
432  return *this;
433  }
434 
435  // ctor on const_iterator and foreign template iterator
436 
437  /**
438  * Creates a new instance with custom initial storage capacity,
439  * copying all elements from the given const_iterator value_type range [first, last).<br>
440  * Size will equal the range [first, last), i.e. <code>size_type(last-first)</code>.
441  * <p>
442  * Throws jau::IllegalArgumentException() if <code>_capacity < size_type(last - first)</code>.
443  * </p>
444  * @param _capacity custom initial storage capacity
445  * @param first const_iterator to first element of value_type range [first, last)
446  * @param last const_iterator to last element of value_type range [first, last)
447  * @param growth_factor custom growth factor
448  * @param alloc custom allocator_type instance
449  */
450  constexpr cow_darray(const size_type _capacity, const_iterator first, const_iterator last,
452  : store_ref(std::make_shared<storage_t>(_capacity, first.underling(), last.underling(), growth_factor, alloc)), sync_atomic(false)
453  {
454  DARRAY_PRINTF("ctor iters0: %s\n", get_info().c_str());
455  }
456 
457  /**
458  * Creates a new instance with custom initial storage capacity,
459  * copying all elements from the given template input-iterator value_type range [first, last).<br>
460  * Size will equal the range [first, last), i.e. <code>size_type(last-first)</code>.
461  * <p>
462  * Throws jau::IllegalArgumentException() if <code>_capacity < size_type(last - first)</code>.
463  * </p>
464  * @tparam InputIt template input-iterator custom type
465  * @param _capacity custom initial storage capacity
466  * @param first template input-iterator to first element of value_type range [first, last)
467  * @param last template input-iterator to last element of value_type range [first, last)
468  * @param growth_factor custom growth factor
469  * @param alloc custom allocator_type instance
470  */
471  template< class InputIt >
472  constexpr explicit cow_darray(const size_type _capacity, InputIt first, InputIt last,
474  : store_ref(std::make_shared<storage_t>(_capacity, first, last, growth_factor, alloc)), sync_atomic(false)
475  {
476  DARRAY_PRINTF("ctor iters1: %s\n", get_info().c_str());
477  }
478 
479  /**
480  * Creates a new instance,
481  * copying all elements from the given template input-iterator value_type range [first, last).<br>
482  * Size will equal the range [first, last), i.e. <code>size_type(last-first)</code>.
483  * @tparam InputIt template input-iterator custom type
484  * @param first template input-iterator to first element of value_type range [first, last)
485  * @param last template input-iterator to last element of value_type range [first, last)
486  * @param alloc custom allocator_type instance
487  */
488  template< class InputIt >
489  constexpr cow_darray(InputIt first, InputIt last, const allocator_type& alloc = allocator_type())
490  : store_ref(std::make_shared<storage_t>(first, last, alloc)), sync_atomic(false)
491  {
492  DARRAY_PRINTF("ctor iters2: %s\n", get_info().c_str());
493  }
494 
495  /**
496  * Create a new instance from an initializer list.
497  *
498  * @param initlist initializer_list.
499  * @param alloc allocator
500  */
501  constexpr cow_darray(std::initializer_list<value_type> initlist, const allocator_type& alloc = allocator_type())
502  : store_ref(std::make_shared<storage_t>(initlist, alloc)), sync_atomic(false)
503  {
504  DARRAY_PRINTF("ctor initlist: %s\n", get_info().c_str());
505  }
506 
507 
508  ~cow_darray() noexcept {
509  DARRAY_PRINTF("dtor: %s\n", get_info().c_str());
510  }
511 
512  /**
513  * Returns <code>std::numeric_limits<difference_type>::max()</code> as the maximum array size.
514  * <p>
515  * We rely on the signed <code>difference_type</code> for pointer arithmetic,
516  * deducing ranges from iterator.
517  * </p>
518  */
519  constexpr size_type max_size() const noexcept { return DIFF_MAX; }
520 
521  // cow_vector features
522 
523  /**
524  * Returns this instances' recursive write mutex, allowing user to
525  * implement more complex mutable write operations.
526  * <p>
527  * See example in jau::cow_darray::set_store()
528  * </p>
529  *
530  * @see jau::cow_darray::get_write_mutex()
531  * @see jau::cow_darray::copy_store()
532  * @see jau::cow_darray::set_store()
533  */
534  constexpr std::recursive_mutex & get_write_mutex() noexcept { return mtx_write; }
535 
536  /**
537  * Returns a new shared_ptr copy of the underlying store,
538  * i.e. using a new copy-constructed vectore.
539  * <p>
540  * See example in jau::cow_darray::set_store()
541  * </p>
542  * <p>
543  * This special operation uses a mutex lock and is blocking this instances' write operations only.
544  * </p>
545  * @see jau::cow_darray::get_write_mutex()
546  * @see jau::cow_darray::copy_store()
547  * @see jau::cow_darray::set_store()
548  */
551  std::lock_guard<std::recursive_mutex> lock(mtx_write);
552  DARRAY_PRINTF("copy_store: %s\n", get_info().c_str());
553  return std::make_shared<storage_t>( *store_ref );
554  }
555 
556  /**
557  * Replace the current store with the given instance,
558  * potentially acquired via jau::cow_darray::copy_store()
559  * and mutated while holding the jau::cow_darray::get_write_mutex() lock.
560  * <p>
561  * This is a move operation, i.e. the given new_store_ref is invalid on the caller side
562  * after this operation. <br>
563  * User shall pass the store via std::move()
564  * <pre>
565  * cow_darray<std::shared_ptr<Thing>> list;
566  * ...
567  * {
568  * std::lock_guard<std::recursive_mutex> lock(list.get_write_mutex());
569  * std::shared_ptr<std::vector<std::shared_ptr<Thing>>> snapshot = list.copy_store();
570  * ...
571  * some fancy mutation
572  * ...
573  * list.set_store(std::move(snapshot));
574  * }
575  * </pre>
576  * Above functionality is covered by jau::cow_rw_iterator, see also jau::cow_rw_iterator::write_back()
577  * </p>
578  * @param new_store_ref the user store to be moved here, replacing the current store.
579  *
580  * @see jau::cow_darray::get_write_mutex()
581  * @see jau::cow_darray::copy_store()
582  * @see jau::cow_darray::set_store()
583  * @see jau::cow_rw_iterator
584  * @see jau::cow_rw_iterator::write_back()
585  */
587  void set_store(storage_ref_t && new_store_ref) noexcept {
588  std::lock_guard<std::recursive_mutex> lock(mtx_write);
589  sc_atomic_critical sync(sync_atomic);
590 #if DEBUG_DARRAY
591  DARRAY_PRINTF("set_store: dest %s\n", get_info().c_str());
592  DARRAY_PRINTF("set_store: src %s\n", new_store_ref->get_info().c_str());
593  jau::print_backtrace(true, 8);
594 #endif
595  store_ref = std::move( new_store_ref );
596  }
597 
598  /**
599  * Returns the current snapshot of the underlying shared storage by reference.
600  * <p>
601  * Note that this snapshot will be outdated by the next (concurrent) write operation.<br>
602  * The returned referenced vector is still valid and not mutated,
603  * but does not represent the current content of this cow_darray instance.
604  * </p>
605  * <p>
606  * This read operation is <i>lock-free</i>.
607  * </p>
608  */
610  storage_ref_t snapshot() const noexcept {
611  sc_atomic_critical sync( sync_atomic );
612  return store_ref;
613  }
614 
615  // const_iterator, non mutable, read-only
616 
617  // Removed for clarity: "constexpr const_iterator begin() const noexcept"
618 
619  /**
620  * Returns an jau::cow_ro_iterator to the first element of this CoW storage.
621  * <p>
622  * This method is the preferred choice if the use case allows,
623  * read remarks in jau::cow_ro_iterator.
624  * </p>
625  * <p>
626  * Use jau::cow_ro_iterator::end() on this returned const_iterator
627  * to retrieve the end const_iterator in a data-race free fashion.
628  * </p>
629  * @return jau::cow_darray::const_iterator of type jau::cow_ro_iterator
630  * @see jau::cow_ro_iterator
631  * @see jau::cow_ro_iterator::size()
632  * @see jau::cow_ro_iterator::begin()
633  * @see jau::cow_ro_iterator::end()
634  * @see jau::for_each_fidelity
635  */
636  constexpr const_iterator cbegin() const noexcept {
637  storage_ref_t sr = snapshot();
638  return const_iterator(sr, sr->cbegin());
639  }
640 
641  // iterator, mutable, read-write
642 
643  /**
644  * Returns an jau::cow_rw_iterator to the first element of this CoW storage.
645  * <p>
646  * Acquiring this mutable iterator has considerable costs attached,
647  * read remarks in jau::cow_rw_iterator.
648  * </p>
649  * <p>
650  * Use jau::cow_rw_iterator::end() on this returned iterator
651  * to retrieve the end iterator in a data-race free fashion.
652  * </p>
653  * @return jau::cow_darray::iterator of type jau::cow_rw_iterator
654  * @see jau::cow_rw_iterator
655  * @see jau::cow_rw_iterator::size()
656  * @see jau::cow_rw_iterator::begin()
657  * @see jau::cow_rw_iterator::end()
658  */
659  constexpr iterator begin() {
660  return iterator(*this);
661  }
662 
663  // read access
664 
665  const allocator_type& get_allocator_ref() const noexcept {
666  sc_atomic_critical sync( sync_atomic );
667  return store_ref->get_allocator_ref();
668  }
669 
670  allocator_type get_allocator() const noexcept {
671  sc_atomic_critical sync( sync_atomic );
672  return store_ref->get_allocator();
673  }
674 
675  /**
676  * Returns the growth factor
677  */
679  float growth_factor() const noexcept {
680  sc_atomic_critical sync( sync_atomic );
681  return store_ref->growth_factor();
682  }
683 
684  /**
685  * Like std::vector::empty().
686  * <p>
687  * This read operation is <i>lock-free</i>.
688  * </p>
689  * @return
690  */
692  size_type capacity() const noexcept {
693  sc_atomic_critical sync( sync_atomic );
694  return store_ref->capacity();
695  }
696 
697  /**
698  * Like std::vector::empty().
699  * <p>
700  * This read operation is <i>lock-free</i>.
701  * </p>
702  */
704  bool empty() const noexcept {
705  sc_atomic_critical sync( sync_atomic );
706  return store_ref->empty();
707  }
708 
709  /**
710  * Like std::vector::size().
711  * <p>
712  * This read operation is <i>lock-free</i>.
713  * </p>
714  */
716  size_type size() const noexcept {
717  sc_atomic_critical sync( sync_atomic );
718  return store_ref->size();
719  }
720 
721  // write access
722 
723  /**
724  * Like std::vector::reserve(), increases this instance's capacity to <code>new_capacity</code>.
725  * <p>
726  * Only creates a new storage and invalidates iterators if <code>new_capacity</code>
727  * is greater than the current jau::darray::capacity().
728  * </p>
729  * <p>
730  * This write operation uses a mutex lock and is blocking this instances' write operations only.
731  * </p>
732  */
733  void reserve(size_type new_capacity) {
734  std::lock_guard<std::recursive_mutex> lock(mtx_write);
735  if( new_capacity > store_ref->capacity() ) {
736  storage_ref_t new_store_ref = std::make_shared<storage_t>( *store_ref, new_capacity,
737  store_ref->growth_factor(),
738  store_ref->get_allocator_ref() );
739  sc_atomic_critical sync( sync_atomic );
740  store_ref = std::move(new_store_ref);
741  }
742  }
743 
744  /**
745  * Like std::vector::clear(), but ending with zero capacity.
746  * <p>
747  * This write operation uses a mutex lock and is blocking this instances' write operations.
748  * </p>
749  */
751  void clear() noexcept {
752  std::lock_guard<std::recursive_mutex> lock(mtx_write);
753  storage_ref_t new_store_ref = std::make_shared<storage_t>();
754  {
755  sc_atomic_critical sync(sync_atomic);
756  store_ref = std::move(new_store_ref);
757  }
758  }
759 
760  /**
761  * Like std::vector::swap().
762  * <p>
763  * This write operation uses a mutex lock and is blocking both cow_darray instance's write operations.
764  * </p>
765  */
767  void swap(cow_darray& x) noexcept {
768  std::unique_lock<std::recursive_mutex> lock(mtx_write, std::defer_lock); // utilize std::lock(a, b), allowing mixed order waiting on either object
769  std::unique_lock<std::recursive_mutex> lock_x(x.mtx_write, std::defer_lock); // otherwise RAII-style relinquish via destructor
770  std::lock(lock, lock_x);
771  {
772  sc_atomic_critical sync_x( x.sync_atomic );
773  sc_atomic_critical sync(sync_atomic);
774  storage_ref_t x_store_ref = x.store_ref;
775  x.store_ref = store_ref;
776  store_ref = x_store_ref;
777  }
778  }
779 
780  /**
781  * Like std::vector::pop_back().
782  * <p>
783  * This write operation uses a mutex lock and is blocking this instances' write operations only.
784  * </p>
785  */
787  void pop_back() noexcept {
788  std::lock_guard<std::recursive_mutex> lock(mtx_write);
789  if( !store_ref->empty() ) {
790  storage_ref_t new_store_ref = std::make_shared<storage_t>( store_ref->capacity(),
791  store_ref->cbegin(),
792  store_ref->cend()-1,
793  store_ref->growth_factor(),
794  store_ref->get_allocator_ref() );
795  {
796  sc_atomic_critical sync(sync_atomic);
797  store_ref = std::move(new_store_ref);
798  }
799  }
800  }
801 
802  /**
803  * Like std::vector::push_back(), copy
804  * <p>
805  * This write operation uses a mutex lock and is blocking this instances' write operations only.
806  * </p>
807  * @param x the value to be added at the tail.
808  */
810  void push_back(const value_type& x) {
811  std::lock_guard<std::recursive_mutex> lock(mtx_write);
812  if( store_ref->capacity_reached() ) {
813  // grow and swap all refs
814  storage_ref_t new_store_ref = std::make_shared<storage_t>( *store_ref, store_ref->get_grown_capacity(),
815  store_ref->growth_factor(),
816  store_ref->get_allocator_ref() );
817  new_store_ref->push_back(x);
818  {
819  sc_atomic_critical sync(sync_atomic);
820  store_ref = std::move(new_store_ref);
821  }
822  } else {
823  // just append ..
824  store_ref->push_back(x);
825  }
826  }
827 
828  /**
829  * Like std::vector::push_back(), move
830  * <p>
831  * This write operation uses a mutex lock and is blocking this instances' write operations only.
832  * </p>
833  */
835  void push_back(value_type&& x) {
836  std::lock_guard<std::recursive_mutex> lock(mtx_write);
837  if( store_ref->capacity_reached() ) {
838  // grow and swap all refs
839  storage_ref_t new_store_ref = std::make_shared<storage_t>( *store_ref, store_ref->get_grown_capacity(),
840  store_ref->growth_factor(),
841  store_ref->get_allocator_ref() );
842  new_store_ref->push_back( std::move(x) );
843  {
844  sc_atomic_critical sync(sync_atomic);
845  store_ref = std::move(new_store_ref);
846  }
847  } else {
848  // just append ..
849  store_ref->push_back( std::move(x) );
850  }
851  }
852 
853  /**
854  * Like std::vector::emplace_back(), construct a new element in place at the end().
855  * <p>
856  * Constructs the element at the end() using placement new.
857  * </p>
858  * <p>
859  * size will be increased by one.
860  * </p>
861  * @param args arguments to forward to the constructor of the element
862  */
863  template<typename... Args>
865  reference emplace_back(Args&&... args) {
866  std::lock_guard<std::recursive_mutex> lock(mtx_write);
867  if( store_ref->capacity_reached() ) {
868  // grow and swap all refs
869  storage_ref_t new_store_ref = std::make_shared<storage_t>( *store_ref, store_ref->get_grown_capacity(),
870  store_ref->growth_factor(),
871  store_ref->get_allocator_ref() );
872  reference res = new_store_ref->emplace_back( std::forward<Args>(args)... );
873  {
874  sc_atomic_critical sync(sync_atomic);
875  store_ref = std::move(new_store_ref);
876  }
877  return res;
878  } else {
879  // just append ..
880  return store_ref->emplace_back( std::forward<Args>(args)... );
881  }
882  }
883 
884  /**
885  * Like std::vector::push_back(), but appends the whole value_type range [first, last).
886  * <p>
887  * This write operation uses a mutex lock and is blocking this instances' write operations only.
888  * </p>
889  * @tparam InputIt foreign input-iterator to range of value_type [first, last)
890  * @param first first foreign input-iterator to range of value_type [first, last)
891  * @param last last foreign input-iterator to range of value_type [first, last)
892  */
893  template< class InputIt >
895  void push_back( InputIt first, InputIt last ) {
896  std::lock_guard<std::recursive_mutex> lock(mtx_write);
897  const size_type new_size_ = store_ref->size() + size_type(last - first);
898 
899  if( new_size_ > store_ref->capacity() ) {
900  // grow and swap all refs
901  storage_ref_t new_store_ref = std::make_shared<storage_t>( *store_ref, new_size_,
902  store_ref->growth_factor(),
903  store_ref->get_allocator_ref() );
904  store_ref->push_back( first, last );
905  {
906  sc_atomic_critical sync(sync_atomic);
907  store_ref = std::move(new_store_ref);
908  }
909  } else {
910  // just append ..
911  store_ref->push_back( first, last );
912  }
913  }
914 
915  /**
916  * Generic value_type equal comparator to be user defined for e.g. jau::cow_darray::push_back_unique().
917  * @param a one element of the equality test.
918  * @param b the other element of the equality test.
919  * @return true if both are equal
920  */
921  typedef bool(*equal_comparator)(const value_type& a, const value_type& b);
922 
923  /**
924  * Like std::vector::push_back(), but only if the newly added element does not yet exist.
925  * <p>
926  * This write operation uses a mutex lock and is blocking this instances' write operations only.
927  * </p>
928  * <p>
929  * Examples
930  * <pre>
931  * static jau::cow_darray<Thing>::equal_comparator thingEqComparator =
932  * [](const Thing &a, const Thing &b) -> bool { return a == b; };
933  * ...
934  * jau::cow_darray<Thing> list;
935  *
936  * bool added = list.push_back_unique(new_element, thingEqComparator);
937  * ...
938  * cow_darray<std::shared_ptr<Thing>> listOfRefs;
939  * bool added = listOfRefs.push_back_unique(new_element,
940  * [](const std::shared_ptr<Thing> &a, const std::shared_ptr<Thing> &b) -> bool { return *a == *b; });
941  * </pre>
942  * </p>
943  * @param x the value to be added at the tail, if not existing yet.
944  * @param comparator the equal comparator to return true if both given elements are equal
945  * @return true if the element has been uniquely added, otherwise false
946  */
948  bool push_back_unique(const value_type& x, equal_comparator comparator) {
949  std::lock_guard<std::recursive_mutex> lock(mtx_write);
950  for(auto it = store_ref->begin(); it != store_ref->end(); ) {
951  if( comparator( *it, x ) ) {
952  return false; // already included
953  } else {
954  ++it;
955  }
956  }
957  push_back(x);
958  return true;
959  }
960 
961  /**
962  * Erase either the first matching element or all matching elements.
963  * <p>
964  * This write operation uses a mutex lock and is blocking this instances' write operations only.
965  * </p>
966  * <p>
967  * Examples
968  * <pre>
969  * cow_darray<Thing> list;
970  * int count = list.erase_matching(element, true,
971  * [](const Thing &a, const Thing &b) -> bool { return a == b; });
972  * ...
973  * static jau::cow_darray<Thing>::equal_comparator thingRefEqComparator =
974  * [](const std::shared_ptr<Thing> &a, const std::shared_ptr<Thing> &b) -> bool { return *a == *b; };
975  * ...
976  * cow_darray<std::shared_ptr<Thing>> listOfRefs;
977  * int count = listOfRefs.erase_matching(element, false, thingRefEqComparator);
978  * </pre>
979  * </p>
980  * @param x the value to be added at the tail, if not existing yet.
981  * @param all_matching if true, erase all matching elements, otherwise only the first matching element.
982  * @param comparator the equal comparator to return true if both given elements are equal
983  * @return number of erased elements
984  */
986  int erase_matching(const value_type& x, const bool all_matching, equal_comparator comparator) {
987  int count = 0;
988 
989  iterator it = begin(); // lock mutex and copy_store
990  while( !it.is_end() ) {
991  if( comparator( *it, x ) ) {
992  it.erase();
993  ++count;
994  if( !all_matching ) {
995  break;
996  }
997  } else {
998  ++it;
999  }
1000  }
1001  if( 0 < count ) {
1002  it.write_back();
1003  }
1004  return count;
1005  }
1006 
1007  constexpr_cxx20 std::string toString() const noexcept {
1008  std::string res("{ " + std::to_string( size() ) + ": ");
1009  int i=0;
1010  jau::for_each_const(*this, [&res, &i](const value_type & e) {
1011  if( 1 < ++i ) { res.append(", "); }
1012  res.append( jau::to_string(e) );
1013  } );
1014  res.append(" }");
1015  return res;
1016  }
1017 
1018  constexpr_cxx20 std::string get_info() const noexcept {
1019  return ("cow_darray[this "+jau::to_hexstring(this)+
1020  ", "+store_ref->get_info()+
1021  "]");
1022  }
1023  };
1024 
1025  /****************************************************************************************
1026  ****************************************************************************************/
1027 
1028  template<typename Value_type, typename Alloc_type>
1029  std::ostream & operator << (std::ostream &out, const cow_darray<Value_type, Alloc_type> &c) {
1030  out << c.toString();
1031  return out;
1032  }
1033 
1034  /****************************************************************************************
1035  ****************************************************************************************/
1036 
1037  template<typename Value_type, typename Alloc_type>
1039  if( &rhs == &lhs ) {
1040  return true;
1041  }
1043  rhs_cend += rhs.size();
1044  return (rhs.size() == lhs.size() && std::equal(rhs.cbegin(), rhs_cend, lhs.cbegin()));
1045  }
1046  template<typename Value_type, typename Alloc_type>
1048  return !(rhs==lhs);
1049  }
1050 
1051  template<typename Value_type, typename Alloc_type>
1054  rhs_cend += rhs.size();
1056  lhs_cend += lhs.size();
1057  return std::lexicographical_compare(rhs.cbegin(), rhs_cend, lhs.begin(), lhs_cend);
1058  }
1059 
1060  template<typename Value_type, typename Alloc_type>
1062  { return lhs < rhs; }
1063 
1064  template<typename Value_type, typename Alloc_type>
1066  { return !(lhs < rhs); }
1067 
1068  template<typename Value_type, typename Alloc_type>
1070  { return !(rhs < lhs); }
1071 
1072  template<typename Value_type, typename Alloc_type>
1074  { rhs.swap(lhs); }
1075 
1076 } /* namespace jau */
1077 
1078 /** \example test_cow_iterator_01.cpp
1079  * This C++ unit test of const jau::cow_ro_iterator and mutable jau::cow_rw_iterator
1080  * in conjunction with jau::cow_darray demonstrates the effect of CoW const and mutable CoW operations
1081  * besides testing them.
1082  */
1083 
1084 /** \example test_cow_darray_perf01.cpp
1085  * This C++ unit test validates the performance and correctness of the jau::cow_darray implementation.
1086  */
1087 
1088 /** \example test_cow_darray_01.cpp
1089  * This C++ unit test validates the jau::cow_darray implementation.
1090  */
1091 
1092 #endif /* JAU_COW_DARRAY_HPP_ */
constexpr_cxx20
#define constexpr_cxx20
constexpr qualifier replacement for C++20 constexpr.
Definition: cpp_lang_util.hpp:95
callocator.hpp
jau::cow_darray::uses_memmove
constexpr static const bool uses_memmove
Definition: cow_darray.hpp:135
jau::cow_darray::operator=
constexpr_atomic cow_darray & operator=(const cow_darray &x)
Like std::vector::operator=(&), assignment.
Definition: cow_darray.hpp:368
jau::cow_darray::copy_store
constexpr_atomic storage_ref_t copy_store()
Returns a new shared_ptr copy of the underlying store, i.e.
Definition: cow_darray.hpp:550
jau::cow_darray::storage_ref_t
std::shared_ptr< storage_t > storage_ref_t
Definition: cow_darray.hpp:153
darray.hpp
jau::cow_darray::DEFAULT_GROWTH_FACTOR
constexpr static const float DEFAULT_GROWTH_FACTOR
Default growth factor using the golden ratio 1.618.
Definition: cow_darray.hpp:133
jau::cow_darray::begin
constexpr iterator begin()
Returns an jau::cow_rw_iterator to the first element of this CoW storage.
Definition: cow_darray.hpp:659
jau::cow_ro_iterator
Implementation of a Copy-On-Write (CoW) read-onlu iterator over immutable value_type storage.
Definition: cow_iterator.hpp:44
jau::cow_darray::darray_tag
bool darray_tag
Used to determine whether this type is a darray or has a darray, see ::is_darray_type<T>
Definition: cow_darray.hpp:156
jau::cow_darray::cbegin
constexpr const_iterator cbegin() const noexcept
Returns an jau::cow_ro_iterator to the first element of this CoW storage.
Definition: cow_darray.hpp:636
jau::cow_darray::erase_matching
constexpr_atomic int erase_matching(const value_type &x, const bool all_matching, equal_comparator comparator)
Erase either the first matching element or all matching elements.
Definition: cow_darray.hpp:986
jau::cow_darray::const_reference
const value_type & const_reference
Definition: cow_darray.hpp:145
jau::print_backtrace
void print_backtrace(const bool skip_anon_frames, const jau::snsize_t max_frames=-1, const jau::nsize_t skip_frames=2) noexcept
Prints the de-mangled backtrace string separated by newline excluding this function to stderr,...
Definition: debug.cpp:91
jau::operator<<
std::ostream & operator<<(std::ostream &out, const cow_darray< Value_type, Alloc_type > &c)
Definition: cow_darray.hpp:1029
jau::cow_rw_iterator::write_back
void write_back() noexcept
Replace the parent's current store with this iterators' instance, unlock the CoW parents' write lock ...
Definition: cow_iterator.hpp:153
jau::cow_darray::push_back
constexpr_atomic void push_back(value_type &&x)
Like std::vector::push_back(), move.
Definition: cow_darray.hpp:835
constexpr_atomic
#define constexpr_atomic
Used when designed to declare a function constexpr, but prohibited by its specific implementation.
Definition: cpp_lang_util.hpp:132
jau::cow_darray::cow_container_t
cow_darray< value_type, allocator_type, size_type, use_memmove, use_realloc, sec_mem > cow_container_t
Definition: cow_darray.hpp:160
jau::cow_darray::pop_back
constexpr_atomic void pop_back() noexcept
Like std::vector::pop_back().
Definition: cow_darray.hpp:787
jau
Definition: basic_algos.hpp:34
jau::cow_darray::cow_darray
constexpr_atomic cow_darray(const cow_darray &x, const float growth_factor, const allocator_type &alloc)
Creates a new instance, copying all elements from the given array.
Definition: cow_darray.hpp:325
jau::cow_darray::snapshot
constexpr_atomic storage_ref_t snapshot() const noexcept
Returns the current snapshot of the underlying shared storage by reference.
Definition: cow_darray.hpp:610
jau::cow_darray::size
constexpr_atomic size_type size() const noexcept
Like std::vector::size().
Definition: cow_darray.hpp:716
jau::cow_darray::storage_t
darray< value_type, allocator_type, size_type, use_memmove, use_realloc, sec_mem > storage_t
Definition: cow_darray.hpp:152
jau::cow_darray::toString
constexpr_cxx20 std::string toString() const noexcept
Definition: cow_darray.hpp:1007
jau::cow_darray::swap
constexpr_atomic void swap(cow_darray &x) noexcept
Like std::vector::swap().
Definition: cow_darray.hpp:767
jau::cow_darray::push_back
constexpr_atomic void push_back(InputIt first, InputIt last)
Like std::vector::push_back(), but appends the whole value_type range [first, last).
Definition: cow_darray.hpp:895
jau::to_string
PRAGMA_DISABLE_WARNING_POP constexpr_cxx20 std::string to_string(const endian &v) noexcept
Return std::string representation of the given jau::endian.
Definition: byte_util.hpp:198
jau::cow_darray::operator=
cow_darray & operator=(storage_t &&x)
Like std::vector::operator=(&&), move, but taking the underling jau::darray.
Definition: cow_darray.hpp:285
jau::cow_darray::reference
value_type & reference
Definition: cow_darray.hpp:144
jau::cow_darray::cow_darray
constexpr cow_darray(std::initializer_list< value_type > initlist, const allocator_type &alloc=allocator_type())
Create a new instance from an initializer list.
Definition: cow_darray.hpp:501
jau::cow_darray::growth_factor
constexpr_atomic float growth_factor() const noexcept
Returns the growth factor.
Definition: cow_darray.hpp:679
jau::cow_darray::cow_darray
constexpr_atomic cow_darray(const cow_darray &x)
Creates a new instance, copying all elements from the given array.
Definition: cow_darray.hpp:305
cow_iterator.hpp
ordered_atomic.hpp
jau::cow_darray::cow_darray
constexpr cow_darray(const storage_t &x)
Definition: cow_darray.hpp:236
jau::ordered_atomic< bool, std::memory_order::memory_order_seq_cst >
jau::darray
Implementation of a dynamic linear array storage, aka vector.
Definition: darray.hpp:102
jau::cow_darray::iterator
cow_rw_iterator< storage_t, storage_ref_t, cow_container_t > iterator
Mutable, read-write iterator, holding the write-lock and a store copy until destruction.
Definition: cow_darray.hpp:200
jau::cow_darray::cow_darray
constexpr cow_darray(const storage_t &x, const float growth_factor, const allocator_type &alloc)
Definition: cow_darray.hpp:242
jau::cow_darray::max_size
constexpr size_type max_size() const noexcept
Returns std::numeric_limits<difference_type>::max() as the maximum array size.
Definition: cow_darray.hpp:519
jau::cow_darray::get_write_mutex
constexpr std::recursive_mutex & get_write_mutex() noexcept
Returns this instances' recursive write mutex, allowing user to implement more complex mutable write ...
Definition: cow_darray.hpp:534
jau::swap
void swap(cow_darray< Value_type, Alloc_type > &rhs, cow_darray< Value_type, Alloc_type > &lhs) noexcept
Definition: cow_darray.hpp:1073
jau::cow_darray::allocator_type
Alloc_type allocator_type
Definition: cow_darray.hpp:148
jau::cow_darray::cow_darray
constexpr_atomic cow_darray(cow_darray &&x) noexcept
Definition: cow_darray.hpp:388
jau::cow_darray::capacity
constexpr_atomic size_type capacity() const noexcept
Like std::vector::empty().
Definition: cow_darray.hpp:692
jau::sc_atomic_critical
This class provides a RAII-style Sequentially Consistent (SC) data race free (DRF) critical block.
Definition: ordered_atomic.hpp:314
jau::cow_darray::get_info
constexpr_cxx20 std::string get_info() const noexcept
Definition: cow_darray.hpp:1018
jau::cow_darray::uses_realloc
constexpr static const bool uses_realloc
Definition: cow_darray.hpp:136
jau::cow_darray::empty
constexpr_atomic bool empty() const noexcept
Like std::vector::empty().
Definition: cow_darray.hpp:704
jau::cow_darray::cow_darray
constexpr cow_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: cow_darray.hpp:229
jau::cow_darray::cow_darray
constexpr cow_darray(storage_t &&x, const float growth_factor, const allocator_type &alloc) noexcept
Definition: cow_darray.hpp:272
jau::cow_darray::cow_darray
constexpr_atomic cow_darray(const cow_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 arra...
Definition: cow_darray.hpp:349
jau::cow_darray::reserve
void reserve(size_type new_capacity)
Like std::vector::reserve(), increases this instance's capacity to new_capacity.
Definition: cow_darray.hpp:733
jau::cow_darray::cow_darray
constexpr cow_darray() noexcept
Default constructor, giving almost zero capacity and zero memory footprint, but the shared empty jau:...
Definition: cow_darray.hpp:218
jau::cow_darray
Implementation of a Copy-On-Write (CoW) using jau::darray as the underlying storage,...
Definition: cow_darray.hpp:130
jau::for_each_const
constexpr UnaryFunction for_each_const(T &data, UnaryFunction f, std::enable_if_t< is_cow_type< T >::value, bool >=true) noexcept
Definition: basic_algos.hpp:323
jau::operator<=
bool operator<=(const cow_darray< Value_type, Alloc_type > &rhs, const cow_darray< Value_type, Alloc_type > &lhs)
Definition: cow_darray.hpp:1065
jau::cow_darray::get_allocator_ref
const allocator_type & get_allocator_ref() const noexcept
Definition: cow_darray.hpp:665
jau::cow_darray::operator=
constexpr_atomic cow_darray & operator=(cow_darray &&x) noexcept
Like std::vector::operator=(&&), move.
Definition: cow_darray.hpp:413
jau::cow_darray::emplace_back
constexpr_atomic reference emplace_back(Args &&... args)
Like std::vector::emplace_back(), construct a new element in place at the end().
Definition: cow_darray.hpp:865
debug.hpp
jau::cow_darray::difference_type
std::make_signed< size_type >::type difference_type
Definition: cow_darray.hpp:147
jau::cow_rw_iterator::is_end
constexpr bool is_end() const noexcept
Returns true, if this iterator points to end().
Definition: cow_iterator.hpp:305
jau::cow_darray::get_allocator
allocator_type get_allocator() const noexcept
Definition: cow_darray.hpp:670
jau::cow_darray::~cow_darray
~cow_darray() noexcept
Definition: cow_darray.hpp:508
jau::operator>
bool operator>(const cow_darray< Value_type, Alloc_type > &rhs, const cow_darray< Value_type, Alloc_type > &lhs)
Definition: cow_darray.hpp:1061
jau::to_hexstring
std::string to_hexstring(value_type const &v) noexcept
Produce a lower-case hexadecimal string representation of the given pointer.
Definition: string_util.hpp:104
jau::cow_darray::equal_comparator
bool(* equal_comparator)(const value_type &a, const value_type &b)
Generic value_type equal comparator to be user defined for e.g.
Definition: cow_darray.hpp:921
cpp_lang_util.hpp
jau::cow_darray::operator=
cow_darray & operator=(const storage_t &x)
Like std::vector::operator=(&), assignment, but copying from the underling jau::darray.
Definition: cow_darray.hpp:254
jau::cow_darray::clear
constexpr_atomic void clear() noexcept
Like std::vector::clear(), but ending with zero capacity.
Definition: cow_darray.hpp:751
jau::darray::get_info
constexpr_cxx20 std::string get_info() const noexcept
Definition: darray.hpp:1136
jau::cow_darray::const_pointer
const value_type * const_pointer
Definition: cow_darray.hpp:143
jau::callocator
A simple allocator using POSIX C functions: ::malloc(), ::free() and ::realloc().
Definition: callocator.hpp:53
jau::cow_darray::size_type
Size_type size_type
Definition: cow_darray.hpp:146
jau::cow_darray::cow_darray
constexpr cow_darray(storage_t &&x) noexcept
Definition: cow_darray.hpp:265
jau::operator!=
bool operator!=(const callocator< T1 > &lhs, const callocator< T2 > &rhs) noexcept
Definition: callocator.hpp:155
basic_types.hpp
jau::cow_rw_iterator
Implementation of a Copy-On-Write (CoW) read-write iterator over mutable value_type storage.
Definition: cow_iterator.hpp:47
jau::cow_darray::push_back
constexpr_atomic void push_back(const value_type &x)
Like std::vector::push_back(), copy.
Definition: cow_darray.hpp:810
jau::cow_darray::const_iterator
cow_ro_iterator< storage_t, storage_ref_t, cow_container_t > const_iterator
Immutable, read-only const_iterator, lock-free, holding the current shared store reference until dest...
Definition: cow_darray.hpp:181
jau::cow_darray::set_store
constexpr_atomic void set_store(storage_ref_t &&new_store_ref) noexcept
Replace the current store with the given instance, potentially acquired via jau::cow_darray::copy_sto...
Definition: cow_darray.hpp:587
jau::operator>=
bool operator>=(const cow_darray< Value_type, Alloc_type > &rhs, const cow_darray< Value_type, Alloc_type > &lhs)
Definition: cow_darray.hpp:1069
jau::cow_rw_iterator::erase
constexpr void erase()
Erases the element at the current position.
Definition: cow_iterator.hpp:488
jau::cow_darray::pointer
value_type * pointer
Definition: cow_darray.hpp:142
jau::cow_darray::cow_darray
constexpr cow_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: cow_darray.hpp:472
jau::operator==
bool operator==(const callocator< T1 > &lhs, const callocator< T2 > &rhs) noexcept
Definition: callocator.hpp:142
jau::cow_darray::cow_darray
constexpr cow_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: cow_darray.hpp:450
DARRAY_PRINTF
#define DARRAY_PRINTF(...)
Definition: darray.hpp:53
jau::operator<
bool operator<(const cow_darray< Value_type, Alloc_type > &rhs, const cow_darray< Value_type, Alloc_type > &lhs)
Definition: cow_darray.hpp:1052
jau::cow_darray::push_back_unique
constexpr_atomic 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: cow_darray.hpp:948
jau::cow_darray::cow_darray
constexpr cow_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: cow_darray.hpp:489
jau::cow_darray::value_type
Value_type value_type
Definition: cow_darray.hpp:141
jau::cow_darray::uses_secmem
constexpr static const bool uses_secmem
Definition: cow_darray.hpp:137