Stroika Library 3.0d18
 
Loading...
Searching...
No Matches
Array.h
1/*
2 * Copyright(c) Sophist Solutions, Inc. 1990-2025. All rights reserved
3 */
4#ifndef _Stroika_Foundation_Containers_DataStructures_Array_h_
5#define _Stroika_Foundation_Containers_DataStructures_Array_h_
6
7#include "Stroika/Foundation/StroikaPreComp.h"
8
9#include <optional>
10
11#include "Stroika/Foundation/Common/Common.h"
13#include "Stroika/Foundation/Containers/Common.h"
15#include "Stroika/Foundation/Execution/Common.h"
16#include "Stroika/Foundation/Memory/Common.h"
17
18/**
19 * \note Code-Status: <a href="Code-Status.md#Beta">Beta</a>
20 */
21
23
25
26 /**
27 * \brief very similar to std::vector<T>
28 *
29 * This class provides an array abstraction, where the size can be set dynamically, and
30 * extra sluff is maintained off the end to reduce copying from reallocs.
31 * Only items 0..size ()-1 are kept constructed. The rest (size()+1
32 * ..fSlotsAlloced) are uninitialized memory. This is important because
33 * it means you can count on DTORs of your T being called when you
34 * remove them from contains, not when the caches happen to empty.
35 *
36 * Array<T> is simple data structure implementation. It is not intended to be directly
37 * used by programmers, except in implementing concrete container reps (and occasionally in
38 * performance sensitive situations, though std::vector<> maybe a better choice then).
39 *
40 * Array<T> is a template which provides a dynamic array class (very similar to std::vector). Elements
41 * of type T can be assigned, and accessed much like a normal array, except
42 * that when debug is on, accesses are range-checked.
43 *
44 * Array<T> also provides a dynamic sizing capability. It reallocs its
45 * underlying storage is such a ways as to keep a buffer of roughly 20%
46 * extra (see Support::ReserveTweaks::GetScaledUpCapacity), so that reallocs on resizes
47 * only occur log(n) times on n appends.
48 * To save even this space, you can call shrink_to_fit().
49 */
50 template <typename T>
52 public:
53 using value_type = T;
54
55 public:
56 /**
57 * Basic (mostly internal) element used by ForwardIterator. Abstract name so can be referenced generically across 'DataStructure' objects
58 */
59 using UnderlyingIteratorRep = size_t;
60
61 public:
62 /**
63 */
64 Array () = default;
65 Array (Array&& from);
66 Array (const Array& from);
67
68 public:
69 ~Array ();
70
71 public:
72 nonvirtual Array& operator= (const Array& rhs);
73
74 public:
75 class ForwardIterator;
76 class BackwardIterator;
77
78 public:
79 /**
80 * \brief returns internal pointer to data - which is unsynchronized, and only guaranteed valid until the next non-const array method.
81 */
82 nonvirtual T* data () noexcept;
83 nonvirtual const T* data () const noexcept;
84
85 public:
86 /**
87 * \note Runtime performance/complexity:
88 * Always: constant
89 */
90 nonvirtual T GetAt (size_t i) const;
91
92 public:
93 /**
94 * Not a great API, since cannot check it very well. However, its more efficient when storing a larger object and you need
95 * to update just part of it.
96 *
97 * \note Runtime performance/complexity:
98 * Always: constant
99 */
100 nonvirtual T* PeekAt (size_t i);
101 nonvirtual const T* PeekAt (size_t i) const;
102
103 public:
104 /**
105 * \note Runtime performance/complexity:
106 * Always: constant
107 */
108 nonvirtual void SetAt (size_t i, ArgByValueType<T> item);
109
110 public:
111 /**
112 * \note Runtime performance/complexity:
113 * Always: constant
114 */
115 nonvirtual T& operator[] (size_t i);
116 nonvirtual T operator[] (size_t i) const;
117
118 public:
119 /**
120 * \note Runtime performance/complexity:
121 * Always: constant
122 */
123 nonvirtual size_t size () const;
124
125 public:
126 /**
127 * \note Runtime performance/complexity:
128 * Always: constant
129 */
130 nonvirtual bool empty () const;
131
132 public:
133 /**
134 * \note Runtime performance/complexity:
135 * Worst Case: O(N)
136 * Typical Case: ?? for small changes often constant, but if enuf change of size O(N) growing. Less shrinking.
137 */
138 nonvirtual void SetLength (size_t newLength, ArgByValueType<T> fillValue);
139
140 public:
141 /**
142 * \note index may == size() - in which case, we are appending.
143 *
144 * \note Runtime performance/complexity:
145 * Worst Case: O(N)
146 * Typical: depends on i, and Capacity - if need to change capacity O(N), and if near start of array O(N), and if near end of the array (append) can be cheap
147 */
148 nonvirtual void Insert (size_t index, ArgByValueType<T> item);
149 template <Memory::ISpanOfT<T> SPAN_T>
150 nonvirtual void Insert (size_t at, const SPAN_T& copyFrom);
151 nonvirtual void Insert (const ForwardIterator& i, ArgByValueType<T> item);
152 nonvirtual void Insert (const BackwardIterator& i, ArgByValueType<T> item);
153
154 public:
155#if qCompilerAndStdLib_MemoryInsertAt_Buggy
156 nonvirtual void Insert_BWA (size_t index, ArgByValueType<T> item);
157#endif
158
159 public:
160 /**
161 * \brief STL-ish alias for Insert (size(), item)
162 *
163 * @aliases Append
164 *
165 * \note Runtime performance/complexity:
166 * Worst Case: O(N)
167 * Typical: constant
168 */
169 nonvirtual void push_back (ArgByValueType<T> item);
170
171 public:
172 /**
173 * \note Runtime performance/complexity:
174 * Worst Case: O(N) - if !trivial_type
175 * Typical: constant
176 */
177 nonvirtual void clear ();
178
179 public:
180 /**
181 * \note Runtime performance/complexity:
182 * Always: O(N)
183 */
184 template <invocable<T> FUNCTION>
185 nonvirtual void Apply (FUNCTION&& doToElement, Execution::SequencePolicy seq = Execution::SequencePolicy::eDEFAULT) const;
186
187 public:
188 class IteratorBase;
189
190 public:
191 /**
192 */
193 nonvirtual ForwardIterator begin () const;
194
195 public:
196 /**
197 */
198 constexpr ForwardIterator end () const;
199
200 public:
201 /**
202 * Return ForwardIterator of first place in the array matching, or nullptr if not found
203 *
204 * \note Runtime performance/complexity:
205 * Worst Case: O(N)
206 * Typical: O(N), but can be less if systematically finding entries near start of array
207 *
208 * \note in Stroika v2.1, this returned value == size() means not found, but now uses optional to make clearer
209 * and more similar to LinkedList find ...
210 *
211 * \note before Stroika v3.0d10, this returned optional<size_t>
212 *
213 * EQUALS_COMPARER OVERLOAD : Returns pointer to T (or nullptr if not found). Lifetime of T* only til next call on this.
214 *
215 * \alias Lookup, First, Contains (sort of)
216 */
217 template <predicate<T> FUNCTION>
218 nonvirtual ForwardIterator Find (FUNCTION&& firstThat) const;
219 template <typename EQUALS_COMPARER = equal_to<T>>
220 nonvirtual const T* Find (ArgByValueType<T> item, EQUALS_COMPARER&& equalsComparer = {}) const;
221 template <typename EQUALS_COMPARER = equal_to<T>>
222 nonvirtual T* Find (ArgByValueType<T> item, EQUALS_COMPARER&& equalsComparer = {});
223
224 public:
225 /*
226 * Memory savings/optimization methods. Use this to tune usage
227 * of arrays so that they don't waste time in Realloc's.
228 */
229 nonvirtual size_t capacity () const;
230
231 public:
232 /**
233 * \brief sets the reserved capacity to slotsAlloced
234 *
235 * \pre size () <= slotsAlloced
236 *
237 * \see also more likely to use ReserveAtLeast
238 */
239 nonvirtual void reserve (size_t slotsAlloced);
240
241 public:
242 /**
243 * \note slotsAllocated maybe < size() - but then it would be ignored, since this only grows the capacity
244 */
245 nonvirtual void ReserveAtLeast (size_t slotsAlloced);
246
247 public:
248 /**
249 */
250 nonvirtual void shrink_to_fit ();
251
252 public:
253 /*
254 * Support for COW (CopyOnWrite):
255 *
256 * Take iterator 'pi' which is originally a valid iterator from 'movedFrom' - and replace *pi with a valid
257 * iterator from 'this' - which points at the same logical position. This requires that this container
258 * was just 'copied' from 'movedFrom' - and is used to produce an equivalent iterator (since iterators are tied to
259 * the container they were iterating over).
260 */
261 nonvirtual void MoveIteratorHereAfterClone (IteratorBase* pi, const Array* movedFrom) const;
262
263 public:
264 /**
265 * \note Runtime performance/complexity:
266 * Worst Case: O(N)
267 * Typical: depends on index but typically O(N) (can be less if removing from end of Array)
268 *
269 * \see erase () - same as Remove(it) but returns iterator of 'next'
270 */
271 nonvirtual void Remove (const ForwardIterator& i);
272 nonvirtual void Remove (const BackwardIterator& i);
273 nonvirtual void Remove (size_t index) noexcept;
274 nonvirtual void Remove (size_t from, size_t to) noexcept;
275
276 public:
277 /**
278 * \brief remove the element at i, and return valid iterator to the element that was following it (which can be empty iterator)
279 *
280 * \pre i != end ()
281 *
282 * \brief see https://en.cppreference.com/w/cpp/container/vector/erase
283 */
284 nonvirtual ForwardIterator erase (const ForwardIterator& i);
285
286 public:
287 /**
288 */
289 nonvirtual void SetAt (const ForwardIterator& i, ArgByValueType<T> newValue);
290 nonvirtual void SetAt (const BackwardIterator& i, ArgByValueType<T> newValue);
291
292 public:
293 nonvirtual void Invariant () const noexcept;
294
295#if qStroika_Foundation_Debug_AssertionsChecked
296 private:
297 nonvirtual void Invariant_ () const noexcept;
298#endif
299
300 public:
301 template <typename EQUALS_COMPARER = equal_to<T>>
302 [[deprecated ("Since Stroika v3.0d18")]] bool Contains (ArgByValueType<T> item, EQUALS_COMPARER&& equalsComparer) const
303 {
304 return this->Find (item, equalsComparer) != nullptr;
305 }
306 /**
307 * \brief insert the
308 * NB: Can be called if i done, and just means add before the last item (so if i==end() - same as append)
309 */
310 [[deprecated ("Since v3.0d18 - use Insert()")]] void AddBefore (const ForwardIterator& i, ArgByValueType<T> item)
311 {
312 InsertAt (i, item);
313 }
314 [[deprecated ("Since v3.0d18 - use Insert()")]] void AddBefore (const BackwardIterator& i, ArgByValueType<T> item)
315 {
316 InsertAt (i, item);
317 }
318 [[deprecated ("Since v3.0d18 - use Insert()")]] void AddAfter (const ForwardIterator& i, ArgByValueType<T> item)
319 {
320 Insert (i.CurrentIndex () + 1, item);
321 }
322 [[deprecated ("Since v3.0d18 - use Insert()")]] void AddAfter (const BackwardIterator& i, ArgByValueType<T> newValue)
323 {
324 Insert (i.CurrentIndex () + 1, newValue);
325 }
326
327 private:
328 // mostly useful cuz allows for use of realloc, which might imply fewer copies,
329 // but is only legal for trivially_copyable types (cuz realloc sometimes resizes sometimes moves data)
330 static constexpr bool kUseMalloc_{is_trivially_copyable_v<T>};
331
332 private:
333 size_t fLength_{0}; // #items advertised/constructed
334 size_t fSlotsAllocated_{0}; // #items allocated (though not necessarily initialized)
335 T* fItems_{nullptr};
336 };
337
338 /**
339 * IteratorBase<T> is an un-advertised implementation
340 * detail designed to help in source-code sharing among various
341 * iterator implementations.
342 *
343 * \note Design note:
344 * Use index instead of cursored pointer, since performance appears same either way, and
345 * cursored pointer requires patching considerations on 'realloc'.
346 */
347 template <typename T>
348 class Array<T>::IteratorBase {
349 public:
350 // stuff STL requires you to set to look like an iterator
351 using iterator_category = forward_iterator_tag;
352 using value_type = Array::value_type;
353 using difference_type = ptrdiff_t;
354 using pointer = const value_type*;
355 using reference = const value_type&;
356
357 public:
358 constexpr IteratorBase () noexcept = default;
359 IteratorBase (const Array* data);
360 IteratorBase (const IteratorBase&) noexcept = default;
361
362#if qStroika_Foundation_Debug_AssertionsChecked
363 ~IteratorBase ();
364#endif
365
366 public:
367 nonvirtual const T& operator* () const; // Error to call if Done (), otherwise OK
368
369 public:
370 nonvirtual const T* operator->() const; // Error to call if Done (), otherwise OK
371
372 public:
373 nonvirtual size_t CurrentIndex () const; // NB: This can be called if we are done - if so, it returns size() + 1.
374
375 public:
376 nonvirtual void SetIndex (size_t i);
377
378 public:
379 nonvirtual UnderlyingIteratorRep GetUnderlyingIteratorRep () const;
380
381 public:
382 nonvirtual void SetUnderlyingIteratorRep (const UnderlyingIteratorRep l);
383
384 public:
385 /**
386 * For debugging, assert the iterator data matches argument data
387 */
388 constexpr void AssertDataMatches (const Array* data) const;
389
390 public:
391 nonvirtual void Invariant () const noexcept;
392
393#if qStroika_Foundation_Debug_AssertionsChecked
394 private:
395 nonvirtual void Invariant_ () const noexcept;
396#endif
397
398 protected:
399 const Array* _fData{nullptr};
400 size_t _fCurrentIdx{0};
401
402 private:
403 friend class Array;
404 };
405
406 /**
407 * Use this iterator to iterate forwards over the array. Be careful
408 * not to add or remove things from the array while using this iterator,
409 * since it is not safe.
410 */
411 template <typename T>
412 class Array<T>::ForwardIterator : public Array<T>::IteratorBase {
413 private:
414 using inherited = IteratorBase;
415
416 public:
417 /**
418 * overload taking only 'data' starts at beginning.
419 * note startAt = 0 for begin(), and startAt = data->size () for end
420 */
421 constexpr ForwardIterator () noexcept = default;
422 explicit ForwardIterator (const Array* data, UnderlyingIteratorRep startAt = static_cast<UnderlyingIteratorRep> (0));
423 ForwardIterator (const ForwardIterator&) noexcept = default;
424 constexpr ForwardIterator (ForwardIterator&&) noexcept;
425
426 public:
427 nonvirtual ForwardIterator& operator= (const ForwardIterator&) = default;
428 nonvirtual ForwardIterator& operator= (ForwardIterator&&) noexcept = default;
429
430 public:
431 /**
432 * return true if iterator not Done
433 */
434 explicit operator bool () const;
435
436 public:
437 nonvirtual bool Done () const noexcept;
438
439 public:
440 nonvirtual ForwardIterator& operator++ () noexcept;
441 nonvirtual ForwardIterator operator++ (int) noexcept;
442
443 public:
444 nonvirtual bool operator== (const ForwardIterator& rhs) const;
445 };
446
447 /**
448 * Use this iterator to iterate backwards over the array. Be careful
449 * not to add or remove things from the array while using this iterator,
450 * since it is not safe. Use BackwardIterator_Patch for those cases.
451 *
452 * // NOTE - I THINK NYI (fully) and not used
453 */
454 template <typename T>
455 class Array<T>::BackwardIterator : public Array<T>::IteratorBase {
456 private:
457 using inherited = IteratorBase;
458
459 public:
460 BackwardIterator (const Array* data);
462
463 public:
464 nonvirtual bool Done () const noexcept;
465
466 public:
467 nonvirtual BackwardIterator& operator++ () noexcept;
468
469 public:
470 nonvirtual bool operator== (const BackwardIterator& rhs) const;
471 };
472
473 static_assert (ranges::input_range<Array<int>>); // smoke test - make sure basic iteration etc should work (allows formattable to work)
474
475}
476
477/*
478 ********************************************************************************
479 ***************************** Implementation Details ***************************
480 ********************************************************************************
481 */
482#include "Array.inl"
483
484#endif /*_Stroika_Foundation_Containers_DataStructures_Array_h_ */
nonvirtual void Apply(FUNCTION &&doToElement, Execution::SequencePolicy seq=Execution::SequencePolicy::eDEFAULT) const
nonvirtual ForwardIterator Find(FUNCTION &&firstThat) const
nonvirtual void Remove(const ForwardIterator &i)
Definition Array.inl:535
nonvirtual void push_back(ArgByValueType< T > item)
STL-ish alias for Insert (size(), item)
Definition Array.inl:144
nonvirtual void Insert(size_t index, ArgByValueType< T > item)
Definition Array.inl:62
nonvirtual T * data() noexcept
returns internal pointer to data - which is unsynchronized, and only guaranteed valid until the next ...
Definition Array.inl:451
nonvirtual void SetAt(size_t i, ArgByValueType< T > item)
Definition Array.inl:487
nonvirtual void reserve(size_t slotsAlloced)
sets the reserved capacity to slotsAlloced
Definition Array.inl:251
nonvirtual void ReserveAtLeast(size_t slotsAlloced)
Definition Array.inl:320
void AddBefore(const ForwardIterator &i, ArgByValueType< T > item)
insert the NB: Can be called if i done, and just means add before the last item (so if i==end() - sam...
Definition Array.h:310
nonvirtual void SetLength(size_t newLength, ArgByValueType< T > fillValue)
Definition Array.inl:389
nonvirtual ForwardIterator erase(const ForwardIterator &i)
remove the element at i, and return valid iterator to the element that was following it (which can be...
Definition Array.inl:543
NOT a real mutex - just a debugging infrastructure support tool so in debug builds can be assured thr...
conditional_t<(sizeof(CHECK_T)<=2 *sizeof(void *)) and is_trivially_copyable_v< CHECK_T >, CHECK_T, const CHECK_T & > ArgByValueType
This is an alias for 'T' - but how we want to pass it on stack as formal parameter.
Definition TypeHints.h:32
constexpr void AssertDataMatches(const DoublyLinkedList *data) const
nonvirtual ForwardIterator & operator++() noexcept
nonvirtual size_t CurrentIndex(const DoublyLinkedList *data) const
SequencePolicy
equivalent which of 4 types being used std::execution::sequenced_policy, parallel_policy,...