Stroika Library 3.0d18
 
Loading...
Searching...
No Matches
Array.inl
1/*
2 * Copyright(c) Sophist Solutions, Inc. 1990-2025. 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)
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
76 /*
77 * Delicate matter so that we assure ctors/dtors/op= called at
78 * right time.
79 */
80 size_t oldLength = fLength_;
81 SetLength (oldLength + 1, item); // Add space for extra item
82 if (index < oldLength) {
83 /*
84 * Slide items down, and add our new entry
85 */
86 Assert (fLength_ >= 2);
87 T* lhs = &fItems_[fLength_ - 1];
88 T* rhs = &fItems_[fLength_ - 2];
89 size_t i = fLength_ - 1;
91 for (; i > index; --i) {
92 *lhs-- = *rhs--;
93 }
94 Assert (i == index);
95 Assert (lhs == &fItems_[index]);
96 *lhs = item;
97 }
98 Invariant ();
99 }
100#endif
101 template <typename T>
102 template <Memory::ISpanOfT<T> SPAN_T>
103 void Array<T>::Insert (size_t at, const SPAN_T& copyFrom)
104 {
106 Require (at >= 0);
107 Require (at <= fLength_);
108 Invariant ();
109 size_t n2Add = copyFrom.size ();
110 if (n2Add != 0) [[likely]] {
111 size_t sz = size ();
112 size_t newSz = sz + n2Add;
113 ReserveAtLeast (newSz);
114#if qCompilerAndStdLib_MemoryInsertAt_Buggy
115 // maybe is a compiler bug - cuz no problem on g++-15 ubuntu 25.04
116 //temporary BWA til I find what is wrong with Memory::Insert () on gcc optimizer
117 for (size_t i = 0; i < copyFrom.size (); ++i) {
118 this->Insert_BWA (i + at, copyFrom[i]);
119 }
120#else
121 this->fLength_ = Memory::Insert (span{this->data (), sz}, span{this->data (), capacity ()}, at, copyFrom).size ();
122#endif
123 Assert (this->fLength_ == newSz);
124 }
125 Invariant ();
126 }
127 template <typename T>
128 inline void Array<T>::Insert (const ForwardIterator& i, ArgByValueType<T> newValue)
129 {
131 Require (i._fData == this); // assure iterator not stale
132 // i CAN BE DONE OR NOT
133 Insert (i.CurrentIndex (), newValue);
134 }
135 template <typename T>
136 inline void Array<T>::Insert (const BackwardIterator& i, ArgByValueType<T> newValue)
137 {
139 Require (i._fData == this); // assure iterator not stale
140 // i CAN BE DONE OR NOT
141 Insert (i.CurrentIndex (), newValue);
142 }
143 template <typename T>
144 inline void Array<T>::push_back (ArgByValueType<T> item)
145 {
146 Insert (this->size (), item);
147 }
148 template <typename T>
149 void Array<T>::Remove (size_t index) noexcept
150 {
152 Require (index >= 0);
153 Require (index < fLength_);
154 Invariant ();
155 (void)Memory::Remove (span{this->data (), size ()}, span{this->data (), capacity ()}, index, index + 1);
156 --fLength_;
157 Invariant ();
158 }
159 template <typename T>
160 void Array<T>::Remove (size_t from, size_t to) noexcept
161 {
163 Invariant ();
164 fLength_ = Memory::Remove (span{this->data (), size ()}, span{this->data (), capacity ()}, from, to).size ();
165 Invariant ();
166 }
167 template <typename T>
171 Invariant ();
172 T* p = &fItems_[0];
173 for (size_t i = fLength_; i > 0; --i, ++p) {
174 destroy_at (p);
175 }
176 fLength_ = 0;
177 Invariant ();
178 }
179 template <typename T>
180 template <invocable<T> FUNCTION>
181 inline void Array<T>::Apply (FUNCTION&& doToElement, Execution::SequencePolicy seq) const
182 {
184 const T* start = &fItems_[0];
185 const T* end = &fItems_[fLength_];
186 switch (seq) {
187 case Execution::SequencePolicy::eSeq:
188 for_each (start, end, forward<FUNCTION> (doToElement));
189 break;
190 default:
191#if __cpp_lib_execution >= 201603L
192 for_each (execution::par, start, end, forward<FUNCTION> (doToElement));
193#else
194 for_each (start, end, forward<FUNCTION> (doToElement));
195#endif
196 break;
197 }
198 }
199 template <typename T>
200 inline auto Array<T>::begin () const -> ForwardIterator
201 {
202 return ForwardIterator{this, 0};
203 }
204 template <typename T>
205 constexpr auto Array<T>::end () const -> ForwardIterator
206 {
207 return ForwardIterator{};
208 }
209 template <typename T>
210 template <predicate<T> FUNCTION>
211 auto Array<T>::Find (FUNCTION&& firstThat) const -> ForwardIterator
212 {
213 Debug::AssertExternallySynchronizedMutex::ReadContext declareContext{*this};
214 const T* start = &fItems_[0];
215 const T* i = start;
216 const T* last = &fItems_[fLength_];
217 for (; i < last; ++i) {
218 if (forward<FUNCTION> (firstThat) (*i)) {
219 return ForwardIterator{this, static_cast<size_t> (i - start)};
220 }
221 }
222 return end ();
223 }
224 template <typename T>
225 template <typename EQUALS_COMPARER>
226 const T* Array<T>::Find (ArgByValueType<T> item, EQUALS_COMPARER&& equalsComparer) const
227 {
228 const T* start = &fItems_[0];
229 const T* last = &fItems_[fLength_];
230 for (const T* i = start; i < last; ++i) {
231 if (forward<EQUALS_COMPARER> (equalsComparer) (i->fItem, item)) {
232 return &i->fItem;
233 }
234 }
235 return nullptr;
236 }
237 template <typename T>
238 template <typename EQUALS_COMPARER>
239 T* Array<T>::Find (ArgByValueType<T> item, EQUALS_COMPARER&& equalsComparer)
240 {
241 const T* start = &fItems_[0];
242 const T* last = &fItems_[fLength_];
243 for (const T* i = start; i < last; ++i) {
244 if (forward<EQUALS_COMPARER> (equalsComparer) (i->fItem, item)) {
245 return &i->fItem;
246 }
247 }
248 return nullptr;
249 }
250 template <typename T>
251 void Array<T>::reserve (size_t slotsAlloced)
252 {
253 /*
254 */
256 Require (size () <= slotsAlloced);
257 Invariant ();
258 if (fSlotsAllocated_ != slotsAlloced) {
259 if (slotsAlloced == 0) {
260 if constexpr (kUseMalloc_) {
261 if (fItems_ != nullptr) {
262 free (fItems_);
263 fItems_ = nullptr;
264 }
265 }
266 else {
267 delete[] (char*)fItems_;
268 fItems_ = nullptr;
269 }
270 }
271 else {
272 /*
273 * We should consider getting rid of use of realloc since it prohibits
274 * internal pointers. For example, we cannot have an array of patchable_arrays.
275 */
276 if (fItems_ == nullptr) {
277 if constexpr (kUseMalloc_) {
278 fItems_ = (T*)malloc (sizeof (T) * slotsAlloced);
279 Execution::ThrowIfNull (fItems_);
280 }
281 else {
282 fItems_ = (T*)new char[sizeof (T) * slotsAlloced];
283 }
285 else {
286 if constexpr (kUseMalloc_) {
287 auto newVal = (T*)realloc (fItems_, sizeof (T) * slotsAlloced);
288 Execution::ThrowIfNull (newVal);
289 fItems_ = newVal;
290 }
291 else {
292 // do better, but for now at least do something SAFE
293 // USE SFINAE IsTriviallyCopyable to see which way to do it (if can use realloc).
294 // ALSO - on windoze - use _expand() if available...
295 T* newV = (T*)new char[sizeof (T) * slotsAlloced];
296 try {
297 size_t n2Copy = fLength_;
298 uninitialized_copy_n (&fItems_[0], n2Copy, newV);
299 }
300 catch (...) {
301 delete[] (char*)newV;
302 throw;
303 }
304 {
305 T* end = &fItems_[fLength_];
306 for (T* p = &fItems_[0]; p != end; ++p) {
307 destroy_at (p);
308 }
309 }
310 delete[] (char*)fItems_;
311 fItems_ = newV;
312 }
313 }
314 }
315 fSlotsAllocated_ = slotsAlloced;
316 }
317 Invariant ();
318 }
319 template <typename T>
320 inline void Array<T>::ReserveAtLeast (size_t slotsAlloced)
321 {
322 if (slotsAlloced > capacity ()) [[unlikely]] {
323 /*
324 * Bump up Slots alloced to be at least big enuf for our
325 * new length. We could be minimalistic here, and just bump up
326 * exactly, but this function can be expensive because it calls
327 * realloc which could cause lots of memory copying. There are two
328 * plausible strategies for bumping up memory in big chunks-
329 * rounding up, and scaling up.
330 */
331 reserve (Support::ReserveTweaks::GetScaledUpCapacity (slotsAlloced, sizeof (T)));
332 }
333 Ensure (size () <= capacity ());
334 Ensure (capacity () >= slotsAlloced);
335 }
336 template <typename T>
337 auto Array<T>::operator= (const Array& list) -> Array&
338 {
340 Invariant ();
341 size_t newLength = list.size ();
342
343 // @todo CLEANUP using std::copy and uninitialized_copy and range::destroy (or iterator range destory)
344
345 /*
346 * In case user already set this, we should not unset,
347 * but must be sure we are big enuf. Do this before we store any pointers
348 * cuz it could invalidate them.
349 */
350 reserve (max (capacity (), newLength));
351
352 /*
353 * Copy array elements where both sides where constructed.
354 */
355 size_t commonLength = Stroika::Foundation::min (fLength_, newLength);
356 T* lhs = &fItems_[0];
357 T* rhs = &list.fItems_[0];
358 for (size_t i = commonLength; i-- > 0;) {
359 *lhs++ = *rhs++;
360 }
361
362 /*
363 * Now if new length smaller, we must destroy entries at the end, and
364 * otherwise we must copy in new entries.
365 */
366 Assert (lhs == &fItems_[commonLength]); // point 1 past first guy to destroy/overwrite
367 if (fLength_ > newLength) {
368 T* end = &fItems_[fLength_]; // point 1 past last old guy
369 /*
370 * Then we must destruct entries at the end.
371 */
372 Assert (lhs < end);
373 do {
374 destroy_at (lhs);
375 } while (++lhs < end);
376 }
377 else if (fLength_ < newLength) {
378 T* end = &fItems_[newLength]; // point 1 past last new guy
379 Assert (lhs < end);
380 do {
381 new (lhs) T{*rhs++};
382 } while (++lhs < end);
383 }
384 fLength_ = newLength;
385 Invariant ();
386 return *this;
387 }
388 template <typename T>
389 void Array<T>::SetLength (size_t newLength, ArgByValueType<T> fillValue)
390 {
392 Invariant ();
393
394 /*
395 * Safe to grow the memory, but not to shrink it here, since
396 * we may need to destruct guys in the shrinking case.
397 */
398 ReserveAtLeast (newLength);
399 T* cur = &fItems_[fLength_]; // point 1 past first guy
400 T* end = &fItems_[newLength]; // point 1 past last guy
401 if (newLength > fLength_) {
402 Assert (cur < end);
403 do {
404 new (cur) T{fillValue};
405 } while (++cur < end);
406 }
407 else {
408 Assert (cur >= end);
409 while (cur-- > end) {
410 destroy_at (cur);
411 }
412 }
413 fLength_ = newLength;
414 Invariant ();
415 }
416#if qStroika_Foundation_Debug_AssertionsChecked
417 template <typename T>
418 void Array<T>::Invariant_ () const noexcept
419 {
420#if qStroika_Foundation_Containers_DataStructures_Array_IncludeSlowDebugChecks_
422#endif
423 Assert ((fSlotsAllocated_ == 0) == (fItems_ == nullptr)); // always free iff slots alloced = 0
424 Assert (fLength_ <= fSlotsAllocated_);
425 }
426#endif
427 template <typename T>
428 inline Array<T>::~Array ()
429 {
430 clear (); // call destructors on elements
431 if constexpr (kUseMalloc_) {
432 if (fItems_ != nullptr) {
433 free (fItems_);
435 }
436 else {
437 delete[] (char*)fItems_;
438 }
439 }
440 template <typename T>
441 inline void Array<T>::MoveIteratorHereAfterClone (IteratorBase* pi, [[maybe_unused]] const Array<T>* movedFrom) const
442 {
444 RequireNotNull (pi);
445 RequireNotNull (movedFrom);
446 Require (pi->CurrentIndex () <= this->size ());
447 Require (pi->_fData == movedFrom);
448 pi->_fData = this;
449 }
450 template <typename T>
451 inline T* Array<T>::data () noexcept
452 {
454 return fItems_;
455 }
456 template <typename T>
457 inline const T* Array<T>::data () const noexcept
458 {
460 return fItems_;
461 }
462 template <typename T>
463 inline T Array<T>::GetAt (size_t i) const
464 {
466 Require (i >= 0);
467 Require (i < fLength_);
468 return fItems_[i];
469 }
470 template <typename T>
471 inline T* Array<T>::PeekAt (size_t i)
472 {
474 Require (i >= 0);
475 Require (i < fLength_);
476 return &fItems_[i];
477 }
478 template <typename T>
479 inline const T* Array<T>::PeekAt (size_t i) const
480 {
482 Require (i >= 0);
483 Require (i < fLength_);
484 return &fItems_[i];
485 }
486 template <typename T>
487 inline void Array<T>::SetAt (size_t i, ArgByValueType<T> item)
488 {
490 Require (i >= 0);
491 Require (i < fLength_);
492 fItems_[i] = item;
493 }
494 template <typename T>
495 inline T& Array<T>::operator[] (size_t i)
496 {
498 Require (i >= 0);
499 Require (i < fLength_);
500 return fItems_[i];
501 }
502 template <typename T>
503 inline T Array<T>::operator[] (size_t i) const
504 {
506 Require (i >= 0);
507 Require (i < fLength_);
508 return fItems_[i];
509 }
510 template <typename T>
511 inline size_t Array<T>::size () const
512 {
514 return fLength_;
515 }
516 template <typename T>
517 inline bool Array<T>::empty () const
518 {
520 return fLength_ == 0;
521 }
522 template <typename T>
523 inline size_t Array<T>::capacity () const
524 {
526 return fSlotsAllocated_;
527 }
528 template <typename T>
529 inline void Array<T>::shrink_to_fit ()
530 {
532 reserve (size ());
533 }
534 template <typename T>
535 inline void Array<T>::Remove (const ForwardIterator& i)
536 {
538 Require (not i.Done ());
539 Require (i._fData == this); // assure iterator not stale
540 this->Remove (i.CurrentIndex ());
541 }
542 template <typename T>
544 {
546 Require (not i.Done ());
547 Require (i._fData == this); // assure iterator not stale
548 size_t idx = i.CurrentIndex ();
549 this->Remove (idx);
550 if (idx == this->fLength_) {
551 return ForwardIterator{};
552 }
553 else {
554 return ForwardIterator{this, idx};
555 }
556 }
557 template <typename T>
558 inline void Array<T>::Remove (const BackwardIterator& i)
559 {
561 Require (not i.Done ());
562 this->Remove (i.CurrentIndex ());
563 }
564 template <typename T>
565 inline void Array<T>::SetAt (const ForwardIterator& i, ArgByValueType<T> newValue)
566 {
568 Require (i._fData == this); // assure iterator not stale
569 Require (not i.Done ());
570 SetAt (i.CurrentIndex (), newValue);
571 }
572 template <typename T>
573 inline void Array<T>::SetAt (const BackwardIterator& i, ArgByValueType<T> newValue)
574 {
575 Debug::AssertExternallySynchronizedMutex::WriteContext declareContext{*this};
576 Require (not i.Done ());
577 SetAt (i.CurrentIndex (), newValue);
578 }
579
580 /*
581 ********************************************************************************
582 ****************************** Array<>::IteratorBase ***************************
583 ********************************************************************************
584 */
585 template <typename T>
586 inline Array<T>::IteratorBase::IteratorBase (const Array* data)
587 : _fData{data}
588 {
590 }
591#if qStroika_Foundation_Debug_AssertionsChecked
592 template <typename T>
593 inline Array<T>::IteratorBase::~IteratorBase ()
594 {
595#if qStroika_Foundation_Debug_AssertionsChecked
596 // hack so crash and debug easier
597 _fData = reinterpret_cast<Array<T>*> (-1);
598 _fCurrentIdx = numeric_limits<size_t>::max ();
599#endif
600 }
601#endif
602 template <typename T>
603 inline size_t Array<T>::IteratorBase::CurrentIndex () const
604 {
606 /*
607 * NB: This can be called if we are done - if so, it returns size().
608 */
609 Invariant ();
610 return _fCurrentIdx;
611 }
612 template <typename T>
613 inline const T& Array<T>::IteratorBase::operator* () const
614 {
616 Invariant ();
617 RequireNotNull (_fData);
618 Require (0 <= _fCurrentIdx and _fCurrentIdx < _fData->fLength_);
619 return _fData->fItems_[_fCurrentIdx];
620 }
621 template <typename T>
622 inline const T* Array<T>::IteratorBase::operator->() const
623 {
625 Invariant ();
626 RequireNotNull (_fData);
627 Require (0 <= _fCurrentIdx and _fCurrentIdx < _fData->fLength_);
628 return _fData->PeekAt (_fCurrentIdx);
629 }
630 template <typename T>
631 inline void Array<T>::IteratorBase::SetIndex (size_t i)
632 {
634 RequireNotNull (_fData);
635 Require (i <= _fData->fLength_);
636 _fCurrentIdx = i;
637 }
638 template <typename T>
639 inline auto Array<T>::IteratorBase::GetUnderlyingIteratorRep () const -> UnderlyingIteratorRep
640 {
642 /*
643 * NB: This can be called if we are done - if so, it returns size().
644 */
645 Invariant ();
646 return _fCurrentIdx;
647 }
648 template <typename T>
649 inline void Array<T>::IteratorBase::SetUnderlyingIteratorRep (UnderlyingIteratorRep i)
650 {
652 RequireNotNull (_fData);
653 Require (i <= _fData->fLength_);
654 _fCurrentIdx = i;
655 }
656 template <typename T>
658 {
659#if qStroika_Foundation_Debug_AssertionsChecked
660 Require (data == _fData);
661#endif
662 }
663 template <typename T>
664 inline void Array<T>::IteratorBase::Invariant () const noexcept
665 {
666#if qStroika_Foundation_Debug_AssertionsChecked
667 Invariant_ ();
668#endif
669 }
670#if qStroika_Foundation_Debug_AssertionsChecked
671 template <typename T>
672 void Array<T>::IteratorBase::Invariant_ () const noexcept
673 {
674 Assert (0 <= _fCurrentIdx);
675 if (_fData != nullptr) {
676#if qStroika_Foundation_Containers_DataStructures_Array_IncludeSlowDebugChecks_
678#endif
679 Assert (_fCurrentIdx <= _fData->fLength_);
680 }
681 }
682#endif
683
684 /*
685 ********************************************************************************
686 *************************** Array<T>::ForwardIterator **************************
687 ********************************************************************************
688 */
689 template <typename T>
691 : inherited{data}
692 {
693 Debug::AssertExternallySynchronizedMutex::ReadContext declareContext{*this->_fData};
694 this->_fCurrentIdx = startAt;
695 this->Invariant ();
696 }
697 template <typename T>
698 constexpr Array<T>::ForwardIterator::ForwardIterator (ForwardIterator&& src) noexcept
699 {
700 this->_fData = -src._fData;
701 this->_fCurrentIdx = src._fCurrentIdx;
702 src._fData = nullptr;
703 }
704 template <typename T>
706 {
707 return not Done ();
708 }
709 template <typename T>
710 inline bool Array<T>::ForwardIterator::Done () const noexcept
711 {
712 if (this->_fData == nullptr) {
713 Assert (this->_fCurrentIdx == 0);
714 return true;
715 }
716#if qStroika_Foundation_Containers_DataStructures_Array_IncludeSlowDebugChecks_
718#endif
719 this->Invariant ();
720 return this->CurrentIndex () == this->_fData->fLength_;
721 }
722 template <typename T>
723 inline auto Array<T>::ForwardIterator::operator++ () noexcept -> ForwardIterator&
724 {
725 Debug::AssertExternallySynchronizedMutex::ReadContext declareContext{*this->_fData};
726 Require (not this->Done ());
727 this->Invariant ();
728 Assert (this->_fCurrentIdx < this->_fData->fLength_);
729 ++this->_fCurrentIdx;
730 this->Invariant ();
731 return *this;
732 }
733 template <typename T>
734 inline auto Array<T>::ForwardIterator::operator++ (int) noexcept -> ForwardIterator
735 {
736 ForwardIterator result = *this;
737 this->operator++ ();
738 return result;
739 }
740 template <typename T>
741 inline bool Array<T>::ForwardIterator::operator== (const ForwardIterator& rhs) const
742 {
743 auto thisDone = this->Done ();
744 bool rhsDone = rhs.Done ();
745 if (thisDone or rhsDone) {
746 return thisDone and rhsDone;
747 }
748 Require (this->_fData == rhs._fData);
749 return this->_fCurrentIdx == rhs._fCurrentIdx;
750 }
751
752 /*
753 ********************************************************************************
754 **************************** Array<T>::BackwardIterator ************************
755 ********************************************************************************
756 */
757 template <typename T>
758 inline Array<T>::BackwardIterator::BackwardIterator (const Array* data, UnderlyingIteratorRep startAt)
759 : inherited{data}
760 {
761 Debug::AssertExternallySynchronizedMutex::ReadContext declareContext{*this->_fData};
762 this->_fCurrent = startAt;
763 this->Invariant ();
764 }
765 template <typename T>
766 inline Array<T>::BackwardIterator::BackwardIterator (const Array* data)
767 : BackwardIterator{data->size () == 0 ? data->size () : data->size () - 1} // start on last item or at magic (at end) value
768 {
769 }
770 template <typename T>
771 inline bool Array<T>::BackwardIterator::Done () const noexcept
772 {
773#if qStroika_Foundation_Containers_DataStructures_Array_IncludeSlowDebugChecks_
775#endif
776 this->Invariant ();
777 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
778 }
779 template <typename T>
780 inline auto Array<T>::BackwardIterator::operator++ () noexcept -> BackwardIterator&
781 {
782 Debug::AssertExternallySynchronizedMutex::ReadContext declareContext{*this->_fData};
783 Require (not this->Done ());
784 this->Invariant ();
785 if (this->_fCurrent == this->_fStart) {
786 this->_fCurrent = this->_fEnd; // magic to indicate done
787 Ensure (this->Done ());
788 }
789 else {
790 this->_fCurrent--;
791 Ensure (not this->Done ());
792 }
793 this->Invariant ();
794 return *this;
795 }
796 template <typename T>
797 inline bool Array<T>::BackwardIterator::operator== (const BackwardIterator& rhs) const
798 {
799 auto thisDone = this->Done ();
800 bool rhsDone = rhs.Done ();
801 if (thisDone or rhsDone) {
802 return thisDone and rhsDone;
803 }
804 Require (this->_fData == rhs._fData);
805 return this->_fCurrentIdx == rhs._fCurrentIdx;
806 }
807
808}
#define RequireNotNull(p)
Definition Assertions.h:347
constexpr void AssertDataMatches(const Array *data) const
Definition Array.inl:657
nonvirtual T * data() noexcept
returns internal pointer to data - which is unsynchronized, and only guaranteed valid until the next ...
Definition Array.inl:451
shared_lock< const AssertExternallySynchronizedMutex > ReadContext
Instantiate AssertExternallySynchronizedMutex::ReadContext to designate an area of code where protect...
unique_lock< AssertExternallySynchronizedMutex > WriteContext
Instantiate AssertExternallySynchronizedMutex::WriteContext to designate an area of code where protec...
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,...