Stroika Library 3.0d24
 
Loading...
Searching...
No Matches
DoublyLinkedList.h
1/*
2 * Copyright(c) Sophist Solutions, Inc. 1990-2026. All rights reserved
3 */
4#ifndef _Stroika_Foundation_Containers_DataStructures_DoublyLinkedList_h_
5#define _Stroika_Foundation_Containers_DataStructures_DoublyLinkedList_h_
6
7#include "Stroika/Foundation/StroikaPreComp.h"
8
9#include "Stroika/Foundation/Common/Common.h"
12#include "Stroika/Foundation/Containers/Common.h"
15
16/*
17 *
18 * Description:
19 *
20 * DoublyLinkedList<T> is a backend implementation. It is not intended to be directly
21 * used by programmers, except in implementing concrete container reps.
22 *
23 * TODO:
24 *
25 * Long-Term TODO:
26 * @todo Could add iterator subclass (or use traits to control) which tracks index internally, as with Stroika v1
27 * but this will do for and maybe best (depending on frequency of calls to CurrentIndex ()
28 *
29 * \note Code-Status: <a href="Code-Status.md#Beta">Beta</a>
30 *
31 */
32
34
36
37 /**
38 * DoublyLinkedList<T> is a generic link (non-intrusive) list implementation.
39 * We provide no public means to access the links themselves.
40 *
41 * \note Satisfies Concepts:
42 * o static_assert (ranges::input_range<DoublyLinkedList<T>>)
43 *
44 * \note \em Thread-Safety <a href="Thread-Safety.md#C++-Standard-Thread-Safety">C++-Standard-Thread-Safety</a>
45 */
46 template <typename T>
48 public:
49 using value_type = T;
50
51 public:
52 /**
53 */
55 DoublyLinkedList (DoublyLinkedList&& src) noexcept;
58
59 public:
60 nonvirtual DoublyLinkedList& operator= (const DoublyLinkedList& list);
61
62 private:
63 class Link_;
64
65 public:
66 /**
67 * Basic (mostly internal) element used by ForwardIterator. Abstract name so can be referenced generically across 'DataStructure' objects
68 */
69 using UnderlyingIteratorRep = const Link_*;
70
71 public:
72 class ForwardIterator;
73
74 public:
75 class BidirectionalIterator;
76
77 public:
78 /**
79 * \note Runtime performance/complexity:
80 * Always: constant
81 */
82 nonvirtual bool empty () const;
83
84 public:
85 /**
86 * \note Runtime performance/complexity:
87 * Always: constant
88 *
89 * \note The length is cached (one size_t per list), not counted - see the ReadMe on why
90 * the DataStructures classes keep size () constant-time: the Stroika containers built
91 * on them guarantee that to their callers, and can only do so if the backend does.
92 */
93 nonvirtual size_t size () const;
94
95 public:
96 /**
97 * \note Runtime performance/complexity:
98 * Always: constant
99 */
100 nonvirtual optional<T> GetFirst () const;
101
102 public:
103 /**
104 * \note Runtime performance/complexity:
105 * Always: constant
106 */
107 nonvirtual optional<T> GetLast () const;
108
109 public:
110 /**
111 * @aliases Prepend
112 *
113 * \note Runtime performance/complexity:
114 * Always: constant
115 *
116 * \note for push_front(span) - this puts the span elements in front in the same order in
117 * which they appears in the span.
118 */
119 nonvirtual void push_front (ArgByValueType<T> item);
120 template <Memory::ISpanOfT<T> SPAN_T>
121 nonvirtual void push_front (const SPAN_T& copyFrom);
122
123 public:
124 /**
125 * @aliases Append
126 *
127 * \note Runtime performance/complexity:
128 * Always: constant
129 *
130 * \note for push_back(span) - this puts the span elements in back in the same order in
131 * which they appears in the span.
132 */
133 nonvirtual void push_back (ArgByValueType<T> item);
134 template <Memory::ISpanOfT<T> SPAN_T>
135 nonvirtual void push_back (const SPAN_T& copyFrom);
136
137 public:
138 /**
139 * \note Runtime performance/complexity:
140 * Always: constant
141 *
142 * \pre not empty ()
143 */
144 nonvirtual void RemoveFirst ();
145
146 public:
147 /**
148 * \note Runtime performance/complexity:
149 * Always: constant
150 *
151 * \pre not empty ()
152 */
153 nonvirtual void RemoveLast ();
154
155 public:
156 /*
157 * \note Runtime performance/complexity:
158 * Worst Case: O(N)
159 * Average Case: O(N)
160 *
161 * Utility to search the list for the given item using EQUALS_COMPARER
162 */
163 template <typename EQUALS_COMPARER = equal_to<T>>
164 nonvirtual bool Contains (ArgByValueType<T> item, EQUALS_COMPARER&& equalsComparer = {}) const;
165
166 public:
167 /**
168 * \note Runtime performance/complexity:
169 * Always: O(N)
170 */
171 template <invocable<T> FUNCTION>
172 nonvirtual void Apply (FUNCTION&& doToElement) const;
173
174 public:
175 /**
176 * \note Runtime performance/complexity:
177 * Worst Case: O(N)
178 * Typical: O(N), but can be less if systematically finding entries near start of container
179 */
180 template <typename FUNCTION>
181 nonvirtual UnderlyingIteratorRep Find (FUNCTION&& firstThat) const;
182
183 public:
184 /**
185 * \note Runtime performance/complexity:
186 * Worst Case: O(N)
187 * Average Case: O(N)
188 *
189 * Note - does nothing if item not found.
190
191 ForwardIterator OVERLOAD:
192 * \note Runtime performance/complexity:
193 * Always: constant
194 *
195 * returns the next link
196 */
197 template <typename EQUALS_COMPARER = equal_to<T>>
198 nonvirtual void Remove (ArgByValueType<T> item, EQUALS_COMPARER&& equalsComparer = {});
199 nonvirtual void Remove (const ForwardIterator& i);
200
201 public:
202 /**
203 * Complexity:
204 * Always: constant
205 *
206 * Returns iterator pointing at next item.
207 */
208 nonvirtual ForwardIterator erase (const ForwardIterator& i);
209
210 public:
211 /**
212 * \note Runtime performance/complexity:
213 * Worst Case: O(N)
214 * Average Case: O(N)
215 */
216 nonvirtual void clear ();
217
218 public:
219 /**
220 * \note Runtime performance/complexity:
221 * Worst Case: O(N)
222 * Average Case: O(N)
223 */
224 nonvirtual T GetAt (size_t i) const;
225
226 public:
227 /**
228 * \note Runtime performance/complexity:
229 * Worst Case: O(N)
230 * Average Case: O(N)
231 */
232 nonvirtual void SetAt (size_t i, ArgByValueType<T> item);
233
234 public:
235 /*
236 * Support for COW (CopyOnWrite):
237 *
238 * Take iterator 'pi' which is originally a valid iterator from 'movedFrom' - and replace *pi with a valid
239 * iterator from 'this' - which points at the same logical position. This requires that this container
240 * was just 'copied' from 'movedFrom' - and is used to produce an equivalent iterator (since iterators are tied to
241 * the container they were iterating over).
242 */
243 nonvirtual void MoveIteratorHereAfterClone (ForwardIterator* pi, const DoublyLinkedList<T>* movedFrom) const;
244
245 public:
246 /**
247 */
248 nonvirtual ForwardIterator begin () const;
249
250 public:
251 /**
252 */
253 constexpr ForwardIterator end () const noexcept;
254
255 public:
256 /**
257 * \note Runtime performance/complexity:
258 * Always: constant
259 */
260 nonvirtual void SetAt (const ForwardIterator& i, ArgByValueType<T> newValue);
261
262 public:
263 /**
264 * \note Runtime performance/complexity:
265 * Always: constant
266 *
267 * \pre not i.AtEnd ()
268 */
269 nonvirtual void AddBefore (const ForwardIterator& i, ArgByValueType<T> item);
270
271 public:
272 /**
273 * \note Runtime performance/complexity:
274 * Always: constant
275 */
276 nonvirtual void AddAfter (const ForwardIterator& i, ArgByValueType<T> item);
277
278 public:
279 nonvirtual void Invariant () const noexcept;
280
281 private:
282 Link_* fHead_{};
283 Link_* fTail_{};
284 // Cached so size () is O(1) - see the note on size (). One size_t per LIST, not per link.
285 // Invariant_ () validates fLength_.
286 size_t fLength_{0};
287
288#if qStroika_Foundation_Debug_AssertionsChecked
289 private:
290 virtual void Invariant_ () const noexcept;
291#endif
292
293 private:
294 friend class ForwardIterator;
295 friend class BidirectionalIterator;
296 };
297
298 /**
299 * Just an implementation detail. Don't use directly except in helper classes.
300 * dont use block allocation for link sizes too large
301 */
302 template <typename T>
303 class DoublyLinkedList<T>::Link_ : public Memory::UseBlockAllocationIfAppropriate<Link_, sizeof (T) <= 1024> {
304 public:
305 Link_ () = delete;
306 Link_ (const Link_&) = delete;
307 constexpr Link_ (ArgByValueType<T> item, Link_* prev, Link_* next);
308
309 public:
310 T fItem;
311 Link_* fPrev{nullptr};
312 Link_* fNext{nullptr};
313 };
314
315 /**
316 * ForwardIterator<T> allows you to iterate over a DoublyLinkedList<T>. Its API
317 * is designed to make easy implementations of subclasses of IteratorRep<T>.
318 * It is unpatched - use DoublyLinkedListIterator_Patch<T> or DoublyLinkedListIterator_Patch<T>
319 * for that.
320 *
321 * \note Satisfies Concepts:
322 * o forward_iterator<typename DoublyLinkedList<T>::ForwardIterator>>
323 * o regular<typename DoublyLinkedList<T>::ForwardIterator>> // implies copyable/movable/equality_comparable
324 */
325 template <typename T>
326 class DoublyLinkedList<T>::ForwardIterator {
327 public:
328 // stuff STL requires you to set to look like an iterator
329 using iterator_category = forward_iterator_tag;
330 using value_type = DoublyLinkedList::value_type;
331 using difference_type = ptrdiff_t;
332 using pointer = const value_type*;
333 using reference = const value_type&;
334
335 public:
336 /**
337 * /0 overload: sets iterator to 'end' - sentinel
338 * /1 (data) overload: sets iterator to begin
339 * /2 (data,startAt) overload: sets iterator to startAt
340 */
341 constexpr ForwardIterator () noexcept = default;
342 explicit constexpr ForwardIterator (const DoublyLinkedList* data) noexcept;
343 explicit constexpr ForwardIterator (const DoublyLinkedList* data, UnderlyingIteratorRep startAt) noexcept;
344 constexpr ForwardIterator (const ForwardIterator&) noexcept = default;
345 constexpr ForwardIterator (ForwardIterator&&) noexcept = default;
346
347 public:
348 nonvirtual ForwardIterator& operator= (const ForwardIterator&) = default;
349 nonvirtual ForwardIterator& operator= (ForwardIterator&&) noexcept = default;
350
351 public:
352 /**
353 * return true if iterator not AtEnd
354 */
355 explicit operator bool () const;
356
357 public:
358 nonvirtual bool AtEnd () const noexcept;
359
360 public:
361 nonvirtual const T& operator* () const;
362
363 public:
364 nonvirtual const T* operator->() const;
365
366 public:
367 nonvirtual ForwardIterator& operator++ () noexcept;
368 nonvirtual ForwardIterator operator++ (int) noexcept;
369
370 public:
371 /**
372 * \note Runtime performance/complexity:
373 * Average/WorseCase: O(N) - super slow cuz have to traverse on average half the list
374 *
375 * \pre data == fData_ argument constructed with (or as adjusted by Move...); api takes extra param so release builds need not store fData_
376 */
377 nonvirtual size_t CurrentIndex (const DoublyLinkedList* data) const;
378
379 public:
380 nonvirtual UnderlyingIteratorRep GetUnderlyingIteratorRep () const;
381
382 public:
383 nonvirtual void SetUnderlyingIteratorRep (UnderlyingIteratorRep l);
384
385 public:
386 /**
387 * For debugging, assert the iterator data matches argument data
388 */
389 constexpr void AssertDataMatches (const DoublyLinkedList* data) const;
390
391 public:
392 nonvirtual bool operator== (const ForwardIterator& rhs) const;
393
394 public:
395 nonvirtual void Invariant () const noexcept;
396
397 protected:
398 const Link_* _fCurrent{nullptr};
399
400 private:
401#if qStroika_Foundation_Debug_AssertionsChecked
402 const DoublyLinkedList* fData_{nullptr};
403#endif
404
405#if qStroika_Foundation_Debug_AssertionsChecked
406 private:
407 nonvirtual void Invariant_ () const noexcept;
408#endif
409
410 private:
411 friend class DoublyLinkedList;
412 };
413
414 // see Satisfies Concepts
415 static_assert (forward_iterator<typename DoublyLinkedList<int>::ForwardIterator>);
416 static_assert (regular<typename DoublyLinkedList<int>::ForwardIterator>);
417
418 /**
419 * @brief Same as ForwardIterator, but adding ability to reverse direction
420 *
421 * \note Satisfies Concepts:
422 * o regular<typename DoublyLinkedList<T>::ForwardIterator>> // implies copyable/movable/equality_comparable
423 * o bidirectional_iterator<typename DoublyLinkedList<T>::ForwardIterator>>
424 */
425 template <typename T>
426 class DoublyLinkedList<T>::BidirectionalIterator : public ForwardIterator {
427 private:
428 using inherited = ForwardIterator;
429
430 public:
431 // stuff STL requires you to set to look like an iterator
432 using iterator_category = bidirectional_iterator_tag;
433 using value_type = DoublyLinkedList::value_type;
434 using difference_type = ptrdiff_t;
435 using pointer = const value_type*;
436 using reference = const value_type&;
437
438 public:
439 /**
440 * /0 overload: sets iterator to 'end' - sentinel
441 * /1 (data) overload: sets iterator to begin
442 * /2 (data,startAt) overload: sets iterator to startAt
443 */
444 constexpr BidirectionalIterator () noexcept = default;
445 explicit constexpr BidirectionalIterator (const DoublyLinkedList* data) noexcept;
446 explicit constexpr BidirectionalIterator (const DoublyLinkedList* data, UnderlyingIteratorRep startAt) noexcept;
447 constexpr BidirectionalIterator (const BidirectionalIterator&) noexcept = default;
448 constexpr BidirectionalIterator (BidirectionalIterator&&) noexcept = default;
449
450 public:
451 nonvirtual BidirectionalIterator& operator= (const BidirectionalIterator&) = default;
452 nonvirtual BidirectionalIterator& operator= (BidirectionalIterator&&) noexcept = default;
453
454 public:
455 nonvirtual bool AtStart () const noexcept;
456
457 public:
458 /**
459 * @brief increment iterator (same as inherited version, but returning BidirectionalIterator)
460 */
461 nonvirtual BidirectionalIterator& operator++ () noexcept;
462 nonvirtual BidirectionalIterator operator++ (int) noexcept;
463
464 public:
465 /**
466 * \pre not AtStart ()
467 */
468 nonvirtual BidirectionalIterator& operator-- () noexcept;
469 nonvirtual BidirectionalIterator operator-- (int) noexcept;
470
471 public:
472 /**
473 * \brief Move the iterator back by the specified number of positions (note i maybe positive or negative, but must result in a valid position).
474 */
475 nonvirtual BidirectionalIterator operator- (ptrdiff_t i) const;
476
477 private:
478 const DoublyLinkedList* fData_{nullptr}; // needed to always know length - even if null/at end
479 };
480
481 // see Satisfies Concepts
482 static_assert (bidirectional_iterator<typename DoublyLinkedList<int>::BidirectionalIterator>);
483 static_assert (regular<typename DoublyLinkedList<int>::BidirectionalIterator>);
484
485 // see Satisfies Concepts
486 static_assert (ranges::input_range<DoublyLinkedList<int>>); // smoke test - make sure basic iteration etc should work (allows formattable to work)
487
488}
489
490/*
491 ********************************************************************************
492 ***************************** Implementation Details ***************************
493 ********************************************************************************
494 */
495#include "DoublyLinkedList.inl"
496
497#endif /*_Stroika_Foundation_Containers_DataStructures_DoublyLinkedList_h_ */
nonvirtual void AddBefore(const ForwardIterator &i, ArgByValueType< T > item)
nonvirtual ForwardIterator erase(const ForwardIterator &i)
nonvirtual void Remove(ArgByValueType< T > item, EQUALS_COMPARER &&equalsComparer={})
nonvirtual void Apply(FUNCTION &&doToElement) const
nonvirtual void SetAt(size_t i, ArgByValueType< T > item)
nonvirtual UnderlyingIteratorRep Find(FUNCTION &&firstThat) const
nonvirtual void AddAfter(const ForwardIterator &i, ArgByValueType< T > item)
NOT a real mutex - just a debugging infrastructure support tool so in debug builds can be assured thr...
conditional_t<(sizeof(CHECK_T)<=2 *sizeof(void *)) and is_trivially_copyable_v< CHECK_T >, CHECK_T, const CHECK_T & > ArgByValueType
This is an alias for 'T' - but how we want to pass it on stack as formal parameter.
Definition TypeHints.h:36
constexpr void AssertDataMatches(const DoublyLinkedList *data) const
nonvirtual size_t CurrentIndex(const DoublyLinkedList *data) const