Stroika Library 3.0d24
 
Loading...
Searching...
No Matches
LinkedList.h
1/*
2 * Copyright(c) Sophist Solutions, Inc. 1990-2026. All rights reserved
3 */
4#ifndef _Stroika_Foundation_Containers_DataStructures_LinkedList_h_
5#define _Stroika_Foundation_Containers_DataStructures_LinkedList_h_
6
7#include "Stroika/Foundation/StroikaPreComp.h"
8
9#include <optional>
10
11#include "Stroika/Foundation/Common/Common.h"
14#include "Stroika/Foundation/Containers/Common.h"
17
18/**
19 * LinkedList<T,TRAITS> is a backend implementation. It is not intended to be directly
20 * used by programmers, except in implementing concrete container reps.
21 *
22 * TODO:
23 * @todo Include Performance numbers for each operation (done for many).
24 * @todo https://github.com/SophistSolutions/Stroika/issues/1148 (STK-1016) - ranges/sentinel support
25 *
26 * \note Code-Status: <a href="Code-Status.md#Beta">Beta</a>
27 *
28 */
29
31
33
34 /**
35 * LinkedList<T,TRAITS> is a generic link (non-intrusive) list implementation (similar to std::forward_list).
36 * We provide no public means to access the links themselves.
37 *
38 * \note \em Thread-Safety <a href="Thread-Safety.md#C++-Standard-Thread-Safety">C++-Standard-Thread-Safety</a>
39 */
40 template <typename T>
42 public:
43 using value_type = T;
44
45 public:
46 /**
47 */
48 LinkedList ();
49 LinkedList (LinkedList&& src) noexcept;
50 LinkedList (const LinkedList& src);
51 ~LinkedList ();
52
53 public:
54 nonvirtual LinkedList& operator= (const LinkedList& list);
55
56 public:
57 class ForwardIterator;
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 /**
70 * Support for COW (CopyOnWrite):
71 *
72 * Take iterator 'pi' which is originally a valid iterator from 'movedFrom' - and replace *pi with a valid
73 * iterator from 'this' - which points at the same logical position. This requires that this container
74 * was just 'copied' from 'movedFrom' - and is used to produce an equivalent iterator (since iterators are tied to
75 * the container they were iterating over).
76 */
77 nonvirtual void MoveIteratorHereAfterClone (ForwardIterator* pi, const LinkedList* movedFrom) const;
78
79 public:
80 /**
81 */
82 nonvirtual ForwardIterator begin () const;
83
84 public:
85 /**
86 */
87 constexpr ForwardIterator end () const noexcept;
88
89 public:
90 /**
91 * \note Runtime performance/complexity:
92 * Always: constant
93 */
94 nonvirtual bool empty () const;
95
96 public:
97 /**
98 * \note Runtime performance/complexity:
99 * Always: constant
100 *
101 * \note The length is cached (one size_t per list), not counted - see the ReadMe on why
102 * the DataStructures classes keep size () constant-time: the Stroika containers built
103 * on them guarantee that to their callers, and can only do so if the backend does.
104 */
105 nonvirtual size_t size () const;
106
107 public:
108 /**
109 * Complexity:
110 * Always: constant
111 */
112 nonvirtual optional<T> GetFirst () const;
113
114 public:
115 /**
116 * @aliases Prepend
117 *
118 * Complexity:
119 * Always: constant
120 *
121 * \see push_back
122 *
123 * \note for push_front(span) - this puts the span elements in front in the same order in
124 * which they appears in the span
125 */
126 nonvirtual void push_front (ArgByValueType<T> item);
127 template <Memory::ISpanOfT<T> SPAN_T>
128 nonvirtual void push_front (const SPAN_T& copyFrom);
129
130 public:
131 /**
132 * Complexity:
133 * Always: constant
134 */
135 nonvirtual void RemoveFirst ();
136
137 public:
138 /**
139 * \note Runtime performance/complexity:
140 * Always: O(N)
141 */
142 template <invocable<T> FUNCTION>
143 nonvirtual void Apply (FUNCTION&& doToElement) const;
144
145 public:
146 /**
147 * \note Runtime performance/complexity:
148 * Worst Case: O(N)
149 * Typical: O(N), but can be less if systematically finding entries near start of container
150 *
151 * Complexity EQUALS_COMPARER OVERLOAD:
152 * Worst Case: O(N)
153 * Average Case: O(N)
154 *
155 * EQUALS_COMPARER OVERLOAD : Returns pointer to T (or nullptr if not found). Lifetime of T* only til next call on this.
156 *
157 * @aliases Lookup, First, Contains (sort of)
158 */
159 template <predicate<T> FUNCTION>
160 nonvirtual UnderlyingIteratorRep Find (FUNCTION&& firstThat) const;
161 template <typename EQUALS_COMPARER = equal_to<T>>
162 nonvirtual const T* Find (ArgByValueType<T> item, EQUALS_COMPARER&& equalsComparer = {}) const;
163 template <typename EQUALS_COMPARER = equal_to<T>>
164 nonvirtual T* Find (ArgByValueType<T> item, EQUALS_COMPARER&& equalsComparer = {});
165
166 public:
167 /**
168 * Complexity:
169 * Always: constant
170 *
171 * \note lifetime of returned pointer only extends til the start of the next non-const call to this LinkedList object
172 */
173 nonvirtual T* PeekAt (const ForwardIterator& i);
174
175 public:
176 /**
177 * Complexity:
178 * Always: constant
179 */
180 nonvirtual void SetAt (const ForwardIterator& i, ArgByValueType<T> newValue);
181
182 public:
183 /**
184 * Complexity:
185 * Always: constant
186 *
187 * NB: Can be called if done
188 */
189 nonvirtual void AddBefore (const ForwardIterator& i, ArgByValueType<T> item);
190 nonvirtual void AddBefore (const ForwardIterator& i, ArgByValueType<T> item, ForwardIterator* newLinkCreatedAt);
191
192 public:
193 /**
194 * Complexity:
195 * Always: constant
196 */
197 nonvirtual void AddAfter (const ForwardIterator& i, ArgByValueType<T> item);
198
199 public:
200 /**
201 * EQUALS_COMPARER overload: Note - does nothing if item not found.
202 *
203 * Complexity (ForwardIterator overload):
204 * Always: constant
205 */
206 template <typename EQUALS_COMPARER>
207 nonvirtual void Remove (ArgByValueType<T> item, const EQUALS_COMPARER& equalsComparer);
208 nonvirtual void Remove (const ForwardIterator& i);
209
210 public:
211 /**
212 * Complexity:
213 * Always: constant
214 *
215 * Returns iterator pointing at next item.
216 */
217 nonvirtual ForwardIterator erase (const ForwardIterator& i);
218
219 public:
220 /**
221 * Complexity:
222 * Always: O(N) - but generally quite quick cuz uses block (de-)allocation
223 */
224 nonvirtual void clear ();
225
226 public:
227 /**
228 * @aliases Append
229 *
230 * Complexity:
231 * Always: O(N)
232 *
233 * Not a lot of point in having this method, as is terribly slow, but it could be convenient.
234 *
235 * \note for push_back(span) - this puts the span elements in back in the same order in
236 * which they appears in the span.
237 *
238 * \see push_front
239 */
240 nonvirtual void push_back (ArgByValueType<T> item);
241 template <Memory::ISpanOfT<T> SPAN_T>
242 nonvirtual void push_back (const SPAN_T& copyFrom);
243
244 public:
245 /**
246 * Complexity:
247 * Always: O(N)
248 *
249 * Not a lot of point in having this method, as is terribly slow, but it could be convenient.
250 */
251 nonvirtual T GetAt (size_t i) const;
252
253 public:
254 /**
255 * Complexity:
256 * Always: O(i)
257 *
258 * Not a lot of point in having this method, as is terribly slow, but it could be convenient.
259 */
260 nonvirtual void SetAt (T item, size_t i);
261
262 public:
263 /**
264 */
265 nonvirtual void Invariant () const noexcept;
266
267 private:
268 Link_* fHead_{nullptr};
269 // Cached so size () is O(1) - see the note on size (). One size_t per LIST, not per link.
270 // Invariant_ () validates fLength_.
271 size_t fLength_{0};
272
273#if qStroika_Foundation_Debug_AssertionsChecked
274 private:
275 virtual void Invariant_ () const noexcept;
276#endif
277
278 private:
279 friend class ForwardIterator;
280 };
281
282 /**
283 * dont use block allocation for link sizes too large
284 */
285 template <typename T>
286 class LinkedList<T>::Link_ : public Memory::UseBlockAllocationIfAppropriate<Link_, sizeof (T) <= 256> {
287 public:
288 Link_ () = delete;
289 constexpr Link_ (ArgByValueType<T> item, Link_* next);
290 Link_ (const Link_&) = delete;
291
292 public:
293 T fItem;
294 Link_* fNext{nullptr};
295 };
296
297 /*
298 * ForwardIterator allows you to iterate over a LinkedList<T>. It is not safe to use a ForwardIterator after any
299 * update to the LinkedList.
300 */
301 template <typename T>
302 class LinkedList<T>::ForwardIterator {
303 public:
304 // stuff STL requires you to set to look like an iterator
305 using iterator_category = forward_iterator_tag;
306 using value_type = LinkedList::value_type;
307 using difference_type = ptrdiff_t;
308 using pointer = const value_type*;
309 using reference = const value_type&;
310
311 public:
312 /**
313 * /0 overload: sets iterator to 'end' - sentinel
314 * /1 (data) overload: sets iterator to begin
315 * /2 (data,startAt) overload: sets iterator to startAt
316 */
317 constexpr ForwardIterator () noexcept = default;
318 explicit constexpr ForwardIterator (const LinkedList* data) noexcept;
319 explicit constexpr ForwardIterator (const LinkedList* data, UnderlyingIteratorRep startAt) noexcept;
320 constexpr ForwardIterator (const ForwardIterator&) noexcept = default;
321 constexpr ForwardIterator (ForwardIterator&&) noexcept = default;
322
323 public:
324 nonvirtual ForwardIterator& operator= (const ForwardIterator&) = default;
325 nonvirtual ForwardIterator& operator= (ForwardIterator&&) noexcept = default;
326
327 public:
328 /**
329 * return true if iterator not AtEnd
330 */
331 explicit operator bool () const;
332
333 public:
334 nonvirtual bool AtEnd () const noexcept;
335
336 public:
337 nonvirtual ForwardIterator& operator++ () noexcept;
338 nonvirtual ForwardIterator operator++ (int) noexcept;
339
340 public:
341 nonvirtual T operator* () const;
342
343 public:
344 nonvirtual const T* operator->() const;
345
346 public:
347 /**
348 * \note Runtime performance/complexity:
349 * Average/WorseCase: O(N) - super slow cuz have to traverse on average half the list
350 *
351 * \pre data == fData_ argument constructed with (or as adjusted by Move...); api takes extra param so release builds need not store fData_
352 */
353 nonvirtual size_t CurrentIndex (const LinkedList* data) const;
354
355 public:
356 nonvirtual UnderlyingIteratorRep GetUnderlyingIteratorRep () const;
357
358 public:
359 nonvirtual void SetUnderlyingIteratorRep (const UnderlyingIteratorRep l);
360
361 public:
362 /**
363 * For debugging, assert the iterator data matches argument data
364 */
365 constexpr void AssertDataMatches (const LinkedList* data) const;
366
367 public:
368 nonvirtual bool operator== (const ForwardIterator& rhs) const;
369
370 public:
371 nonvirtual void Invariant () const noexcept;
372
373 private:
374 const Link_* fCurrent_{nullptr};
375#if qStroika_Foundation_Debug_AssertionsChecked
376 const LinkedList* fData_{nullptr};
377#endif
378
379#if qStroika_Foundation_Debug_AssertionsChecked
380 private:
381 nonvirtual void Invariant_ () const noexcept;
382#endif
383
384 private:
385 friend class LinkedList;
386 };
387
388 static_assert (ranges::input_range<LinkedList<int>>); // smoke test - make sure basic iteration etc should work (allows formattable to work)
389
390}
391
392/*
393 ********************************************************************************
394 ***************************** Implementation Details ***************************
395 ********************************************************************************
396 */
397#include "LinkedList.inl"
398
399#endif /*_Stroika_Foundation_Containers_DataStructures_LinkedList_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 Remove(ArgByValueType< T > item, const EQUALS_COMPARER &equalsComparer)
nonvirtual void AddBefore(const ForwardIterator &i, ArgByValueType< T > item)
nonvirtual void SetAt(const ForwardIterator &i, ArgByValueType< T > newValue)
nonvirtual void MoveIteratorHereAfterClone(ForwardIterator *pi, const LinkedList *movedFrom) const
nonvirtual void push_back(ArgByValueType< T > item)
nonvirtual void push_front(ArgByValueType< T > item)
nonvirtual T * PeekAt(const ForwardIterator &i)
nonvirtual UnderlyingIteratorRep Find(FUNCTION &&firstThat) const
nonvirtual ForwardIterator erase(const ForwardIterator &i)
nonvirtual void Apply(FUNCTION &&doToElement) 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