Stroika Library 3.0d24
 
Loading...
Searching...
No Matches
Array.inl
1/*
2 * Copyright(c) Sophist Solutions, Inc. 1990-2026. All rights reserved
3 */
4#include <optional>
5
6#include "Stroika/Foundation/Containers/Support/ReserveTweaks.h"
8#include "Stroika/Foundation/Execution/Throw.h"
9#include "Stroika/Foundation/Memory/Common.h"
10
12
13// Would like to leave on by default but we just added and cannot afford to have debug builds get that slow
14#ifndef qStroika_Foundation_Containers_DataStructures_Array_IncludeSlowDebugChecks_
15#define qStroika_Foundation_Containers_DataStructures_Array_IncludeSlowDebugChecks_ 0
16#endif
17
18 /*
19 ********************************************************************************
20 *********************************** Array<T> ***********************************
21 ********************************************************************************
22 */
23 template <typename T>
24 inline void Array<T>::Invariant () const noexcept
25 {
26#if qStroika_Foundation_Debug_AssertionsChecked
27 Invariant_ ();
28#endif
29 }
30 template <typename T>
31 Array<T>::Array (const Array& from)
32 {
33 from.Invariant ();
34 reserve (from.size ());
35
36 /*
37 * Construct the new items in-place into the new memory.
38 */
39 size_t newLength = from.size ();
40 if (newLength > 0) {
41 T* lhs = &fItems_[0];
42 const T* rhs = &from.fItems_[0];
43 T* end = &fItems_[newLength];
44 do {
45 new (lhs) T{*rhs++};
46 } while (++lhs < end);
47 }
48 fLength_ = newLength;
49 Invariant ();
50 }
51 template <typename T>
52 Array<T>::Array (Array&& from) noexcept
53 : fItems_{move (from.fItems_)}
54 , fLength_{from.fLength_}
55 {
56 Invariant ();
57 from.fItems_ = nullptr;
58 from.fLength_ = 0;
59 from.Invariant ();
60 }
61 template <typename T>
62 inline void Array<T>::Insert (size_t index, ArgByValueType<T> item)
63 {
64 Insert (index, span{&item, 1});
65 }
66#if qCompilerAndStdLib_MemoryInsertAt_Buggy
67 template <typename T>
68 nonvirtual void Array<T>::Insert_BWA (size_t index, ArgByValueType<T> item)
69 {
70 // workaround crash in gcc optimized output
72 Require (index >= 0);
73 Require (index <= fLength_);
74 Invariant ();
75 size_t oldLength = fLength_;
76 SetLength (oldLength + 1, item); // Add space for extra item
77 if (index < oldLength) {
78 Assert (fLength_ >= 2);
79 T* lhs = &fItems_[fLength_ - 1];
80 T* rhs = &fItems_[fLength_ - 2];
81 size_t i = fLength_ - 1;
82 for (; i > index; --i) {
83 *lhs-- = *rhs--;
84 }
85 Assert (i == index);
86 Assert (lhs == &fItems_[index]);
87 *lhs = item;
88 }
89 Invariant ();
90 }
91#endif
92 template <typename T>
93 template <Memory::ISpanOfT<T> SPAN_T>
94 void Array<T>::Insert (size_t at, const SPAN_T& copyFrom)
95 {
97 Require (at >= 0);
98 Require (at <= fLength_);
99 Invariant ();
100 size_t n2Add = copyFrom.size ();
101 if (n2Add != 0) [[likely]] {
102 size_t sz = size ();
103 size_t newSz = sz + n2Add;
104 ReserveAtLeast (newSz);
105#if qCompilerAndStdLib_MemoryInsertAt_Buggy
106 // maybe is a compiler bug - cuz no problem on g++-15 ubuntu 25.04
107 //temporary BWA til I find what is wrong with Memory::Insert () on gcc optimizer
108 for (size_t i = 0; i < copyFrom.size (); ++i) {
109 this->Insert_BWA (i + at, copyFrom[i]);
110 }
111#else
112 this->fLength_ = Memory::Insert (span{this->data (), sz}, span{this->data (), capacity ()}, at, copyFrom).size ();
113#endif
114 Assert (this->fLength_ == newSz);
115 }
116 Invariant ();
117 }
118 template <typename T>
119 inline void Array<T>::Insert (const ForwardIterator& i, ArgByValueType<T> newValue)
120 {
122 Require (i._fData == this); // assure iterator not stale
123 // i CAN BE DONE OR NOT
124 Insert (i.CurrentIndex (), newValue);
125 }
126 template <typename T>
127 inline void Array<T>::Insert (const BackwardIterator& i, ArgByValueType<T> newValue)
128 {
130 Require (i._fData == this); // assure iterator not stale
131 // i CAN BE DONE OR NOT
132 Insert (i.CurrentIndex (), newValue);
133 }
134 template <typename T>
135 inline void Array<T>::push_back (ArgByValueType<T> item)
136 {
137 Insert (this->size (), item);
138 }
139 template <typename T>
140 void Array<T>::Remove (size_t index) noexcept
141 {
143 Require (index >= 0);
144 Require (index < fLength_);
145 Invariant ();
146 (void)Memory::Remove (span{this->data (), size ()}, span{this->data (), capacity ()}, index, index + 1);
147 --fLength_;
148 Invariant ();
150 template <typename T>
151 void Array<T>::Remove (size_t from, size_t to) noexcept
152 {
154 Invariant ();
155 fLength_ = Memory::Remove (span{this->data (), size ()}, span{this->data (), capacity ()}, from, to).size ();
156 Invariant ();
157 }
158 template <typename T>
160 {
162 Invariant ();
163 T* p = &fItems_[0];
164 for (size_t i = fLength_; i > 0; --i, ++p) {
165 destroy_at (p);
166 }
167 fLength_ = 0;
168 Invariant ();
169 }
170 template <typename T>
171 template <invocable<T> FUNCTION>
172 inline void Array<T>::Apply (FUNCTION&& doToElement) const
173 {
174 // @todo measure the crossover and auto-choose the policy here - eSeq is a placeholder, not a decision
175 Apply (forward<FUNCTION> (doToElement), Execution::SequencePolicy::eSeq);
176 }
177 template <typename T>
178 template <invocable<T> FUNCTION>
179 inline void Array<T>::Apply (FUNCTION&& doToElement, Execution::SequencePolicy seq) const
180 {
182 const T* start = &fItems_[0];
183 const T* end = &fItems_[fLength_];
184 switch (seq) {
185#if __cpp_lib_execution >= 201603L
186 // only ePar goes parallel; 'default' running sequentially is deliberate - see the dispatch note
187 // on Execution::SequencePolicy
189 for_each (execution::par, start, end, forward<FUNCTION> (doToElement));
190 break;
191#endif
192 // @todo add other Execution::SequencePolicy cases
193 default:
194 for_each (start, end, forward<FUNCTION> (doToElement));
195 break;
196 }
197 }
198 template <typename T>
199 inline auto Array<T>::begin () const -> ForwardIterator
200 {
201 return ForwardIterator{this, 0};
202 }
203 template <typename T>
204 constexpr auto Array<T>::end () const -> ForwardIterator
205 {
206 return ForwardIterator{};
207 }
208 template <typename T>
209 template <predicate<T> FUNCTION>
210 auto Array<T>::Find (FUNCTION&& firstThat) const -> ForwardIterator
211 {
213 const T* start = &fItems_[0];
214 const T* i = start;
215 const T* last = &fItems_[fLength_];
216 for (; i < last; ++i) {
217 if (forward<FUNCTION> (firstThat) (*i)) {
218 return ForwardIterator{this, static_cast<size_t> (i - start)};
219 }
220 }
221 return end ();
222 }
223 template <typename T>
224 template <typename EQUALS_COMPARER>
225 const T* Array<T>::Find (ArgByValueType<T> item, EQUALS_COMPARER&& equalsComparer) const
226 {
227 const T* start = &fItems_[0];
228 const T* last = &fItems_[fLength_];
229 for (const T* i = start; i < last; ++i) {
230 if (forward<EQUALS_COMPARER> (equalsComparer) (i->fItem, item)) {
231 return &i->fItem;
232 }
233 }
234 return nullptr;
235 }
236 template <typename T>
237 template <typename EQUALS_COMPARER>
238 T* Array<T>::Find (ArgByValueType<T> item, EQUALS_COMPARER&& equalsComparer)
239 {
240 const T* start = &fItems_[0];
241 const T* last = &fItems_[fLength_];
242 for (const T* i = start; i < last; ++i) {
243 if (forward<EQUALS_COMPARER> (equalsComparer) (i->fItem, item)) {
244 return &i->fItem;
245 }
246 }
247 return nullptr;
248 }
249 template <typename T>
250 void Array<T>::reserve (size_t slotsAlloced)
251 {
252 /*
253 */
255 Require (size () <= slotsAlloced);
256 Invariant ();
257 if (fSlotsAllocated_ != slotsAlloced) {
258 if (slotsAlloced == 0) {
259 if constexpr (kUseMalloc_) {
260 if (fItems_ != nullptr) {
261 free (fItems_);
262 fItems_ = nullptr;
263 }
264 }
265 else {
266 delete[] (char*)fItems_;
267 fItems_ = nullptr;
268 }
269 }
270 else {
271 /*
272 * We should consider getting rid of use of realloc since it prohibits
273 * internal pointers. For example, we cannot have an array of patchable_arrays.
274 */
275 if (fItems_ == nullptr) {
276 if constexpr (kUseMalloc_) {
277 fItems_ = (T*)malloc (sizeof (T) * slotsAlloced);
278 Execution::ThrowIfNull (fItems_);
279 }
280 else {
281 fItems_ = (T*)new char[sizeof (T) * slotsAlloced];
282 }
283 }
284 else {
285 if constexpr (kUseMalloc_) {
286 auto newVal = (T*)realloc (fItems_, sizeof (T) * slotsAlloced);
287 Execution::ThrowIfNull (newVal);
288 fItems_ = newVal;
289 }
290 else {
291 // do better, but for now at least do something SAFE
292 // USE SFINAE IsTriviallyCopyable to see which way to do it (if can use realloc).
293 // ALSO - on windoze - use _expand() if available...
294 T* newV = (T*)new char[sizeof (T) * slotsAlloced];
295 try {
296 size_t n2Copy = fLength_;
297 uninitialized_copy_n (&fItems_[0], n2Copy, newV);
298 }
299 catch (...) {
300 delete[] (char*)newV;
301 throw;
302 }
303 {
304 T* end = &fItems_[fLength_];
305 for (T* p = &fItems_[0]; p != end; ++p) {
306 destroy_at (p);
307 }
308 }
309 delete[] (char*)fItems_;
310 fItems_ = newV;
311 }
312 }
313 }
314 fSlotsAllocated_ = slotsAlloced;
315 }
316 Invariant ();
317 }
318 template <typename T>
319 inline void Array<T>::ReserveAtLeast (size_t slotsAlloced)
320 {
321 if (slotsAlloced > capacity ()) [[unlikely]] {
322 /*
323 * Bump up Slots alloced to be at least big enuf for our
324 * new length. We could be minimalistic here, and just bump up
325 * exactly, but this function can be expensive because it calls
326 * realloc which could cause lots of memory copying. There are two
327 * plausible strategies for bumping up memory in big chunks-
328 * rounding up, and scaling up.
329 */
330 reserve (Containers::Support::ReserveTweaks::GetScaledUpCapacity (slotsAlloced, sizeof (T)));
331 }
332 Ensure (size () <= capacity ());
333 Ensure (capacity () >= slotsAlloced);
334 }
335 template <typename T>
336 auto Array<T>::operator= (const Array& list) -> Array&
337 {
339 Invariant ();
340 size_t newLength = list.size ();
341
342 // @todo CLEANUP using std::copy and uninitialized_copy and range::destroy (or iterator range destory)
343
344 /*
345 * In case user already set this, we should not unset,
346 * but must be sure we are big enuf. Do this before we store any pointers
347 * cuz it could invalidate them.
348 */
349 reserve (max (capacity (), newLength));
350
351 /*
352 * Copy array elements where both sides where constructed.
353 */
354 size_t commonLength = Stroika::Foundation::min (fLength_, newLength);
355 T* lhs = &fItems_[0];
356 T* rhs = &list.fItems_[0];
357 for (size_t i = commonLength; i-- > 0;) {
358 *lhs++ = *rhs++;
359 }
360
361 /*
362 * Now if new length smaller, we must destroy entries at the end, and
363 * otherwise we must copy in new entries.
364 */
365 Assert (lhs == &fItems_[commonLength]); // point 1 past first guy to destroy/overwrite
366 if (fLength_ > newLength) {
367 T* end = &fItems_[fLength_]; // point 1 past last old guy
368 /*
369 * Then we must destruct entries at the end.
370 */
371 Assert (lhs < end);
372 do {
373 destroy_at (lhs);
374 } while (++lhs < end);
375 }
376 else if (fLength_ < newLength) {
377 T* end = &fItems_[newLength]; // point 1 past last new guy
378 Assert (lhs < end);
379 do {
380 new (lhs) T{*rhs++};
381 } while (++lhs < end);
382 }
383 fLength_ = newLength;
384 Invariant ();
385 return *this;
386 }
387 template <typename T>
388 void Array<T>::SetLength (size_t newLength, ArgByValueType<T> fillValue)
389 {
391 Invariant ();
392
393 /*
394 * Safe to grow the memory, but not to shrink it here, since
395 * we may need to destruct guys in the shrinking case.
396 */
397 ReserveAtLeast (newLength);
398 T* cur = &fItems_[fLength_]; // point 1 past first guy
399 T* end = &fItems_[newLength]; // point 1 past last guy
400 if (newLength > fLength_) {
401 Assert (cur < end);
402 do {
403 new (cur) T{fillValue};
404 } while (++cur < end);
406 else {
407 Assert (cur >= end);
408 while (cur-- > end) {
409 destroy_at (cur);
410 }
411 }
412 fLength_ = newLength;
413 Invariant ();
414 }
415#if qStroika_Foundation_Debug_AssertionsChecked
416 template <typename T>
417 void Array<T>::Invariant_ () const noexcept
418 {
419#if qStroika_Foundation_Containers_DataStructures_Array_IncludeSlowDebugChecks_
421#endif
422 Assert ((fSlotsAllocated_ == 0) == (fItems_ == nullptr)); // always free iff slots alloced = 0
423 Assert (fLength_ <= fSlotsAllocated_);
424 }
425#endif
426 template <typename T>
427 inline Array<T>::~Array ()
428 {
429 clear (); // call destructors on elements
430 if constexpr (kUseMalloc_) {
431 if (fItems_ != nullptr) {
432 free (fItems_);
433 }
434 }
435 else {
436 delete[] (char*)fItems_;
437 }
438 }
439 template <typename T>
440 inline void Array<T>::MoveIteratorHereAfterClone (IteratorBase* pi, [[maybe_unused]] const Array<T>* movedFrom) const
441 {
443 RequireNotNull (pi);
444 RequireNotNull (movedFrom);
445 Require (pi->CurrentIndex () <= this->size ());
446 Require (pi->_fData == movedFrom);
447 pi->_fData = this;
448 }
449 template <typename T>
450 inline T* Array<T>::data () noexcept
451 {
453 return fItems_;
454 }
455 template <typename T>
456 inline const T* Array<T>::data () const noexcept
457 {
459 return fItems_;
460 }
461 template <typename T>
462 inline T Array<T>::GetAt (size_t i) const
463 {
465 Require (i >= 0);
466 Require (i < fLength_);
467 return fItems_[i];
468 }
469 template <typename T>
470 inline T* Array<T>::PeekAt (size_t i)
471 {
473 Require (i >= 0);
474 Require (i < fLength_);
475 return &fItems_[i];
476 }
477 template <typename T>
478 inline const T* Array<T>::PeekAt (size_t i) const
479 {
481 Require (i >= 0);
482 Require (i < fLength_);
483 return &fItems_[i];
484 }
485 template <typename T>
486 inline void Array<T>::SetAt (size_t i, ArgByValueType<T> item)
487 {
489 Require (i >= 0);
490 Require (i < fLength_);
491 fItems_[i] = item;
492 }
493 template <typename T>
494 inline T& Array<T>::operator[] (size_t i)
495 {
497 Require (i >= 0);
498 Require (i < fLength_);
499 return fItems_[i];
500 }
501 template <typename T>
502 inline T Array<T>::operator[] (size_t i) const
503 {
505 Require (i >= 0);
506 Require (i < fLength_);
507 return fItems_[i];
508 }
509 template <typename T>
510 inline size_t Array<T>::size () const
511 {
513 return fLength_;
514 }
515 template <typename T>
516 inline bool Array<T>::empty () const
517 {
519 return fLength_ == 0;
520 }
521 template <typename T>
522 inline size_t Array<T>::capacity () const
523 {
525 return fSlotsAllocated_;
526 }
527 template <typename T>
528 inline void Array<T>::shrink_to_fit ()
529 {
531 reserve (size ());
532 }
533 template <typename T>
534 inline void Array<T>::Remove (const ForwardIterator& i)
535 {
537 Require (not i.AtEnd ());
538 Require (i._fData == this); // assure iterator not stale
539 this->Remove (i.CurrentIndex ());
540 }
541 template <typename T>
543 {
545 Require (not i.AtEnd ());
546 Require (i._fData == this); // assure iterator not stale
547 size_t idx = i.CurrentIndex ();
548 this->Remove (idx);
549 if (idx == this->fLength_) {
550 return ForwardIterator{};
551 }
552 else {
553 return ForwardIterator{this, idx};
554 }
555 }
556 template <typename T>
557 inline void Array<T>::Remove (const BackwardIterator& i)
558 {
560 Require (not i.AtEnd ());
561 this->Remove (i.CurrentIndex ());
562 }
563 template <typename T>
564 inline void Array<T>::SetAt (const ForwardIterator& i, ArgByValueType<T> newValue)
565 {
567 Require (i._fData == this); // assure iterator not stale
568 Require (not i.AtEnd ());
569 SetAt (i.CurrentIndex (), newValue);
570 }
571 template <typename T>
572 inline void Array<T>::SetAt (const BackwardIterator& i, ArgByValueType<T> newValue)
573 {
575 Require (not i.AtEnd ());
576 SetAt (i.CurrentIndex (), newValue);
577 }
578
579 /*
580 ********************************************************************************
581 ****************************** Array<>::IteratorBase ***************************
582 ********************************************************************************
583 */
584 template <typename T>
585 inline Array<T>::IteratorBase::IteratorBase (const Array* data)
586 : _fData{data}
587 {
589 }
590#if qStroika_Foundation_Debug_AssertionsChecked
591 template <typename T>
592 inline Array<T>::IteratorBase::~IteratorBase ()
593 {
594#if qStroika_Foundation_Debug_AssertionsChecked
595 // hack so crash and debug easier
596 _fData = reinterpret_cast<Array<T>*> (-1);
597 _fCurrentIdx = numeric_limits<size_t>::max ();
598#endif
599 }
600#endif
601 template <typename T>
602 inline size_t Array<T>::IteratorBase::CurrentIndex () const
603 {
605 /*
606 * NB: This can be called if we are done - if so, it returns size().
607 */
608 Invariant ();
609 return _fCurrentIdx;
610 }
611 template <typename T>
612 inline const T& Array<T>::IteratorBase::operator* () const
613 {
615 Invariant ();
616 RequireNotNull (_fData);
617 Require (0 <= _fCurrentIdx and _fCurrentIdx < _fData->fLength_);
618 return _fData->fItems_[_fCurrentIdx];
619 }
620 template <typename T>
621 inline const T* Array<T>::IteratorBase::operator->() const
622 {
624 Invariant ();
625 RequireNotNull (_fData);
626 Require (0 <= _fCurrentIdx and _fCurrentIdx < _fData->fLength_);
627 return _fData->PeekAt (_fCurrentIdx);
628 }
629 template <typename T>
630 inline void Array<T>::IteratorBase::SetIndex (size_t i)
631 {
633 RequireNotNull (_fData);
634 Require (i <= _fData->fLength_);
635 _fCurrentIdx = i;
636 }
637 template <typename T>
638 inline auto Array<T>::IteratorBase::GetUnderlyingIteratorRep () const -> UnderlyingIteratorRep
639 {
641 /*
642 * NB: This can be called if we are done - if so, it returns size().
643 */
644 Invariant ();
645 return _fCurrentIdx;
646 }
647 template <typename T>
648 inline void Array<T>::IteratorBase::SetUnderlyingIteratorRep (UnderlyingIteratorRep i)
649 {
651 RequireNotNull (_fData);
652 Require (i <= _fData->fLength_);
653 _fCurrentIdx = i;
654 }
655 template <typename T>
657 {
658#if qStroika_Foundation_Debug_AssertionsChecked
659 Require (data == _fData);
660#endif
661 }
662 template <typename T>
663 inline void Array<T>::IteratorBase::Invariant () const noexcept
664 {
665#if qStroika_Foundation_Debug_AssertionsChecked
666 Invariant_ ();
667#endif
668 }
669#if qStroika_Foundation_Debug_AssertionsChecked
670 template <typename T>
671 void Array<T>::IteratorBase::Invariant_ () const noexcept
672 {
673 Assert (0 <= _fCurrentIdx);
674 if (_fData != nullptr) {
675#if qStroika_Foundation_Containers_DataStructures_Array_IncludeSlowDebugChecks_
677#endif
678 Assert (_fCurrentIdx <= _fData->fLength_);
679 }
680 }
681#endif
682
683 /*
684 ********************************************************************************
685 *************************** Array<T>::ForwardIterator **************************
686 ********************************************************************************
687 */
688 template <typename T>
690 : inherited{data}
691 {
693 this->_fCurrentIdx = startAt;
694 this->Invariant ();
695 }
696 template <typename T>
697 constexpr Array<T>::ForwardIterator::ForwardIterator (ForwardIterator&& src) noexcept
698 {
699 this->_fData = -src._fData;
700 this->_fCurrentIdx = src._fCurrentIdx;
701 src._fData = nullptr;
702 }
703 template <typename T>
705 {
706 return not AtEnd ();
707 }
708 template <typename T>
709 inline bool Array<T>::ForwardIterator::AtStart () const noexcept
710 {
711 if (this->_fData == nullptr) {
712 Assert (this->_fCurrentIdx == 0);
713 return true;
714 }
715#if qStroika_Foundation_Containers_DataStructures_Array_IncludeSlowDebugChecks_
717#endif
718 this->Invariant ();
719 return this->CurrentIndex () == 0;
720 }
721 template <typename T>
722 inline bool Array<T>::ForwardIterator::AtEnd () const noexcept
723 {
724 if (this->_fData == nullptr) {
725 Assert (this->_fCurrentIdx == 0);
726 return true;
727 }
728#if qStroika_Foundation_Containers_DataStructures_Array_IncludeSlowDebugChecks_
730#endif
731 this->Invariant ();
732 return this->CurrentIndex () == this->_fData->fLength_;
733 }
734 template <typename T>
735 inline auto Array<T>::ForwardIterator::operator++ () noexcept -> ForwardIterator&
736 {
738 Require (not this->AtEnd ());
739 this->Invariant ();
740 Assert (this->_fCurrentIdx < this->_fData->fLength_);
741 ++this->_fCurrentIdx;
742 this->Invariant ();
743 return *this;
744 }
745 template <typename T>
746 inline auto Array<T>::ForwardIterator::operator++ (int) noexcept -> ForwardIterator
747 {
748 ForwardIterator result = *this;
749 this->operator++ ();
750 return result;
751 }
752 template <typename T>
753 inline auto Array<T>::ForwardIterator::operator-- () noexcept -> ForwardIterator&
754 {
756 Require (not this->AtStart ()); // checks not==0, so decrement is safe
757 this->Invariant ();
758 Assert (this->_fCurrentIdx <= this->_fData->fLength_); // stepping back from AtEnd () (_fCurrentIdx == fLength_) is legal
759 --this->_fCurrentIdx;
760 this->Invariant ();
761 return *this;
762 }
763 template <typename T>
764 inline auto Array<T>::ForwardIterator::operator-- (int) noexcept -> ForwardIterator
765 {
766 ForwardIterator result = *this;
767 this->operator-- ();
768 return result;
769 }
770 template <typename T>
771 inline auto Array<T>::ForwardIterator::operator+ (difference_type i) const -> ForwardIterator
772 {
773 size_t newIdx = static_cast<difference_type> (this->_fCurrentIdx) + i;
774 Require (newIdx <= this->_fData->fLength_); // reaching exactly 'end' (== fLength_) is legal
775 return ForwardIterator{this->_fData, newIdx};
776 }
777 template <typename T>
778 inline auto Array<T>::ForwardIterator::operator- (difference_type i) const -> ForwardIterator
779 {
780 size_t newIdx = static_cast<difference_type> (this->_fCurrentIdx) - i;
781 Require (newIdx <= this->_fData->fLength_); // reaching exactly 'end' (== fLength_) is legal
782 return ForwardIterator{this->_fData, newIdx};
783 }
784 template <typename T>
785 inline auto Array<T>::ForwardIterator::operator+= (difference_type i) -> ForwardIterator&
786 {
787 this->_fCurrentIdx += i;
788 Require (this->_fCurrentIdx <= this->_fData->fLength_); // reaching exactly 'end' (== fLength_) is legal
789 return *this;
790 }
791 template <typename T>
792 inline auto Array<T>::ForwardIterator::operator-= (difference_type i) -> ForwardIterator&
793 {
794 this->_fCurrentIdx -= i;
795 Require (this->_fCurrentIdx <= this->_fData->fLength_); // reaching exactly 'end' (== fLength_) is legal (and cannot check < 0 since unsigned)
796 return *this;
797 }
798 template <typename T>
799 inline const T& Array<T>::ForwardIterator::operator[] (difference_type i) const
800 {
801 size_t newIdx = static_cast<difference_type> (this->_fCurrentIdx) + i;
802 Require (newIdx < this->_fData->fLength_);
803 return this->_fData->fItems_[newIdx];
804 }
805 template <typename T>
806 inline bool Array<T>::ForwardIterator::operator== (const ForwardIterator& rhs) const
807 {
808 auto thisDone = this->AtEnd ();
809 bool rhsDone = rhs.AtEnd ();
810 if (thisDone or rhsDone) {
811 return thisDone and rhsDone;
812 }
813 Require (this->_fData == rhs._fData);
814 return this->_fCurrentIdx == rhs._fCurrentIdx;
815 }
816 template <typename T>
817 inline strong_ordering Array<T>::ForwardIterator::operator<=> (const ForwardIterator& rhs) const
818 {
819 auto thisDone = this->AtEnd ();
820 bool rhsDone = rhs.AtEnd ();
821 if (thisDone) {
822 return rhsDone ? strong_ordering::equal : strong_ordering::less;
823 }
824 if (rhsDone) {
825 Assert (not thisDone);
826 return strong_ordering::greater;
827 }
828 Require (this->_fData == rhs._fData);
829 return this->_fCurrentIdx <=> rhs._fCurrentIdx;
830 }
831 template <typename T>
832 inline typename Array<T>::ForwardIterator operator+ (typename Array<T>::ForwardIterator::difference_type i,
833 const typename Array<T>::ForwardIterator& it)
834 {
835 return it + i; // commutative
836 }
837 template <typename T>
838 inline typename Array<T>::ForwardIterator operator- (typename Array<T>::ForwardIterator::difference_type i,
839 const typename Array<T>::ForwardIterator& it)
840 {
841 return -(it - i); // anti-commutative
842 }
843 template <typename T>
844 auto Array<T>::ForwardIterator::OPERATOR_MINUS_BWA_ (const ForwardIterator& lhs, const ForwardIterator& rhs) -> difference_type
845 {
846 using difference_type = typename Array<T>::ForwardIterator::difference_type;
847 // slightly tricky, because of 'sentinal' end cases
848 // if both at end, just return 0 diff;
849 // at least least ONE not at end, can use its 'data' field to get array length, and use that for any at-end iterators
850 // 'atend' iterators. Else just use given index (easy case).
851 // and careful to convert everything to difference_type to assure signed arithmetic.
852 if (lhs.AtEnd ()) {
853 if (rhs.AtEnd ()) {
854 return 0;
855 }
856 else {
857 difference_type lhsIdx = static_cast<difference_type> (rhs._fData->size ()); // not at end, so must have data
858 difference_type rhsIdx = static_cast<difference_type> (rhs.CurrentIndex ());
859 return lhsIdx - rhsIdx;
860 }
861 }
862 else {
863 if (rhs.AtEnd ()) {
864 difference_type lhsIdx = static_cast<difference_type> (lhs.CurrentIndex ());
865 difference_type rhsIdx = static_cast<difference_type> (lhs._fData->size ()); // not at end, so must have data
866 return lhsIdx - rhsIdx;
867 }
868 else {
869 difference_type lhsIdx = static_cast<difference_type> (lhs.CurrentIndex ());
870 difference_type rhsIdx = static_cast<difference_type> (rhs.CurrentIndex ());
871 return lhsIdx - rhsIdx;
872 }
873 }
875 return 0;
876 }
877
878 /*
879 ********************************************************************************
880 **************************** Array<T>::BackwardIterator ************************
881 ********************************************************************************
882 */
883 template <typename T>
884 inline Array<T>::BackwardIterator::BackwardIterator (const Array* data, UnderlyingIteratorRep startAt)
885 : inherited{data}
886 {
888 this->_fCurrent = startAt;
889 this->Invariant ();
890 }
891 template <typename T>
892 inline Array<T>::BackwardIterator::BackwardIterator (const Array* data)
893 : BackwardIterator{data->size () == 0 ? data->size () : data->size () - 1} // start on last item or at magic (at end) value
894 {
895 }
896 template <typename T>
897 inline bool Array<T>::BackwardIterator::AtEnd () const noexcept
898 {
899#if qStroika_Foundation_Containers_DataStructures_Array_IncludeSlowDebugChecks_
901#endif
902 this->Invariant ();
903 return bool (this->CurrentIndex () == this->_fData->fLength_); // a little queer/confusing, but in C++ only legal extra address is past end, one before start not legal
904 }
905 template <typename T>
906 inline auto Array<T>::BackwardIterator::operator++ () noexcept -> BackwardIterator&
907 {
909 Require (not this->AtEnd ());
910 this->Invariant ();
911 if (this->_fCurrent == this->_fStart) {
912 this->_fCurrent = this->_fEnd; // magic to indicate done
913 Ensure (this->AtEnd ());
914 }
915 else {
916 this->_fCurrent--;
917 Ensure (not this->AtEnd ());
918 }
919 this->Invariant ();
920 return *this;
921 }
922 template <typename T>
923 inline bool Array<T>::BackwardIterator::operator== (const BackwardIterator& rhs) const
924 {
925 auto thisDone = this->AtEnd ();
926 bool rhsDone = rhs.AtEnd ();
927 if (thisDone or rhsDone) {
928 return thisDone and rhsDone;
929 }
930 Require (this->_fData == rhs._fData);
931 return this->_fCurrentIdx == rhs._fCurrentIdx;
932 }
933
934}
#define RequireNotNull(p)
Definition Assertions.h:348
#define AssertNotReached()
Definition Assertions.h:356
friend ForwardIterator operator+(difference_type i, const ForwardIterator &it)
addition of iterator and int is commutative.
friend ForwardIterator operator-(difference_type i, const ForwardIterator &it)
difference of int and iterator is anti-commutative (so - (it - i))
constexpr void AssertDataMatches(const Array *data) const
Definition Array.inl:656
nonvirtual void Apply(FUNCTION &&doToElement) const
nonvirtual ForwardIterator Find(FUNCTION &&firstThat) const
nonvirtual void Remove(const ForwardIterator &i)
Definition Array.inl:534
nonvirtual void push_back(ArgByValueType< T > item)
STL-ish alias for Insert (size(), item)
Definition Array.inl:135
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:450
nonvirtual void SetAt(size_t i, ArgByValueType< T > item)
Definition Array.inl:486
nonvirtual void reserve(size_t slotsAlloced)
sets the reserved capacity to slotsAlloced
Definition Array.inl:250
nonvirtual void ReserveAtLeast(size_t slotsAlloced)
Definition Array.inl:319
nonvirtual void SetLength(size_t newLength, ArgByValueType< T > fillValue)
Definition Array.inl:388
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:542
unique_lock< AssertExternallySynchronizedChecker > WriteContext
Instantiate AssertExternallySynchronizedChecker::WriteContext to designate an area of code where prot...
shared_lock< const AssertExternallySynchronizedChecker > ReadContext
Instantiate AssertExternallySynchronizedChecker::ReadContext to designate an area of code where prote...
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,...
@ ePar
must synchronize shared data, can use mutex (or atomics), cuz each parallel execution in real thread
@ eSeq
default case - not parallelized
void ThrowIfNull(const Private_::ConstVoidStar &p, const HRESULT &hr)
Template specialization for ThrowIfNull (), for thing being thrown HRESULT - really throw HRESULTErro...