Stroika Library 3.0d16
 
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 RemoveAll ();
79 Invariant ();
80 Ensure (fHead_ == nullptr);
81 }
82 template <typename T>
83 auto LinkedList<T>::operator= (const LinkedList& rhs) -> LinkedList&
84 {
85 Debug::AssertExternallySynchronizedMutex::WriteContext declareContext{*this};
86 Invariant ();
87 if (this != &rhs) {
88 RemoveAll ();
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
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;
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)
183 Invariant ();
184 fHead_ = new Link_{item, fHead_};
185 Invariant ();
186 }
187 template <typename T>
188 void LinkedList<T>::push_back (ArgByValueType<T> item)
191 if (this->fHead_ == nullptr) [[unlikely]] {
192 push_front (item);
193 }
194 else {
195 Link_* last = this->fHead_;
196 for (; last->fNext != nullptr; last = last->fNext)
197 ;
198 Assert (last != nullptr);
199 Assert (last->fNext == nullptr);
200 last->fNext = new Link_{item, nullptr};
201 }
202 }
203 template <typename T>
205 {
207 Require (not empty ());
208 AssertNotNull (fHead_);
209 Invariant ();
210 Link_* victim = fHead_;
211 fHead_ = victim->fNext;
212 delete victim;
213 Invariant ();
214 }
215 template <typename T>
216 inline T* LinkedList<T>::PeekAt (const ForwardIterator& i)
219#if qStroika_Foundation_Debug_AssertionsChecked
220 Require (i.fData_ == this); // assure iterator not stale
221#endif
222 Require (not i.Done ());
223 Invariant ();
224 i.Invariant ();
225 return &const_cast<Link_*> (i.fCurrent_)->fItem;
226 }
227 template <typename T>
228 inline void LinkedList<T>::SetAt (const ForwardIterator& i, ArgByValueType<T> newValue)
229 {
231 Require (not i.Done ());
232#if qStroika_Foundation_Debug_AssertionsChecked
233 Require (i.fData_ == this); // assure iterator not stale
234#endif
235 Invariant ();
236 i.Invariant ();
237 const_cast<Link_*> (i.fCurrent_)->fItem = newValue;
238 Invariant ();
239 }
240 template <typename T>
241 void LinkedList<T>::AddBefore (const ForwardIterator& i, ArgByValueType<T> item)
242 {
244#if qStroika_Foundation_Debug_AssertionsChecked
245 Require (i.fData_ == this); // assure iterator not stale
246#endif
247 /*
248 * NB: This code works fine, even if 'i' is Done ()
249 */
250 Invariant ();
251 i.Invariant ();
252
253 Link_* prev = nullptr;
254 if ((this->fHead_ != nullptr) and (this->fHead_ != i.fCurrent_)) {
255 for (prev = this->fHead_; prev->fNext != i.fCurrent_; prev = prev->fNext) {
256 AssertNotNull (prev); // cuz that would mean fCurrent_ not in LinkedList!!!
257 }
258 }
259
260 if (prev == nullptr) {
261 Assert (this->fHead_ == i.fCurrent_); // could be nullptr, or not...
262 this->fHead_ = new Link_{item, this->fHead_};
263 }
264 else {
265 Assert (prev->fNext == i.fCurrent_);
266 prev->fNext = new Link_{item, prev->fNext};
267 }
268
269 Invariant ();
270 }
271 template <typename T>
272 void LinkedList<T>::AddBefore (const ForwardIterator& i, ArgByValueType<T> item, ForwardIterator* newLinkCreatedAt)
273 {
274 RequireNotNull (newLinkCreatedAt);
276#if qStroika_Foundation_Debug_AssertionsChecked
277 Require (i.fData_ == this); // assure iterator not stale
278#endif
279 /*
280 * NB: This code works fine, even if 'i' is Done ()
281 */
282 Invariant ();
283 i.Invariant ();
284
285 Link_* prev = nullptr;
286 if ((this->fHead_ != nullptr) and (this->fHead_ != i.fCurrent_)) {
287 for (prev = this->fHead_; prev->fNext != i.fCurrent_; prev = prev->fNext) {
288 AssertNotNull (prev); // cuz that would mean fCurrent_ not in LinkedList!!!
289 }
290 }
291
292 if (prev == nullptr) {
293 Assert (this->fHead_ == i.fCurrent_); // could be nullptr, or not...
294 this->fHead_ = new Link_{item, this->fHead_};
295 *newLinkCreatedAt = ForwardIterator{this, this->fHead_};
296 }
297 else {
298 Assert (prev->fNext == i.fCurrent_);
299 prev->fNext = new Link_{item, prev->fNext};
300 *newLinkCreatedAt = ForwardIterator{this, prev->fNext};
301 }
302
303 Invariant ();
304 }
305 template <typename T>
306 inline void LinkedList<T>::AddAfter (const ForwardIterator& i, ArgByValueType<T> newValue)
307 {
309 Require (not i.Done ());
310#if qStroika_Foundation_Debug_AssertionsChecked
311 Require (i.fData_ == this); // assure iterator not stale
312#endif
313 AssertNotNull (i.fCurrent_); // since not done...
314 i.Invariant ();
315 const_cast<Link_*> (i.fCurrent_)->fNext = new Link_{newValue, i.fCurrent_->fNext};
316 }
317 template <typename T>
318 inline auto LinkedList<T>::erase (const ForwardIterator& i) -> ForwardIterator
319 {
320 ForwardIterator next = i;
321 ++next;
322 Remove (i);
323 next.Invariant ();
324 return next;
325 }
326 template <typename T>
327 void LinkedList<T>::Remove (const ForwardIterator& i)
328 {
330#if qStroika_Foundation_Debug_AssertionsChecked
331 Require (i.fData_ == this); // assure iterator not stale
332#endif
333 Require (not i.Done ());
334 Invariant ();
335 i.Invariant ();
336
337 const Link_* victim = i.fCurrent_;
338
339 /*
340 * At this point we need the prev pointer (so so we can adjust its 'next').
341 * Since the links go in one direction, we must start at the head, and find the item
342 * pointing to the 'victim'.
343 */
344 Link_* prevLink = nullptr;
345 if (this->fHead_ != victim) {
346 auto potentiallyPrevLink = this->fHead_;
347 AssertNotNull (potentiallyPrevLink); // cuz there must be something to remove current
348 for (; potentiallyPrevLink->fNext != victim; potentiallyPrevLink = potentiallyPrevLink->fNext) {
349 AssertNotNull (potentiallyPrevLink); // cuz that would mean victim not in LinkedList!!!
350 }
351 prevLink = potentiallyPrevLink;
352 }
353 Assert (prevLink == nullptr or prevLink->fNext == victim);
354 if (prevLink == nullptr) {
355 Require (this->fHead_ == victim); // If this ever happened, it would mean the argument link to be removed from
356 // this list was not actually in this list! Caller error - serious bug (corruption?)
357 this->fHead_ = victim->fNext;
358 }
359 else {
360 Assert (prevLink->fNext == victim); // because of how we computed prevLink above, this must be true
361 prevLink->fNext = victim->fNext;
362 }
363
364 delete victim;
365 Invariant ();
366 }
367 template <typename T>
368 template <typename EQUALS_COMPARER>
369 void LinkedList<T>::Remove (ArgByValueType<T> item, const EQUALS_COMPARER& equalsComparer)
370 {
371 Debug::AssertExternallySynchronizedMutex::WriteContext declareContext{*this};
372 Invariant ();
373 /*
374 * Base class impl is fine, but doesn't do patching, and doesn't
375 * provide the hooks so I can do the patching from here.
376 *
377 * @todo We may want to correct that (see STL container impl -
378 * returning ptr to next node would do it).
379 */
380 for (ForwardIterator it{this}; not it.Done (); ++it) {
381 if (equalsComparer (*it, item)) {
382 this->Remove (it);
383 break;
384 }
385 }
386 Invariant ();
387 }
388 template <typename T>
389 template <invocable<T> FUNCTION>
390 inline void LinkedList<T>::Apply (FUNCTION&& doToElement) const
391 {
392 AssertExternallySynchronizedMutex::ReadContext declareContext{*this};
393 for (const Link_* i = fHead_; i != nullptr; i = i->fNext) {
394 doToElement (i->fItem);
395 }
396 }
397 template <typename T>
398 template <predicate<T> FUNCTION>
399 inline auto LinkedList<T>::Find (FUNCTION&& firstThat) const -> UnderlyingIteratorRep
400 {
401 AssertExternallySynchronizedMutex::ReadContext declareContext{*this};
402 for (Link_* i = fHead_; i != nullptr; i = i->fNext) {
403 if (firstThat (i->fItem)) {
404 return i;
405 }
406 }
407 return nullptr;
408 }
409 template <typename T>
410 template <typename EQUALS_COMPARER>
411 T* LinkedList<T>::Find (ArgByValueType<T> item, EQUALS_COMPARER&& equalsComparer)
412 {
413 Debug::AssertExternallySynchronizedMutex::WriteContext declareContext{*this}; // lock not shared cuz return mutable ptr
414 for (Link_* i = fHead_; i != nullptr; i = i->fNext) {
415 if (equalsComparer (i->fItem, item)) {
416 return &i->fItem;
417 }
418 }
419 return nullptr;
420 }
421 template <typename T>
422 template <typename EQUALS_COMPARER>
423 const T* LinkedList<T>::Find (ArgByValueType<T> item, EQUALS_COMPARER&& equalsComparer) const
424 {
425 AssertExternallySynchronizedMutex::ReadContext declareContext{*this};
426 for (const Link_* i = fHead_; i != nullptr; i = i->fNext) {
427 if (equalsComparer (i->fItem, item)) {
428 return &i->fItem;
429 }
430 }
431 return nullptr;
432 }
433 template <typename T>
435 {
437 Invariant ();
438 for (Link_* i = fHead_; i != nullptr;) {
439 Link_* deleteMe = i;
440 i = i->fNext;
441 delete deleteMe;
442 }
443 fHead_ = nullptr;
444 Invariant ();
445 Ensure (empty ());
446 }
447 template <typename T>
448 T LinkedList<T>::GetAt (size_t i) const
449 {
450 AssertExternallySynchronizedMutex::ReadContext declareContext{*this};
451 Require (i >= 0);
452 Require (i < size ());
453 const Link_* cur = fHead_;
454 for (; i != 0; cur = cur->fNext, --i) {
455 AssertNotNull (cur); // cuz i <= fLength
456 }
457 AssertNotNull (cur); // cuz i <= fLength
458 return cur->fItem;
459 }
460 template <typename T>
461 void LinkedList<T>::SetAt (T item, size_t i)
462 {
464 Require (i >= 0);
465 Require (i < size ());
466 Link_* cur = fHead_;
467 for (; i != 0; cur = cur->fNext, --i) {
468 AssertNotNull (cur); // cuz i <= fLength
469 }
470 AssertNotNull (cur); // cuz i <= fLength
471 cur->fItem = item;
472 }
473#if qStroika_Foundation_Debug_AssertionsChecked
474 template <typename T>
475 void LinkedList<T>::Invariant_ () const noexcept
476 {
477#if qStroika_Foundation_Containers_DataStructures_LinkedList_IncludeSlowDebugChecks_
478 AssertExternallySynchronizedMutex::ReadContext declareContext{*this};
479#endif
480 /*
481 * Check we are properly linked together.
482 */
483 for (Link_* i = fHead_; i != nullptr; i = i->fNext) {
484 // at least make sure no corrupted links and no infinite loops
485 }
486 }
487#endif
488
489 /*
490 ********************************************************************************
491 ************************* LinkedList<T>::ForwardIterator ***********************
492 ********************************************************************************
493 */
494 template <typename T>
495 constexpr LinkedList<T>::ForwardIterator::ForwardIterator ([[maybe_unused]] const LinkedList* data, UnderlyingIteratorRep startAt) noexcept
496 : fCurrent_{startAt}
497#if qStroika_Foundation_Debug_AssertionsChecked
498 , fData_{data}
499#endif
500 {
501 RequireNotNull (data);
502 }
503 template <typename T>
504 constexpr LinkedList<T>::ForwardIterator::ForwardIterator (const LinkedList* data) noexcept
505 : ForwardIterator{data, (RequireExpression (data != nullptr), data->fHead_)}
506 {
507 RequireNotNull (data);
508 }
509 template <typename T>
510 inline void LinkedList<T>::ForwardIterator::Invariant () const noexcept
511 {
512#if qStroika_Foundation_Debug_AssertionsChecked
513 Invariant_ ();
514#endif
515 }
516 template <typename T>
517 inline LinkedList<T>::ForwardIterator::operator bool () const
518 {
519 return not Done ();
520 }
521 template <typename T>
522 inline bool LinkedList<T>::ForwardIterator::Done () const noexcept
523 {
524 Invariant ();
525 return fCurrent_ == nullptr;
526 }
527 template <typename T>
528 inline auto LinkedList<T>::ForwardIterator::operator++ () noexcept -> ForwardIterator&
529 {
530 Require (not Done ());
531 Invariant ();
532 Assert (fCurrent_ != nullptr);
533 fCurrent_ = fCurrent_->fNext;
534 Invariant ();
535 return *this;
536 }
537 template <typename T>
538 inline auto LinkedList<T>::ForwardIterator::operator++ (int) noexcept -> ForwardIterator
539 {
540 ForwardIterator result = *this;
541 this->operator++ ();
542 return result;
543 }
544 template <typename T>
545 inline T LinkedList<T>::ForwardIterator::operator* () const
546 {
547 Require (not(Done ()));
548 Invariant ();
549 AssertNotNull (fCurrent_);
550 return fCurrent_->fItem;
551 }
552 template <typename T>
553 inline const T* LinkedList<T>::ForwardIterator::operator->() const
554 {
555 Require (not(Done ()));
556 Invariant ();
557 AssertNotNull (fCurrent_);
558 return &fCurrent_->fItem;
559 }
560 template <typename T>
561 size_t LinkedList<T>::ForwardIterator::CurrentIndex (const LinkedList* data) const
562 {
563 Require (not Done ());
564#if qStroika_Foundation_Debug_AssertionsChecked
565 Require (data == fData_);
566 RequireNotNull (fData_);
567#endif
568 RequireNotNull (this->fCurrent_);
569 size_t i = 0;
570 for (const Link_* l = data->fHead_;; l = l->fNext, ++i) {
571 AssertNotNull (l);
572 if (l == fCurrent_) [[unlikely]] {
573 return i;
574 }
575 }
577 return i;
578 }
579 template <typename T>
580 inline auto LinkedList<T>::ForwardIterator::GetUnderlyingIteratorRep () const -> UnderlyingIteratorRep
581 {
582 return fCurrent_;
583 }
584 template <typename T>
585 inline void LinkedList<T>::ForwardIterator::SetUnderlyingIteratorRep (const UnderlyingIteratorRep l)
586 {
587 // MUUST COME FROM THIS LIST
588 // CAN be nullptr
589 fCurrent_ = l;
590 }
591 template <typename T>
592 constexpr void LinkedList<T>::ForwardIterator::AssertDataMatches ([[maybe_unused]] const LinkedList* data) const
593 {
594#if qStroika_Foundation_Debug_AssertionsChecked
595 Require (data == fData_);
596#endif
597 }
598 template <typename T>
599 inline bool LinkedList<T>::ForwardIterator::operator== (const ForwardIterator& rhs) const
600 {
601#if qStroika_Foundation_Debug_AssertionsChecked
602 Require (fData_ == nullptr or rhs.fData_ == nullptr or fData_ == rhs.fData_); // fData_==null for end sentinel case
603#endif
604 return fCurrent_ == rhs.fCurrent_;
605 }
606#if qStroika_Foundation_Debug_AssertionsChecked
607 template <typename T>
608 void LinkedList<T>::ForwardIterator::Invariant_ () const noexcept
609 {
610 }
611#endif
612
613}
#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...