Stroika Library 3.0d24
 
Loading...
Searching...
No Matches
DoublyLinkedList.inl
1/*
2 * Copyright(c) Sophist Solutions, Inc. 1990-2026. All rights reserved
3 */
5
7
8// Would like to leave on by default but we just added and cannot afford to have debug builds get that slow
9#ifndef qStroika_Foundation_Containers_DataStructures_DoublyLinkedList_IncludeSlowDebugChecks_
10#define qStroika_Foundation_Containers_DataStructures_DoublyLinkedList_IncludeSlowDebugChecks_ 0
11#endif
12
13 /*
14 ********************************************************************************
15 ***************************** DoublyLinkedList<T>::Link_ ***********************
16 ********************************************************************************
17 */
18 template <typename T>
19 constexpr DoublyLinkedList<T>::Link_::Link_ (ArgByValueType<T> item, Link_* prev, Link_* next)
20 : fItem{item}
21 , fPrev{prev}
22 , fNext{next}
23 {
24 }
25
26 /*
27 ********************************************************************************
28 ******************************* DoublyLinkedList<T> ****************************
29 ********************************************************************************
30 */
31 template <typename T>
32 inline DoublyLinkedList<T>::DoublyLinkedList ()
33 {
34 Invariant ();
35 }
36 template <typename T>
37 DoublyLinkedList<T>::DoublyLinkedList (const DoublyLinkedList& src)
38 {
39 // The try/catch is REQUIRED, and is not the same case as operator=: a CTOR that throws leaves an
40 // object that never finished constructing, so ~DoublyLinkedList () is never run for it, so nothing
41 // else will ever free the links built so far. push_back () leaves the list fully linked after each
42 // element, so clear () can always walk it at the throw point.
43 try {
44 for (const Link_* cur = src.fHead_; cur != nullptr; cur = cur->fNext) {
45 push_back (cur->fItem);
46 }
47 }
48 catch (...) {
49 clear ();
50 throw;
51 }
52 Invariant ();
53 }
54 template <typename T>
55 DoublyLinkedList<T>::DoublyLinkedList (DoublyLinkedList&& src) noexcept
56 : fHead_{src.fHead_}
57 , fTail_{src.fTail_}
58 , fLength_{src.fLength_}
59 {
60 Invariant ();
61 src.fHead_ = nullptr;
62 src.fTail_ = nullptr;
63 src.fLength_ = 0;
64 src.Invariant ();
65 }
66 template <typename T>
67 inline DoublyLinkedList<T>::~DoublyLinkedList ()
68 {
69 /*
70 * This could be a little cheaper - we could avoid setting fHead_ pointer,
71 * but we must worry more about codeSize/re-use.
72 * That would involve a new function that COULD NOT BE INLINED.
73 *
74 * < I guess I could add a hack method - unadvertised - but has to be
75 * at least protected - and call it here to do what I've mentioned above >
76 */
77 Invariant ();
78 clear ();
79 Invariant ();
80 Ensure (size () == 0);
81 Ensure (fHead_ == nullptr);
82 Ensure (fTail_ == nullptr);
83 }
84 template <typename T>
85 inline void DoublyLinkedList<T>::Invariant () const noexcept
86 {
87#if qStroika_Foundation_Debug_AssertionsChecked
88 Invariant_ ();
89#endif
90 }
91 template <typename T>
92 inline bool DoublyLinkedList<T>::empty () const
93 {
94 AssertExternallySynchronizedChecker::ReadContext declareContext{*this};
95 return fHead_ == nullptr;
96 }
97 template <typename T>
98 inline size_t DoublyLinkedList<T>::size () const
99 {
100 AssertExternallySynchronizedChecker::ReadContext declareContext{*this};
101 return fLength_;
102 }
103 template <typename T>
104 inline optional<T> DoublyLinkedList<T>::GetFirst () const
105 {
106 AssertExternallySynchronizedChecker::ReadContext declareContext{*this};
107 return fHead_ == nullptr ? optional<T>{} : fHead_->fItem;
108 }
109 template <typename T>
110 inline optional<T> DoublyLinkedList<T>::GetLast () const
111 {
112 AssertExternallySynchronizedChecker::ReadContext declareContext{*this};
113 return fTail_ == nullptr ? optional<T>{} : fTail_->fItem;
114 }
115 template <typename T>
116 inline void DoublyLinkedList<T>::push_front (ArgByValueType<T> item)
117 {
119 Invariant ();
120 fHead_ = new Link_{item, nullptr, fHead_};
121 if (fHead_->fNext != nullptr) [[likely]] {
122 // backlink second item to first
123 fHead_->fNext->fPrev = fHead_;
124 }
125 if (fTail_ == nullptr) [[unlikely]] {
126 // if last is null, list was empty, so first==last now
127 fTail_ = fHead_;
128 }
129 ++fLength_;
130 Invariant ();
131 }
132 template <typename T>
133 template <Memory::ISpanOfT<T> SPAN_T>
134 void DoublyLinkedList<T>::push_front (const SPAN_T& copyFrom)
135 {
136 // @todo note could optimize this - slightly, setting tail and calling invariant etc at the end...
137 // push_front in reverse order cuz push_front reverses traversal order, and two wrongs make a right
138 for (auto ri = copyFrom.rbegin (); ri != copyFrom.rend (); ++ri) {
139 push_front (*ri);
140 }
141 }
142 template <typename T>
143 inline void DoublyLinkedList<T>::push_back (ArgByValueType<T> item)
146 Invariant ();
147 fTail_ = new Link_{item, fTail_, nullptr};
148 if (fTail_->fPrev != nullptr) [[likely]] {
149 // forward link second to last item to its prev
150 fTail_->fPrev->fNext = fTail_;
151 }
152 if (fHead_ == nullptr) [[unlikely]] {
153 // if head is null, list was empty, so first==last now
154 fHead_ = fTail_;
155 }
156 ++fLength_;
157 Invariant ();
158 }
159 template <typename T>
160 template <Memory::ISpanOfT<T> SPAN_T>
161 void DoublyLinkedList<T>::push_back (const SPAN_T& copyFrom)
162 {
163 // @todo note could optimize this - slightly, setting tail and calling invariant etc at the end...
164 for (auto i : copyFrom) {
165 push_back (i);
166 }
167 }
168 template <typename T>
170 {
172 RequireNotNull (fHead_);
173 Invariant ();
174 Link_* victim = fHead_;
175 Assert (victim->fPrev == nullptr); // cuz it was first..
176 /*
177 * Before:
178 * | | | | | |
179 * | V | | B | | C |
180 * | <-prev | | <-prev | | <-prev | ...
181 * | next-> | | next-> | | next-> |
182 * | | | | | |
183 *
184 * After:
185 * | | | |
186 * | B | | C |
187 * | <-prev | | <-prev | ...
188 * | next-> | | next-> |
189 * | | | |
190 */
191 fHead_ = victim->fNext; // First points to B
192 if (fHead_ == nullptr) {
193 Assert (victim == fTail_);
194 fTail_ = nullptr;
195 }
196 else {
197 Assert (fHead_->fPrev == victim);
198 fHead_->fPrev = nullptr; // B's prev is Nil since it is new first
199 }
200 delete victim;
201 --fLength_;
202 Invariant ();
203 }
204 template <typename T>
206 {
209 Invariant ();
210 Link_* victim = fTail_;
211 Assert (victim->fNext == nullptr); // cuz it was last..
212 /*
213 * Before:
214 * | | | | | |
215 * | A | | B | | V |
216 * ... | <-prev | | <-prev | | <-prev |
217 * | next-> | | next-> | | next-> |
218 * | | | | | |
219 *
220 * After:
221 * | | | |
222 * | A | | B |
223 * ... | <-prev | | <-prev |
224 * | next-> | | next-> |
225 * | | | |
226 */
227 fTail_ = victim->fPrev; // new last item
228 if (fTail_ == nullptr) {
229 Assert (fHead_ == victim);
230 fHead_ = nullptr;
231 }
232 else {
233 Assert (fTail_->fNext == victim);
234 fTail_->fNext = nullptr; // B's fNext is Nil since it is new last
235 }
236 delete victim;
237 --fLength_;
238 Invariant ();
239 }
240 template <typename T>
241 auto DoublyLinkedList<T>::operator= (const DoublyLinkedList& rhs) -> DoublyLinkedList&
242 {
244 Invariant ();
245 if (this != &rhs) [[likely]] {
246 clear ();
247 /*
248 * Built with push_back () rather than by hand-linking, which is what the copy CTOR does.
249 * The hand-rolled loop that used to be here linked only fNext - so fPrev was left null on
250 * every element and fTail_ was left as clear () left it - and it did not even compile
251 * (Link_ has a single 3-argument CTOR; that code passed two). It was never instantiated,
252 * because nothing in Stroika assigns a DoublyLinkedList, which is why none of that showed
253 * up. push_back () maintains fPrev, fTail_ and fLength_ correctly, and keeps fLength_
254 * consistent with the links if a T copy CTOR throws partway.
255 */
256 for (const Link_* cur = rhs.fHead_; cur != nullptr; cur = cur->fNext) {
257 push_back (cur->fItem);
258 }
259 }
260 Invariant ();
261 return *this;
262 }
263 template <typename T>
264 template <typename EQUALS_COMPARER>
265 void DoublyLinkedList<T>::Remove (ArgByValueType<T> item, EQUALS_COMPARER&& equalsComparer)
266 {
268 Invariant ();
269 /*
270 * Find, then delegate to Remove (ForwardIterator) - which already handles first/middle/last
271 * and the fPrev, fTail_ and fLength_ bookkeeping. This is what LinkedList<T> does.
272 *
273 * The hand-rolled unlink that used to be here got three things wrong: it dereferenced
274 * fHead_->fItem with no null check (so Remove () on an EMPTY list was a null dereference),
275 * it never fixed link->fNext->fPrev, and it never updated fTail_ when the removed element
276 * was the last - so a doubly-linked list stopped being doubly linked.
277 */
278 for (ForwardIterator it{this}; not it.AtEnd (); ++it) {
279 if (equalsComparer (*it, item)) {
280 this->Remove (it);
281 break;
282 }
283 }
284 Invariant ();
285 }
286 template <typename T>
287 template <typename EQUALS_COMPARER>
288 bool DoublyLinkedList<T>::Contains (ArgByValueType<T> item, EQUALS_COMPARER&& equalsComparer) const
289 {
290 AssertExternallySynchronizedChecker::ReadContext declareContext{*this};
291 for (const Link_* current = fHead_; current != nullptr; current = current->fNext) {
292 if (forward<EQUALS_COMPARER> (equalsComparer) (current->fItem, item)) {
293 return true;
294 }
295 }
296 return false;
297 }
298 template <typename T>
299 template <invocable<T> FUNCTION>
300 inline void DoublyLinkedList<T>::Apply (FUNCTION&& doToElement) const
301 {
302 AssertExternallySynchronizedChecker::ReadContext declareContext{*this};
303 for (const Link_* i = fHead_; i != nullptr; i = i->fNext) {
304 forward<FUNCTION> (doToElement) (i->fItem);
305 }
306 }
307 template <typename T>
308 template <typename FUNCTION>
309 inline auto DoublyLinkedList<T>::Find (FUNCTION&& firstThat) const -> UnderlyingIteratorRep
310 {
311 AssertExternallySynchronizedChecker::ReadContext declareContext{*this};
312 for (Link_* i = fHead_; i != nullptr; i = i->fNext) {
313 if (forward<FUNCTION> (firstThat) (i->fItem)) {
314 return i;
315 }
316 }
317 return nullptr;
318 }
319 template <typename T>
321 {
323 for (Link_* i = fHead_; i != nullptr;) {
324 Link_* deleteMe = i;
325 i = i->fNext;
326 delete deleteMe;
327 }
328 fHead_ = nullptr;
329 fTail_ = nullptr;
330 fLength_ = 0;
331 }
332 template <typename T>
333 T DoublyLinkedList<T>::GetAt (size_t i) const
334 {
335 AssertExternallySynchronizedChecker::ReadContext declareContext{*this};
336 Require (i >= 0);
337 Require (i < size ());
338 const Link_* cur = fHead_;
339 for (; i != 0; cur = cur->fNext, --i) {
340 AssertNotNull (cur); // cuz i <= fLength
341 }
342 AssertNotNull (cur); // cuz i <= fLength
343 return (cur->fItem);
344 }
345 template <typename T>
346 void DoublyLinkedList<T>::SetAt (size_t i, ArgByValueType<T> item)
347 {
349 Require (i >= 0);
350 Require (i < size ());
351 Link_* cur = fHead_;
352 for (; i != 0; cur = cur->fNext, --i) {
353 AssertNotNull (cur); // cuz i <= fLength
354 }
355 AssertNotNull (cur); // cuz i <= fLength
356 cur->fItem = item;
357 }
358 template <typename T>
359 inline void DoublyLinkedList<T>::MoveIteratorHereAfterClone (ForwardIterator* pi, const DoublyLinkedList<T>* movedFrom) const
360 {
361 AssertExternallySynchronizedChecker::ReadContext declareContext{*this};
362 // TRICKY TODO - BUT MUST DO - MUST MOVE FROM OLD ITER TO NEW
363 // only way
364 //
365 // For STL containers, not sure how to find an equiv new iterator for an old one, but my best guess is to iterate through
366 // old for old, and when I match, stop on new
367#if qStroika_Foundation_Debug_AssertionsChecked
368 Require (pi->fData_ == movedFrom);
369#endif
370 auto newI = this->fHead_;
371 [[maybe_unused]] auto newE = nullptr;
372 auto oldI = movedFrom->fHead_;
373 [[maybe_unused]] auto oldE = nullptr;
374 while (oldI != pi->_fCurrent) {
375 Assert (newI != newE);
376 Assert (oldI != oldE);
377 newI = newI->fNext;
378 oldI = oldI->fNext;
379 Assert (newI != newE);
380 Assert (oldI != oldE);
381 }
382 Assert (oldI == pi->_fCurrent);
383 pi->_fCurrent = newI;
384#if qStroika_Foundation_Debug_AssertionsChecked
385 pi->fData_ = this;
386#endif
387 }
388 template <typename T>
389 inline auto DoublyLinkedList<T>::begin () const -> ForwardIterator
390 {
391 AssertExternallySynchronizedChecker::ReadContext declareContext{*this};
392 return ForwardIterator{this};
393 }
394 template <typename T>
395 constexpr auto DoublyLinkedList<T>::end () const noexcept -> ForwardIterator
396 {
397 return ForwardIterator{};
398 }
399 template <typename T>
400 auto DoublyLinkedList<T>::erase (const ForwardIterator& i) -> ForwardIterator
401 {
402 ForwardIterator next = i;
403 ++next;
404 Remove (i);
405 next.Invariant ();
406 return next;
407 }
408 template <typename T>
409 void DoublyLinkedList<T>::Remove (const ForwardIterator& i)
410 {
412 Require (not i.AtEnd ());
413#if qStroika_Foundation_Debug_AssertionsChecked
414 Require (i.fData_ == this); // assure iterator not stale
415#endif
416 this->Invariant ();
417
418 const Link_* victim = i._fCurrent;
419 AssertNotNull (victim); // cuz not AtEnd
420 /*
421 * Before:
422 * | | | | | |
423 * | A | | V | | C |
424 * ... | <-prev | | <-prev | | <-prev | ...
425 * | next-> | | next-> | | next-> |
426 * | | | | | |
427 *
428 * After:
429 * | | | |
430 * | A | | C |
431 * ... | <-prev | | <-prev | ...
432 * | next-> | | next-> |
433 * | | | |
434 */
435 if (victim->fPrev == nullptr) {
436 // In this case 'A' does not exist - it is Nil...
437 Assert (fHead_ == victim);
438 fHead_ = victim->fNext; // 'C' is now first
439 if (fHead_ == nullptr) {
440 fTail_ = nullptr;
441 }
442 else {
443 Assert (fHead_->fPrev == victim); // Victim used to be 'C's prev
444 fHead_->fPrev = nullptr; // Now Nil!
445 }
446 }
447 else {
448 Assert (victim->fPrev->fNext == victim); // In this case 'A' DOES exist
449 victim->fPrev->fNext = victim->fNext; // Make A point to C
450 // Now make 'C' point back to A (careful if 'C' is Nil)
451 if (victim->fNext == nullptr) {
452 // In this case 'C' does not exist - it is Nil...
453 Assert (victim == fTail_);
454 fTail_ = victim->fPrev; // 'A' is now last
455 }
456 else {
457 Assert (victim->fNext->fPrev == victim); // Victim used to be 'C's prev
458 victim->fNext->fPrev = victim->fPrev; // Now 'A' is
459 }
460 }
461 delete victim;
462 --fLength_;
463 this->Invariant ();
464 }
465 template <typename T>
466 inline void DoublyLinkedList<T>::SetAt (const ForwardIterator& i, ArgByValueType<T> newValue)
467 {
469 Require (not i.AtEnd ());
470#if qStroika_Foundation_Debug_AssertionsChecked
471 Require (i.fData_ == this); // assure iterator not stale
472#endif
473 this->Invariant ();
474 const_cast<Link_*> (i._fCurrent)->fItem = newValue;
475 this->Invariant ();
476 }
477 template <typename T>
478 void DoublyLinkedList<T>::AddBefore (const ForwardIterator& i, ArgByValueType<T> newValue)
479 {
481#if qStroika_Foundation_Debug_AssertionsChecked
482 Require (i.fData_ == this); // assure iterator not stale
483#endif
484 /*
485 * NB: This code works fine, even if we are AtEnd!!!
486 */
487 this->Invariant ();
488 if (i._fCurrent == nullptr) {
489 /*
490 * NB: If I am past the last item on the list, AddBefore() is equivalent
491 * to Appending to the list.
492 */
493 Assert (i.AtEnd ());
494 push_back (newValue);
495 Assert (i.AtEnd ()); // what is done, cannot be undone!!!
496 }
497 else {
498 Link_* prev = i._fCurrent->fPrev;
499 if (prev == nullptr) {
500 push_front (newValue);
501 }
502 else {
503 /*
504 * | | | | | |
505 * | PREV | | NEW | | CUR |
506 * ... | <-prev | | <-prev | | <-prev | ...
507 * | next-> | | next-> | | next-> |
508 * | | | | | |
509 */
510 Assert (prev->fNext == i._fCurrent);
511 Link_* iteratorCurLink = const_cast<Link_*> (i._fCurrent);
512 prev->fNext = new Link_{newValue, prev, iteratorCurLink};
513 // Since fCurrent != nullptr from above, we update its prev, and don't have
514 // to worry about fTail_.
515 iteratorCurLink->fPrev = prev->fNext;
516 ++fLength_;
517 Assert (i._fCurrent->fPrev->fPrev == prev); // old prev is two back now...
518 }
519 }
520 this->Invariant ();
521 }
522 template <typename T>
523 inline void DoublyLinkedList<T>::AddAfter (const ForwardIterator& i, ArgByValueType<T> newValue)
524 {
526#if qStroika_Foundation_Debug_AssertionsChecked
527 Require (i.fData_ == this); // assure iterator not stale
528#endif
529 this->Invariant ();
530 Require (not i.AtEnd ());
531 AssertNotNull (i._fCurrent); // since not done...
532 Assert (fHead_ != nullptr);
533 /*
534 * | | | |
535 * | CUR | | NEW |
536 * ... | <-prev | | <-prev | ...
537 * | next-> | | next-> |
538 * | | | |
539 */
540 Link_* iteratorCurLink = const_cast<Link_*> (i._fCurrent);
541 Link_* newLink = new Link_{newValue, iteratorCurLink, iteratorCurLink->fNext};
542 iteratorCurLink->fNext = newLink;
543 if (newLink->fNext != nullptr) {
544 newLink->fNext->fPrev = newLink;
545 }
546 if (newLink->fNext == nullptr) {
547 fTail_ = newLink;
548 }
549 else {
550 Assert (newLink->fNext->fPrev == newLink); // cuz of params to new Link_...
551 }
552 ++fLength_;
553 this->Invariant ();
554 }
555#if qStroika_Foundation_Debug_AssertionsChecked
556 template <typename T>
557 void DoublyLinkedList<T>::Invariant_ () const noexcept
558 {
559#if qStroika_Foundation_Containers_DataStructures_DoublyLinkedList_IncludeSlowDebugChecks_
560 AssertExternallySynchronizedChecker::ReadContext declareContext{*this};
561#endif
562 if (fHead_ != nullptr) {
563 Assert (fHead_->fPrev == nullptr);
564 if (fHead_->fNext == nullptr) {
565 Assert (fHead_ == fTail_);
566 }
567 else {
568 Assert (fHead_->fNext->fPrev == fHead_);
569 }
570 }
571 if (fTail_ != nullptr) {
572 Assert (fTail_->fNext == nullptr);
573 if (fTail_->fPrev == nullptr) {
574 Assert (fHead_ == fTail_);
575 }
576 else {
577 Assert (fTail_->fPrev->fNext == fTail_);
578 }
579 }
580 {
581 // the cached length must agree with the links, or some mutator failed to maintain it
582 size_t n = 0;
583 for (const Link_* i = fHead_; i != nullptr; i = i->fNext) {
584 ++n;
585 }
586 Assert (n == fLength_);
587 }
588 Assert (fHead_ == nullptr or fHead_->fPrev == nullptr);
589 Assert (fTail_ == nullptr or fTail_->fNext == nullptr);
590
591 /*
592 * Check we are properly linked together.
593 */
594 size_t forwardCounter = 0;
595 for (Link_* i = fHead_; i != nullptr; i = i->fNext) {
596 Assert (i->fNext == nullptr or i->fNext->fPrev == i); // adjacent nodes point at each other
597 ++forwardCounter;
598 }
599 size_t backwardCounter{};
600 for (Link_* i = fTail_; i != nullptr; i = i->fPrev) {
601 Assert (i->fPrev == nullptr or i->fPrev->fNext == i); // adjacent nodes point at each other
602 ++backwardCounter;
603 }
604 Assert (forwardCounter == backwardCounter);
605 }
606#endif
607
608 /*
609 ********************************************************************************
610 ********************** DoublyLinkedList<T>::ForwardIterator ********************
611 ********************************************************************************
612 */
613 template <typename T>
614 constexpr DoublyLinkedList<T>::ForwardIterator::ForwardIterator ([[maybe_unused]] const DoublyLinkedList* data, UnderlyingIteratorRep startAt) noexcept
615 : _fCurrent{startAt}
616#if qStroika_Foundation_Debug_AssertionsChecked
617 , fData_{data}
618#endif
619 {
620 }
621 template <typename T>
622 constexpr DoublyLinkedList<T>::ForwardIterator::ForwardIterator (const DoublyLinkedList* data) noexcept
623 : ForwardIterator{data, (RequireExpression (data != nullptr), data->fHead_)}
624 {
625 }
626 template <typename T>
627 inline void DoublyLinkedList<T>::ForwardIterator::Invariant () const noexcept
628 {
629#if qStroika_Foundation_Debug_AssertionsChecked
630 Invariant_ ();
631#endif
632 }
633 template <typename T>
634 inline DoublyLinkedList<T>::ForwardIterator::operator bool () const
635 {
636 return not AtEnd ();
637 }
638 template <typename T>
639 inline bool DoublyLinkedList<T>::ForwardIterator::AtEnd () const noexcept
640 {
641#if qStroika_Foundation_Debug_AssertionsChecked
642 if (fData_ != nullptr) {
643 AssertExternallySynchronizedChecker::ReadContext declareContext{*fData_};
644 Invariant ();
645 }
646#endif
647 return _fCurrent == nullptr;
648 }
649 template <typename T>
650 inline auto DoublyLinkedList<T>::ForwardIterator::operator++ () noexcept -> ForwardIterator&
651 {
652 Require (not AtEnd ());
653 Invariant ();
654 Assert (_fCurrent != nullptr);
655 _fCurrent = _fCurrent->fNext;
656 Invariant ();
657 return *this;
658 }
659 template <typename T>
660 inline auto DoublyLinkedList<T>::ForwardIterator::operator++ (int) noexcept -> ForwardIterator
661 {
662 ForwardIterator result = *this;
663 this->operator++ ();
664 return result;
665 }
666 template <typename T>
667 inline const T& DoublyLinkedList<T>::ForwardIterator::operator* () const
668 {
669#if qStroika_Foundation_Debug_AssertionsChecked
670 RequireNotNull (fData_);
671 AssertExternallySynchronizedChecker::ReadContext declareContext{*fData_};
672#endif
673 Require (not AtEnd ());
674 Invariant ();
675 AssertNotNull (_fCurrent);
676 return _fCurrent->fItem;
677 }
678 template <typename T>
679 inline const T* DoublyLinkedList<T>::ForwardIterator::operator->() const
680 {
681#if qStroika_Foundation_Debug_AssertionsChecked
682 RequireNotNull (fData_);
683 AssertExternallySynchronizedChecker::ReadContext declareContext{*fData_};
684#endif
685 Require (not AtEnd ());
686 Invariant ();
687 AssertNotNull (_fCurrent);
688 return &_fCurrent->fItem;
689 }
690 template <typename T>
691 size_t DoublyLinkedList<T>::ForwardIterator::CurrentIndex (const DoublyLinkedList* data) const
692 {
693 Require (not AtEnd ());
694#if qStroika_Foundation_Debug_AssertionsChecked
695 Require (data == fData_);
696 RequireNotNull (fData_);
697#endif
698 RequireNotNull (this->_fCurrent);
699 size_t i = 0;
700 for (const Link_* l = data->fHead_;; l = l->fNext, ++i) {
701 AssertNotNull (l);
702 if (l == _fCurrent) [[unlikely]] {
703 return i;
704 }
705 }
707 return i;
708 }
709 template <typename T>
710 inline auto DoublyLinkedList<T>::ForwardIterator::GetUnderlyingIteratorRep () const -> UnderlyingIteratorRep
711 {
712 return _fCurrent;
713 }
714 template <typename T>
715 inline void DoublyLinkedList<T>::ForwardIterator::SetUnderlyingIteratorRep (UnderlyingIteratorRep l)
716 {
717#if qStroika_Foundation_Debug_AssertionsChecked
718 AssertExternallySynchronizedChecker::ReadContext declareContext{*fData_}; // read lock on data, though writing to this iterator
719#endif
720 // Note - MUST come from this DoublyLinkedList - could assert walking fData_ ... begin...end, and assert we find it!
721 // unless its (allowed) nullptr, which is a sentinal for the the empty / end iterator
722 _fCurrent = l;
723 }
724 template <typename T>
725 constexpr void DoublyLinkedList<T>::ForwardIterator::AssertDataMatches ([[maybe_unused]] const DoublyLinkedList* data) const
726 {
727#if qStroika_Foundation_Debug_AssertionsChecked
728 Require (data == fData_);
729#endif
730 }
731 template <typename T>
732 inline bool DoublyLinkedList<T>::ForwardIterator::operator== (const ForwardIterator& rhs) const
733 {
734 return _fCurrent == rhs._fCurrent;
735 }
736#if qStroika_Foundation_Debug_AssertionsChecked
737 template <typename T>
738 void DoublyLinkedList<T>::ForwardIterator::Invariant_ () const noexcept
739 {
740 if (fData_ != nullptr) {
741 // fData_->Invariant (); -- skip due to cost
742 }
743 }
744#endif
745
746 /*
747 ********************************************************************************
748 ***************** DoublyLinkedList<T>::BidirectionalIterator *******************
749 ********************************************************************************
750 */
751 template <typename T>
752 constexpr DoublyLinkedList<T>::BidirectionalIterator::BidirectionalIterator ([[maybe_unused]] const DoublyLinkedList* data,
753 UnderlyingIteratorRep startAt) noexcept
754 : inherited{data, startAt}
755 , fData_{data}
756 {
757 }
758 template <typename T>
759 constexpr DoublyLinkedList<T>::BidirectionalIterator::BidirectionalIterator (const DoublyLinkedList* data) noexcept
760 : inherited{data}
761 , fData_{data}
762 {
763 }
764 template <typename T>
765 inline bool DoublyLinkedList<T>::BidirectionalIterator::AtStart () const noexcept
766 {
767 if (this->_fCurrent == nullptr) {
768 // then at start IFF we are empty, cuz we are at the end
769 return this->fData_->empty ();
770 }
771 return this->_fCurrent->fPrev == nullptr;
772 }
773 template <typename T>
774 inline auto DoublyLinkedList<T>::BidirectionalIterator::operator++ () noexcept -> BidirectionalIterator&
775 {
776 inherited::operator++ ();
777 return *this;
778 }
779 template <typename T>
780 inline auto DoublyLinkedList<T>::BidirectionalIterator::operator++ (int) noexcept -> BidirectionalIterator
781 {
782 BidirectionalIterator result{*this};
783 this->operator++ ();
784 return result;
785 }
786 template <typename T>
787 inline auto DoublyLinkedList<T>::BidirectionalIterator::operator-- () noexcept -> BidirectionalIterator&
788 {
789 Require (not this->AtStart ());
790 this->Invariant ();
791 if (this->_fCurrent == nullptr) {
792 this->_fCurrent = this->fData_->fTail_;
793 }
794 else {
795 this->_fCurrent = this->_fCurrent->fPrev;
796 }
797 Assert (this->_fCurrent != nullptr); // after Require (not AtStart ()), stepping back must always land on a real node
798 this->Invariant ();
799 return *this;
800 }
801 template <typename T>
802 inline auto DoublyLinkedList<T>::BidirectionalIterator::operator-- (int) noexcept -> BidirectionalIterator
803 {
804 BidirectionalIterator result{*this};
805 this->operator-- ();
806 return result;
807 }
808 template <typename T>
809 inline auto DoublyLinkedList<T>::BidirectionalIterator::operator- (ptrdiff_t i) const -> BidirectionalIterator
810 {
811 BidirectionalIterator result{*this};
812 while (i > 0) {
813 --result;
814 --i;
815 }
816 while (i < 0) {
817 ++result;
818 ++i;
819 }
820 return result;
821 }
822
823}
#define AssertNotNull(p)
Definition Assertions.h:334
#define RequireNotNull(p)
Definition Assertions.h:348
#define RequireExpression(c)
Definition Assertions.h:268
#define AssertNotReached()
Definition Assertions.h:356
unique_lock< AssertExternallySynchronizedChecker > WriteContext
Instantiate AssertExternallySynchronizedChecker::WriteContext to designate an area of code where prot...