Stroika Library 3.0d18
 
Loading...
Searching...
No Matches
LinkedList.inl
1/*
2 * Copyright(c) Sophist Solutions, Inc. 1990-2025. All rights reserved
3 */
4#include <optional>
5
7
9
10// Would like to leave on by default but we just added and cannot afford to have debug builds get that slow
11#ifndef qStroika_Foundation_Containers_DataStructures_LinkedList_IncludeSlowDebugChecks_
12#define qStroika_Foundation_Containers_DataStructures_LinkedList_IncludeSlowDebugChecks_ 0
13#endif
14
15 /*
16 ********************************************************************************
17 ************************ LinkedList<T,TRAITS>::Link_ ***************************
18 ********************************************************************************
19 */
20 template <typename T>
21 constexpr LinkedList<T>::Link_::Link_ (ArgByValueType<T> item, Link_* next)
22 : fItem{item}
23 , fNext{next}
24 {
25 }
26
27 /*
28 ********************************************************************************
29 **************************** LinkedList<T,TRAITS> ******************************
30 ********************************************************************************
31 */
32 template <typename T>
33 inline LinkedList<T>::LinkedList ()
34 {
35 Invariant ();
36 }
37 template <typename T>
38 inline LinkedList<T>::LinkedList (LinkedList&& src)
39 : fHead_{src.fHead_}
40 {
41 src.fHead_ = nullptr;
42 Invariant ();
43 src.Invariant ();
44 }
45 template <typename T>
46 LinkedList<T>::LinkedList (const LinkedList& src)
47 {
48 /*
49 * Copy the link list by keeping a pointer to the new current and new
50 * previous, and sliding them along in parallel as we construct the
51 * new list. Only do this if we have at least one element - then we
52 * don't have to worry about the head of the list, or nullptr ptrs, etc - that
53 * case is handled outside, before the loop.
54 */
55 if (src.fHead_ != nullptr) {
56 fHead_ = new Link_{src.fHead_->fItem, nullptr};
57 Link_* newCur = fHead_;
58 for (const Link_* cur = src.fHead_->fNext; cur != nullptr; cur = cur->fNext) {
59 Link_* newPrev = newCur;
60 newCur = new Link_{cur->fItem, nullptr};
61 newPrev->fNext = newCur;
62 }
63 }
64 Invariant ();
65 }
66 template <typename T>
67 inline LinkedList<T>::~LinkedList ()
68 {
69 /*
70 * This could be a little cheaper - we could avoid setting fLength field,
71 * and fHead_ pointer, 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 (fHead_ == nullptr);
81 }
82 template <typename T>
84 {
86 Invariant ();
87 if (this != &rhs) {
88 clear ();
89 /*
90 * Copy the link list by keeping a point to the new current and new
91 * previous, and sliding them along in parallel as we construct the
92 * new list. Only do this if we have at least one element - then we
93 * don't have to worry about the head of the list, or nullptr ptrs, etc - that
94 * case is handled outside, before the loop.
95 */
96 if (rhs.fHead_ != nullptr) {
97 fHead_ = new Link_{rhs.fHead_->fItem, nullptr};
98 Link_* newCur = fHead_;
99 for (const Link_* cur = rhs.fHead_->fNext; cur != nullptr; cur = cur->fNext) {
100 Link_* newPrev = newCur;
101 newCur = new Link_{cur->fItem, nullptr};
102 newPrev->fNext = newCur;
103 }
104 }
105 }
106 Invariant ();
107 return *this;
109 template <typename T>
110 inline void LinkedList<T>::Invariant () const noexcept
111 {
112#if qStroika_Foundation_Debug_AssertionsChecked
113 Invariant_ ();
114#endif
115 }
116 template <typename T>
117 inline void LinkedList<T>::MoveIteratorHereAfterClone (ForwardIterator* pi, const LinkedList* movedFrom) const
118 {
119 AssertExternallySynchronizedMutex::ReadContext declareContext{*this};
120 // TRICKY TODO - BUT MUST DO - MUST MOVE FROM OLD ITER TO NEW
121 // only way
122 //
123 // For STL containers, not sure how to find an equiv new iterator for an old one, but my best guess is to iterate through
124 // old for old, and when I match, stop on new
125#if qStroika_Foundation_Debug_AssertionsChecked
126 Require (pi->fData_ == movedFrom);
127#endif
128 auto newI = this->fHead_;
129 [[maybe_unused]] auto newE = nullptr;
130 auto oldI = movedFrom->fHead_;
131 [[maybe_unused]] auto oldE = nullptr;
132 while (oldI != pi->fCurrent_) {
133 Assert (newI != newE);
134 Assert (oldI != oldE);
135 newI = newI->fNext;
136 oldI = oldI->fNext;
137 Assert (newI != newE);
138 Assert (oldI != oldE);
139 }
140 Assert (oldI == pi->fCurrent_);
141 pi->fCurrent_ = newI;
142#if qStroika_Foundation_Debug_AssertionsChecked
143 pi->fData_ = this;
144#endif
145 }
146 template <typename T>
147 inline auto LinkedList<T>::begin () const -> ForwardIterator
148 {
149 AssertExternallySynchronizedMutex::ReadContext declareContext{*this};
150 return ForwardIterator{this};
151 }
152 template <typename T>
153 constexpr auto LinkedList<T>::end () const noexcept -> ForwardIterator
154 {
155 return ForwardIterator{};
156 }
157 template <typename T>
158 inline bool LinkedList<T>::empty () const
159 {
160 AssertExternallySynchronizedMutex::ReadContext declareContext{*this};
161 return fHead_ == nullptr;
162 }
163 template <typename T>
164 inline size_t LinkedList<T>::size () const
165 {
166 AssertExternallySynchronizedMutex::ReadContext declareContext{*this};
167 size_t n = 0;
168 for (const Link_* i = fHead_; i != nullptr; i = i->fNext) {
169 ++n;
170 }
171 return n;
172 }
173 template <typename T>
174 inline optional<T> LinkedList<T>::GetFirst () const
175 {
176 AssertExternallySynchronizedMutex::ReadContext declareContext{*this};
177 return fHead_ == nullptr ? optional<T>{} : fHead_->fItem;
178 }
179 template <typename T>
180 inline void LinkedList<T>::push_front (ArgByValueType<T> item)
181 {
183 Invariant ();
184 fHead_ = new Link_{item, fHead_};
185 Invariant ();
186 }
187 template <typename T>
188 template <Memory::ISpanOfT<T> SPAN_T>
189 void LinkedList<T>::push_front (const SPAN_T& copyFrom)
190 {
191 // push_front in reverse order cuz push_front reverses traversal order, and two wrongs make a right
192 for (auto ri = copyFrom.rbegin (); ri != copyFrom.rend (); ++ri) {
193 push_front (*ri);
194 }
195 }
196 template <typename T>
197 void LinkedList<T>::push_back (ArgByValueType<T> item)
198 {
200 if (this->fHead_ == nullptr) [[unlikely]] {
201 push_front (item);
202 }
203 else {
204 Link_* last = this->fHead_;
205 for (; last->fNext != nullptr; last = last->fNext)
206 ;
207 Assert (last != nullptr);
208 Assert (last->fNext == nullptr);
209 last->fNext = new Link_{item, nullptr};
210 }
211 }
212 template <typename T>
213 template <Memory::ISpanOfT<T> SPAN_T>
214 void LinkedList<T>::push_back (const SPAN_T& copyFrom)
215 {
217 Link_* last = this->fHead_; // Compute last once, and re-use for each item appended
218 if (last != nullptr) {
219 for (; last->fNext != nullptr; last = last->fNext)
221 }
222 for (const auto& i : copyFrom) {
223 if (last == nullptr) [[unlikely]] {
224 push_front (i);
225 }
226 else {
227 Assert (last->fNext == nullptr); // really we are last
228 last->fNext = new Link_{i, nullptr};
229 last = last->fNext; // for next item in span
230 }
231 }
232 }
233 template <typename T>
235 {
237 Require (not empty ());
238 AssertNotNull (fHead_);
239 Invariant ();
240 Link_* victim = fHead_;
241 fHead_ = victim->fNext;
242 delete victim;
243 Invariant ();
244 }
245 template <typename T>
246 inline T* LinkedList<T>::PeekAt (const ForwardIterator& i)
249#if qStroika_Foundation_Debug_AssertionsChecked
250 Require (i.fData_ == this); // assure iterator not stale
251#endif
252 Require (not i.Done ());
253 Invariant ();
254 i.Invariant ();
255 return &const_cast<Link_*> (i.fCurrent_)->fItem;
257 template <typename T>
258 inline void LinkedList<T>::SetAt (const ForwardIterator& i, ArgByValueType<T> newValue)
259 {
261 Require (not i.Done ());
262#if qStroika_Foundation_Debug_AssertionsChecked
263 Require (i.fData_ == this); // assure iterator not stale
264#endif
265 Invariant ();
266 i.Invariant ();
267 const_cast<Link_*> (i.fCurrent_)->fItem = newValue;
268 Invariant ();
269 }
270 template <typename T>
271 void LinkedList<T>::AddBefore (const ForwardIterator& i, ArgByValueType<T> item)
272 {
274#if qStroika_Foundation_Debug_AssertionsChecked
275 Require (i.fData_ == this); // assure iterator not stale
276#endif
277 /*
278 * NB: This code works fine, even if 'i' is Done ()
279 */
280 Invariant ();
281 i.Invariant ();
282
283 Link_* prev = nullptr;
284 if ((this->fHead_ != nullptr) and (this->fHead_ != i.fCurrent_)) {
285 for (prev = this->fHead_; prev->fNext != i.fCurrent_; prev = prev->fNext) {
286 AssertNotNull (prev); // cuz that would mean fCurrent_ not in LinkedList!!!
287 }
288 }
289
290 if (prev == nullptr) {
291 Assert (this->fHead_ == i.fCurrent_); // could be nullptr, or not...
292 this->fHead_ = new Link_{item, this->fHead_};
293 }
294 else {
295 Assert (prev->fNext == i.fCurrent_);
296 prev->fNext = new Link_{item, prev->fNext};
297 }
298
299 Invariant ();
300 }
301 template <typename T>
302 void LinkedList<T>::AddBefore (const ForwardIterator& i, ArgByValueType<T> item, ForwardIterator* newLinkCreatedAt)
303 {
304 RequireNotNull (newLinkCreatedAt);
306#if qStroika_Foundation_Debug_AssertionsChecked
307 Require (i.fData_ == this); // assure iterator not stale
308#endif
309 /*
310 * NB: This code works fine, even if 'i' is Done ()
311 */
312 Invariant ();
313 i.Invariant ();
314
315 Link_* prev = nullptr;
316 if ((this->fHead_ != nullptr) and (this->fHead_ != i.fCurrent_)) {
317 for (prev = this->fHead_; prev->fNext != i.fCurrent_; prev = prev->fNext) {
318 AssertNotNull (prev); // cuz that would mean fCurrent_ not in LinkedList!!!
319 }
320 }
321
322 if (prev == nullptr) {
323 Assert (this->fHead_ == i.fCurrent_); // could be nullptr, or not...
324 this->fHead_ = new Link_{item, this->fHead_};
325 *newLinkCreatedAt = ForwardIterator{this, this->fHead_};
326 }
327 else {
328 Assert (prev->fNext == i.fCurrent_);
329 prev->fNext = new Link_{item, prev->fNext};
330 *newLinkCreatedAt = ForwardIterator{this, prev->fNext};
331 }
332
333 Invariant ();
334 }
335 template <typename T>
336 inline void LinkedList<T>::AddAfter (const ForwardIterator& i, ArgByValueType<T> newValue)
337 {
339 Require (not i.Done ());
340#if qStroika_Foundation_Debug_AssertionsChecked
341 Require (i.fData_ == this); // assure iterator not stale
342#endif
343 AssertNotNull (i.fCurrent_); // since not done...
344 i.Invariant ();
345 const_cast<Link_*> (i.fCurrent_)->fNext = new Link_{newValue, i.fCurrent_->fNext};
346 }
347 template <typename T>
348 inline auto LinkedList<T>::erase (const ForwardIterator& i) -> ForwardIterator
349 {
350 ForwardIterator next = i;
351 ++next;
352 Remove (i);
353 next.Invariant ();
354 return next;
355 }
356 template <typename T>
357 void LinkedList<T>::Remove (const ForwardIterator& i)
358 {
360#if qStroika_Foundation_Debug_AssertionsChecked
361 Require (i.fData_ == this); // assure iterator not stale
362#endif
363 Require (not i.Done ());
364 Invariant ();
365 i.Invariant ();
366
367 const Link_* victim = i.fCurrent_;
368
369 /*
370 * At this point we need the prev pointer (so so we can adjust its 'next').
371 * Since the links go in one direction, we must start at the head, and find the item
372 * pointing to the 'victim'.
373 */
374 Link_* prevLink = nullptr;
375 if (this->fHead_ != victim) {
376 auto potentiallyPrevLink = this->fHead_;
377 AssertNotNull (potentiallyPrevLink); // cuz there must be something to remove current
378 for (; potentiallyPrevLink->fNext != victim; potentiallyPrevLink = potentiallyPrevLink->fNext) {
379 AssertNotNull (potentiallyPrevLink); // cuz that would mean victim not in LinkedList!!!
380 }
381 prevLink = potentiallyPrevLink;
382 }
383 Assert (prevLink == nullptr or prevLink->fNext == victim);
384 if (prevLink == nullptr) {
385 Require (this->fHead_ == victim); // If this ever happened, it would mean the argument link to be removed from
386 // this list was not actually in this list! Caller error - serious bug (corruption?)
387 this->fHead_ = victim->fNext;
388 }
389 else {
390 Assert (prevLink->fNext == victim); // because of how we computed prevLink above, this must be true
391 prevLink->fNext = victim->fNext;
392 }
393
394 delete victim;
395 Invariant ();
396 }
397 template <typename T>
398 template <typename EQUALS_COMPARER>
399 void LinkedList<T>::Remove (ArgByValueType<T> item, const EQUALS_COMPARER& equalsComparer)
400 {
401 Debug::AssertExternallySynchronizedMutex::WriteContext declareContext{*this};
402 Invariant ();
403 /*
404 * Base class impl is fine, but doesn't do patching, and doesn't
405 * provide the hooks so I can do the patching from here.
406 *
407 * @todo We may want to correct that (see STL container impl -
408 * returning ptr to next node would do it).
409 */
410 for (ForwardIterator it{this}; not it.Done (); ++it) {
411 if (equalsComparer (*it, item)) {
412 this->Remove (it);
413 break;
414 }
415 }
416 Invariant ();
417 }
418 template <typename T>
419 template <invocable<T> FUNCTION>
420 inline void LinkedList<T>::Apply (FUNCTION&& doToElement) const
421 {
422 AssertExternallySynchronizedMutex::ReadContext declareContext{*this};
423 for (const Link_* i = fHead_; i != nullptr; i = i->fNext) {
424 doToElement (i->fItem);
425 }
426 }
427 template <typename T>
428 template <predicate<T> FUNCTION>
429 inline auto LinkedList<T>::Find (FUNCTION&& firstThat) const -> UnderlyingIteratorRep
430 {
431 AssertExternallySynchronizedMutex::ReadContext declareContext{*this};
432 for (Link_* i = fHead_; i != nullptr; i = i->fNext) {
433 if (firstThat (i->fItem)) {
434 return i;
435 }
436 }
437 return nullptr;
438 }
439 template <typename T>
440 template <typename EQUALS_COMPARER>
441 T* LinkedList<T>::Find (ArgByValueType<T> item, EQUALS_COMPARER&& equalsComparer)
442 {
443 Debug::AssertExternallySynchronizedMutex::WriteContext declareContext{*this}; // lock not shared cuz return mutable ptr
444 for (Link_* i = fHead_; i != nullptr; i = i->fNext) {
445 if (forward<EQUALS_COMPARER> (equalsComparer) (i->fItem, item)) {
446 return &i->fItem;
447 }
448 }
449 return nullptr;
450 }
451 template <typename T>
452 template <typename EQUALS_COMPARER>
453 const T* LinkedList<T>::Find (ArgByValueType<T> item, EQUALS_COMPARER&& equalsComparer) const
454 {
455 AssertExternallySynchronizedMutex::ReadContext declareContext{*this};
456 for (const Link_* i = fHead_; i != nullptr; i = i->fNext) {
457 if (forward<EQUALS_COMPARER> (equalsComparer) (i->fItem, item)) {
458 return &i->fItem;
459 }
460 }
461 return nullptr;
462 }
463 template <typename T>
465 {
467 Invariant ();
468 for (Link_* i = fHead_; i != nullptr;) {
469 Link_* deleteMe = i;
470 i = i->fNext;
471 delete deleteMe;
472 }
473 fHead_ = nullptr;
474 Invariant ();
475 Ensure (empty ());
476 }
477 template <typename T>
478 T LinkedList<T>::GetAt (size_t i) const
479 {
480 AssertExternallySynchronizedMutex::ReadContext declareContext{*this};
481 Require (i >= 0);
482 Require (i < size ());
483 const Link_* cur = fHead_;
484 for (; i != 0; cur = cur->fNext, --i) {
485 AssertNotNull (cur); // cuz i <= fLength
486 }
487 AssertNotNull (cur); // cuz i <= fLength
488 return cur->fItem;
489 }
490 template <typename T>
491 void LinkedList<T>::SetAt (T item, size_t i)
492 {
494 Require (i >= 0);
495 Require (i < size ());
496 Link_* cur = fHead_;
497 for (; i != 0; cur = cur->fNext, --i) {
498 AssertNotNull (cur); // cuz i <= fLength
499 }
500 AssertNotNull (cur); // cuz i <= fLength
501 cur->fItem = item;
502 }
503#if qStroika_Foundation_Debug_AssertionsChecked
504 template <typename T>
505 void LinkedList<T>::Invariant_ () const noexcept
506 {
507#if qStroika_Foundation_Containers_DataStructures_LinkedList_IncludeSlowDebugChecks_
508 AssertExternallySynchronizedMutex::ReadContext declareContext{*this};
509#endif
510 /*
511 * Check we are properly linked together.
512 */
513 for (Link_* i = fHead_; i != nullptr; i = i->fNext) {
514 // at least make sure no corrupted links and no infinite loops
515 }
516 }
517#endif
518
519 /*
520 ********************************************************************************
521 ************************* LinkedList<T>::ForwardIterator ***********************
522 ********************************************************************************
523 */
524 template <typename T>
525 constexpr LinkedList<T>::ForwardIterator::ForwardIterator ([[maybe_unused]] const LinkedList* data, UnderlyingIteratorRep startAt) noexcept
526 : fCurrent_{startAt}
527#if qStroika_Foundation_Debug_AssertionsChecked
528 , fData_{data}
529#endif
530 {
531 RequireNotNull (data);
532 }
533 template <typename T>
534 constexpr LinkedList<T>::ForwardIterator::ForwardIterator (const LinkedList* data) noexcept
535 : ForwardIterator{data, (RequireExpression (data != nullptr), data->fHead_)}
536 {
537 RequireNotNull (data);
538 }
539 template <typename T>
540 inline void LinkedList<T>::ForwardIterator::Invariant () const noexcept
541 {
542#if qStroika_Foundation_Debug_AssertionsChecked
543 Invariant_ ();
544#endif
545 }
546 template <typename T>
547 inline LinkedList<T>::ForwardIterator::operator bool () const
548 {
549 return not Done ();
550 }
551 template <typename T>
552 inline bool LinkedList<T>::ForwardIterator::Done () const noexcept
553 {
554 Invariant ();
555 return fCurrent_ == nullptr;
556 }
557 template <typename T>
558 inline auto LinkedList<T>::ForwardIterator::operator++ () noexcept -> ForwardIterator&
559 {
560 Require (not Done ());
561 Invariant ();
562 Assert (fCurrent_ != nullptr);
563 fCurrent_ = fCurrent_->fNext;
564 Invariant ();
565 return *this;
566 }
567 template <typename T>
568 inline auto LinkedList<T>::ForwardIterator::operator++ (int) noexcept -> ForwardIterator
569 {
570 ForwardIterator result = *this;
571 this->operator++ ();
572 return result;
573 }
574 template <typename T>
575 inline T LinkedList<T>::ForwardIterator::operator* () const
576 {
577 Require (not(Done ()));
578 Invariant ();
579 AssertNotNull (fCurrent_);
580 return fCurrent_->fItem;
581 }
582 template <typename T>
583 inline const T* LinkedList<T>::ForwardIterator::operator->() const
584 {
585 Require (not(Done ()));
586 Invariant ();
587 AssertNotNull (fCurrent_);
588 return &fCurrent_->fItem;
589 }
590 template <typename T>
591 size_t LinkedList<T>::ForwardIterator::CurrentIndex (const LinkedList* data) const
592 {
593 Require (not Done ());
594#if qStroika_Foundation_Debug_AssertionsChecked
595 Require (data == fData_);
596 RequireNotNull (fData_);
597#endif
598 RequireNotNull (this->fCurrent_);
599 size_t i = 0;
600 for (const Link_* l = data->fHead_;; l = l->fNext, ++i) {
601 AssertNotNull (l);
602 if (l == fCurrent_) [[unlikely]] {
603 return i;
604 }
605 }
607 return i;
608 }
609 template <typename T>
610 inline auto LinkedList<T>::ForwardIterator::GetUnderlyingIteratorRep () const -> UnderlyingIteratorRep
611 {
612 return fCurrent_;
613 }
614 template <typename T>
615 inline void LinkedList<T>::ForwardIterator::SetUnderlyingIteratorRep (const UnderlyingIteratorRep l)
616 {
617 // MUUST COME FROM THIS LIST
618 // CAN be nullptr
619 fCurrent_ = l;
620 }
621 template <typename T>
622 constexpr void LinkedList<T>::ForwardIterator::AssertDataMatches ([[maybe_unused]] const LinkedList* data) const
623 {
624#if qStroika_Foundation_Debug_AssertionsChecked
625 Require (data == fData_);
626#endif
627 }
628 template <typename T>
629 inline bool LinkedList<T>::ForwardIterator::operator== (const ForwardIterator& rhs) const
630 {
631#if qStroika_Foundation_Debug_AssertionsChecked
632 Require (fData_ == nullptr or rhs.fData_ == nullptr or fData_ == rhs.fData_); // fData_==null for end sentinel case
633#endif
634 return fCurrent_ == rhs.fCurrent_;
635 }
636#if qStroika_Foundation_Debug_AssertionsChecked
637 template <typename T>
638 void LinkedList<T>::ForwardIterator::Invariant_ () const noexcept
639 {
640 }
641#endif
642
643}
#define AssertNotNull(p)
Definition Assertions.h:333
#define RequireNotNull(p)
Definition Assertions.h:347
#define RequireExpression(c)
Definition Assertions.h:267
#define AssertNotReached()
Definition Assertions.h:355
unique_lock< AssertExternallySynchronizedMutex > WriteContext
Instantiate AssertExternallySynchronizedMutex::WriteContext to designate an area of code where protec...