Stroika Library 3.0d16
 
Loading...
Searching...
No Matches
DoublyLinkedList.h
1/*
2 * Copyright(c) Sophist Solutions, Inc. 1990-2025. 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 \em Thread-Safety <a href="Thread-Safety.md#C++-Standard-Thread-Safety">C++-Standard-Thread-Safety</a>
42 */
43 template <typename T>
45 public:
46 using value_type = T;
47
48 public:
49 /**
50 */
55
56 public:
57 nonvirtual DoublyLinkedList& operator= (const DoublyLinkedList& list);
58
59 private:
60 class Link_;
61
62 public:
63 /**
64 * Basic (mostly internal) element used by ForwardIterator. Abstract name so can be referenced generically across 'DataStructure' objects
65 */
66 using UnderlyingIteratorRep = const Link_*;
67
68 public:
69 class ForwardIterator;
70
71 public:
72 /**
73 * \note Runtime performance/complexity:
74 * Always: constant
75 */
76 nonvirtual bool empty () const;
77
78 public:
79 /**
80 * \note Runtime performance/complexity:
81 * Always: O(N)
82 */
83 nonvirtual size_t size () const;
84
85 public:
86 /**
87 * \note Runtime performance/complexity:
88 * Always: constant
89 */
90 nonvirtual optional<T> GetFirst () const;
91
92 public:
93 /**
94 * \note Runtime performance/complexity:
95 * Always: constant
96 */
97 nonvirtual optional<T> GetLast () const;
98
99 public:
100 /**
101 * @aliases Prepend
102 *
103 * \note Runtime performance/complexity:
104 * Always: constant
105 */
106 nonvirtual void push_front (ArgByValueType<T> item);
107
108 public:
109 /**
110 * @aliases Append
111 *
112 * \note Runtime performance/complexity:
113 * Always: constant
114 */
115 nonvirtual void push_back (ArgByValueType<T> item);
116
117 public:
118 /**
119 * \note Runtime performance/complexity:
120 * Always: constant
121 *
122 * \pre not empty ()
123 */
124 nonvirtual void RemoveFirst ();
125
126 public:
127 /**
128 * \note Runtime performance/complexity:
129 * Always: constant
130 *
131 * \pre not empty ()
132 */
133 nonvirtual void RemoveLast ();
134
135 public:
136 /*
137 * \note Runtime performance/complexity:
138 * Worst Case: O(N)
139 * Average Case: O(N)
140 *
141 * Utility to search the list for the given item using EQUALS_COMPARER
142 */
143 template <typename EQUALS_COMPARER = equal_to<T>>
144 nonvirtual bool Contains (ArgByValueType<T> item, const EQUALS_COMPARER& equalsComparer = {}) const;
145
146 public:
147 /**
148 * \note Runtime performance/complexity:
149 * Always: O(N)
150 */
151 template <invocable<T> FUNCTION>
152 nonvirtual void Apply (FUNCTION&& doToElement) const;
153
154 public:
155 /**
156 * \note Runtime performance/complexity:
157 * Worst Case: O(N)
158 * Typical: O(N), but can be less if systematically finding entries near start of container
159 */
160 template <typename FUNCTION>
161 nonvirtual UnderlyingIteratorRep Find (FUNCTION&& firstThat) const;
162
163 public:
164 /**
165 * \note Runtime performance/complexity:
166 * Worst Case: O(N)
167 * Average Case: O(N)
168 *
169 * Note - does nothing if item not found.
170
171 ForwardIterator OVERLOAD:
172 * \note Runtime performance/complexity:
173 * Always: constant
174 *
175 * returns the next link
176 */
177 template <typename EQUALS_COMPARER = equal_to<T>>
178 nonvirtual void Remove (ArgByValueType<T> item, EQUALS_COMPARER&& equalsComparer = {});
179 nonvirtual void Remove (const ForwardIterator& i);
180
181 public:
182 /**
183 * Complexity:
184 * Always: constant
185 *
186 * Returns iterator pointing at next item.
187 */
188 nonvirtual ForwardIterator erase (const ForwardIterator& i);
189
190 public:
191 /**
192 * \note Runtime performance/complexity:
193 * Worst Case: O(N)
194 * Average Case: O(N)
195 */
196 nonvirtual void RemoveAll ();
197
198 public:
199 /**
200 * \note Runtime performance/complexity:
201 * Worst Case: O(N)
202 * Average Case: O(N)
203 */
204 nonvirtual T GetAt (size_t i) const;
205
206 public:
207 /**
208 * \note Runtime performance/complexity:
209 * Worst Case: O(N)
210 * Average Case: O(N)
211 */
212 nonvirtual void SetAt (size_t i, ArgByValueType<T> item);
213
214 public:
215 /*
216 * Support for COW (CopyOnWrite):
217 *
218 * Take iterator 'pi' which is originally a valid iterator from 'movedFrom' - and replace *pi with a valid
219 * iterator from 'this' - which points at the same logical position. This requires that this container
220 * was just 'copied' from 'movedFrom' - and is used to produce an equivalent iterator (since iterators are tied to
221 * the container they were iterating over).
222 */
223 nonvirtual void MoveIteratorHereAfterClone (ForwardIterator* pi, const DoublyLinkedList<T>* movedFrom) const;
224
225 public:
226 /**
227 */
228 nonvirtual ForwardIterator begin () const;
229
230 public:
231 /**
232 */
233 constexpr ForwardIterator end () const noexcept;
234
235 public:
236 /**
237 * \note Runtime performance/complexity:
238 * Always: constant
239 */
240 nonvirtual void SetAt (const ForwardIterator& i, ArgByValueType<T> newValue);
241
242 public:
243 /**
244 * \note Runtime performance/complexity:
245 * Always: constant
246 *
247 * \pre not i.Done ()
248 */
249 nonvirtual void AddBefore (const ForwardIterator& i, ArgByValueType<T> item);
250
251 public:
252 /**
253 * \note Runtime performance/complexity:
254 * Always: constant
255 */
256 nonvirtual void AddAfter (const ForwardIterator& i, ArgByValueType<T> item);
257
258 public:
259 nonvirtual void Invariant () const noexcept;
260
261 private:
262 Link_* fHead_{};
263 Link_* fTail_{};
264
265#if qStroika_Foundation_Debug_AssertionsChecked
266 private:
267 virtual void Invariant_ () const noexcept;
268#endif
269
270 private:
271 friend class ForwardIterator;
272 };
273
274 /**
275 * Just an implementation detail. Don't use directly except in helper classes.
276 * dont use block allocation for link sizes too large
277 */
278 template <typename T>
279 class DoublyLinkedList<T>::Link_ : public Memory::UseBlockAllocationIfAppropriate<Link_, sizeof (T) <= 1024> {
280 public:
281 Link_ () = delete;
282 Link_ (const Link_&) = delete;
283 constexpr Link_ (ArgByValueType<T> item, Link_* prev, Link_* next);
284
285 public:
286 T fItem;
287 Link_* fPrev{nullptr};
288 Link_* fNext{nullptr};
289 };
290
291 /**
292 * ForwardIterator<T> allows you to iterate over a DoublyLinkedList<T>. Its API
293 * is designed to make easy implementations of subclasses of IteratorRep<T>.
294 * It is unpatched - use DoublyLinkedListIterator_Patch<T> or DoublyLinkedListIterator_Patch<T>
295 * for that.
296 */
297 template <typename T>
298 class DoublyLinkedList<T>::ForwardIterator {
299 public:
300 // stuff STL requires you to set to look like an iterator
301 using iterator_category = forward_iterator_tag;
302 using value_type = DoublyLinkedList::value_type;
303 using difference_type = ptrdiff_t;
304 using pointer = const value_type*;
305 using reference = const value_type&;
306
307 public:
308 /**
309 * /0 overload: sets iterator to 'end' - sentinel
310 * /1 (data) overload: sets iterator to begin
311 * /2 (data,startAt) overload: sets iterator to startAt
312 */
313 constexpr ForwardIterator () noexcept = default;
314 explicit constexpr ForwardIterator (const DoublyLinkedList* data) noexcept;
315 explicit constexpr ForwardIterator (const DoublyLinkedList* data, UnderlyingIteratorRep startAt) noexcept;
316 constexpr ForwardIterator (const ForwardIterator&) noexcept = default;
317 constexpr ForwardIterator (ForwardIterator&&) noexcept = default;
318
319 public:
320 nonvirtual ForwardIterator& operator= (const ForwardIterator&) = default;
321 nonvirtual ForwardIterator& operator= (ForwardIterator&&) noexcept = default;
322
323 public:
324 /**
325 * return true if iterator not Done
326 */
327 explicit operator bool () const;
328
329 public:
330 nonvirtual bool Done () const noexcept;
331
332 public:
333 nonvirtual const T& operator* () const;
334
335 public:
336 nonvirtual const T* operator->() const;
337
338 public:
339 nonvirtual ForwardIterator& operator++ () noexcept;
340 nonvirtual ForwardIterator operator++ (int) noexcept;
341
342 /**
343 * \note Runtime performance/complexity:
344 * Average/WorseCase: O(N) - super slow cuz have to traverse on average half the list
345 *
346 * \pre data == fData_ argument constructed with (or as adjusted by Move...); api takes extra param so release builds need not store fData_
347 */
348 nonvirtual size_t CurrentIndex (const DoublyLinkedList* data) const;
349
350 public:
351 nonvirtual UnderlyingIteratorRep GetUnderlyingIteratorRep () const;
352
353 public:
354 nonvirtual void SetUnderlyingIteratorRep (UnderlyingIteratorRep l);
355
356 public:
357 /**
358 * For debugging, assert the iterator data matches argument data
359 */
360 constexpr void AssertDataMatches (const DoublyLinkedList* data) const;
361
362 public:
363 nonvirtual bool operator== (const ForwardIterator& rhs) const;
364
365 public:
366 nonvirtual void Invariant () const noexcept;
367
368 private:
369 const Link_* fCurrent_{nullptr};
370#if qStroika_Foundation_Debug_AssertionsChecked
371 const DoublyLinkedList* fData_{nullptr};
372#endif
373
374#if qStroika_Foundation_Debug_AssertionsChecked
375 private:
376 nonvirtual void Invariant_ () const noexcept;
377#endif
378
379 private:
380 friend class DoublyLinkedList;
381 };
382
383 static_assert (ranges::input_range<DoublyLinkedList<int>>); // smoke test - make sure basic iteration etc should work (allows formattable to work)
384
385}
386
387/*
388 ********************************************************************************
389 ***************************** Implementation Details ***************************
390 ********************************************************************************
391 */
392#include "DoublyLinkedList.inl"
393
394#endif /*_Stroika_Foundation_Containers_DataStructures_DoublyLinkedList_h_ */
conditional_t< qStroika_Foundation_Memory_PreferBlockAllocation and andTrueCheck, BlockAllocationUseHelper< T >, Common::Empty > UseBlockAllocationIfAppropriate
Use this to enable block allocation for a particular class. Beware of subclassing.
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:32
constexpr void AssertDataMatches(const DoublyLinkedList *data) const
nonvirtual size_t CurrentIndex(const DoublyLinkedList *data) const