Stroika Library 3.0d24
 
Loading...
Searching...
No Matches
Sequence.inl
1/*
2 * Copyright(c) Sophist Solutions, Inc. 1990-2026. All rights reserved
3 */
4
5#include <algorithm>
6#include <execution>
7
11#include "Stroika/Foundation/Containers/Private/IterableUtils.h"
15
17
18 /*
19 ********************************************************************************
20 ******************** Sequence<T>::TemporaryElementReference_ *******************
21 ********************************************************************************
22 */
23 /*
24 * TemporaryElementReference_ is a private implementation detail, so we can do:
25 * Sequence<int> x; // and initialize with several items, and then
26 * x[3] = 4;
27 *
28 * We need two templated variants - one inheriting and one not - to handle the fact that some people want to call a method
29 * on T, as in:
30 *
31 * Sequence<String> x;
32 * size_t a = x[3].length (); // wont work if we use aggregating variant of TemporaryElementReference_
33 * // e.g: error: �struct Stroika::Foundation::Containers::Sequence<Stroika::Foundation::Characters::String>::TemporaryElementReference_� has no member named �Trim�
34 */
35 template <typename T>
36 struct Sequence<T>::TemporaryElementReference_ : conditional_t<is_class_v<T> or is_union_v<T>, T, Common::Empty> {
37 private:
38 static constexpr bool kSubClass_ = is_class_v<T> or is_union_v<T>;
39 Sequence<T>* fV;
40 size_t fIndex_;
42
43 public:
44 TemporaryElementReference_ (const TemporaryElementReference_&) = default;
45 TemporaryElementReference_ (TemporaryElementReference_&& src)
46 : fV{move (src.fV)}
47 , fIndex_{src.fIndex_}
48 , fValue_{move (src.fValue_)}
49 {
50 src.fV = nullptr; // it no longer writes on its DTOR
51 }
52 TemporaryElementReference_ (Sequence<T>* s, size_t i)
53 : fV{(RequireExpression (s != nullptr), s)}
54 , fIndex_{i}
55 {
56 if constexpr (kSubClass_) {
57 *static_cast<T*> (this) = s->GetAt (i);
58 }
59 else {
60 fValue_ = s->GetAt (i);
61 }
62 }
63 TemporaryElementReference_& operator= (const TemporaryElementReference_&) = delete;
64 TemporaryElementReference_& operator= (TemporaryElementReference_&&) = delete;
65 TemporaryElementReference_& operator= (ArgByValueType<T> v)
66 {
67 RequireNotNull (fV);
68 if constexpr (kSubClass_) {
69 *static_cast<T*> (this) = v;
70 }
71 else {
72 fValue_ = v;
73 }
74 return *this;
75 }
76 operator T () const
77 requires (not(kSubClass_))
78 {
79 RequireNotNull (fV);
80 if constexpr (kSubClass_) {
81 return *static_cast<T*> (this);
82 }
83 else {
84 return fValue_;
85 }
86 }
87 operator T&()
88 requires (not(kSubClass_))
89 {
90 RequireNotNull (fV);
91 if constexpr (kSubClass_) {
92 return *static_cast<T*> (this);
93 }
94 else {
95 return fValue_;
96 }
97 }
98 // Tried this for https://github.com/SophistSolutions/Stroika/issues/1151 (STK-1024) - but didn't help
99 // and dont much like anyhow
100 //auto ToString () const
101 //{
102 // RequireNotNull (fV);
103 // if constexpr (kSubClass_) {
104 // return Characters::UnoverloadedToString (*static_cast<T*> (this));
105 // }
106 // else {
107 // return Characters::UnoverloadedToString (fValue_);
108 // }
109 //}
110 ~TemporaryElementReference_ ()
111 {
112 // now remaining problem with this strategy is that if we have
113 // String a = sequence[i] = the temporary may get MOVE()d to 'a', and so *this is now invalid, and cannot be used in a set.
114 // We don't need to set in that case, but we have no way to reliably tell that we got moved.
115
116 // needed cuz modifications CAN come from from something like Sequence<String> x; x[1].clear ();
117 if (fV != nullptr) {
118 if constexpr (kSubClass_) {
119 IgnoreExceptionsForCall (fV->SetAt (fIndex_, *((T*)this)));
120 }
121 else {
122 IgnoreExceptionsForCall (fV->SetAt (fIndex_, fValue_));
123 }
124 }
125 }
126 };
127
128 /*
129 ********************************************************************************
130 ******** Sequence<T>::_IRep::IndexBasedRandomAccessIteratorRep_ ***************
131 ********************************************************************************
132 */
133 /*
134 * Generic RandomAccessIterator<T>::IRep, implemented purely in terms of _IRep::GetAt ()/size () -
135 * so it works for ANY Sequence<T> backend, but at the cost of a GetAt () call per step (which could
136 * be O(n) for a backend like Sequence_LinkedList).
137 */
138 template <typename T>
139 class Sequence<T>::_IRep::IndexBasedRandomAccessIteratorRep_ : public RandomAccessIterator<T>::IRep {
140 public:
141 IndexBasedRandomAccessIteratorRep_ (const _IRep* rep, size_t idx)
142 : fRep_{rep}
143 , fIdx_{idx}
144 {
145 RequireNotNull (rep);
146 Require (idx <= rep->size ());
147 }
148 IndexBasedRandomAccessIteratorRep_ (const IndexBasedRandomAccessIteratorRep_&) = default;
149
150 public:
151 virtual unique_ptr<typename Iterator<T>::IRep> Clone () const override
152 {
154 }
155 virtual bool AtEnd () const override
156 {
157 return fIdx_ >= fRep_->size ();
158 }
159 virtual optional<T> Current () const override
160 {
161 if (fIdx_ >= fRep_->size ()) {
162 return nullopt;
163 }
164 return fRep_->GetAt (fIdx_);
165 }
166 virtual optional<T> More () override
167 {
168 Require (not AtEnd ());
169 ++fIdx_;
170 return Current ();
171 }
172 virtual bool Equals (const typename Iterator<T>::IRep* rhs) const override
173 {
175 const auto* r = Debug::UncheckedDynamicCast<const IndexBasedRandomAccessIteratorRep_*> (rhs);
176 return fIdx_ == r->fIdx_;
177 }
178
179 public:
180 // BidirectionalIterator<T>::IRep
181 virtual bool AtStart () const override
182 {
183 return fIdx_ == 0;
184 }
185 virtual T Back () override
186 {
187 Require (not AtStart ());
188 --fIdx_;
189 return fRep_->GetAt (fIdx_);
190 }
191
192 public:
193 // RandomAccessIterator<T>::IRep
194 virtual void Advance (ptrdiff_t i) override
195 {
196 Require (i >= 0 or static_cast<size_t> (-i) <= fIdx_);
197 fIdx_ = static_cast<size_t> (static_cast<ptrdiff_t> (fIdx_) + i);
198 }
199 virtual ptrdiff_t Difference (const typename RandomAccessIterator<T>::IRep* rhs) const override
200 {
201 if (rhs == nullptr) {
202 return static_cast<ptrdiff_t> (fIdx_) - static_cast<ptrdiff_t> (fRep_->size ());
203 }
204 const auto* r = Debug::UncheckedDynamicCast<const IndexBasedRandomAccessIteratorRep_*> (rhs);
205 return static_cast<ptrdiff_t> (fIdx_) - static_cast<ptrdiff_t> (r->fIdx_);
206 }
207 virtual const T* PeekAtElement (ptrdiff_t i) const override
208 {
209 fPeekCache_ = fRep_->GetAt (static_cast<size_t> (static_cast<ptrdiff_t> (fIdx_) + i));
210 return &*fPeekCache_;
211 }
212
213 private:
214 const _IRep* fRep_;
215 size_t fIdx_;
216 mutable optional<T> fPeekCache_;
217 };
218
219 /*
220 ********************************************************************************
221 **************************** Sequence<T>::_IRep ********************************
222 ********************************************************************************
223 */
224 template <typename T>
229 template <typename T>
234
235 /*
236 ********************************************************************************
237 ******************************** Sequence<T> ***********************************
238 ********************************************************************************
239 */
240 template <typename T>
242 : inherited{Factory::Sequence_Factory<T>::Default () ()}
243 {
244 _AssertRepValidType ();
245 }
246 template <typename T>
247 inline Sequence<T>::Sequence (const initializer_list<value_type>& src)
248 : Sequence{}
249 {
250 AppendAll (src);
251 _AssertRepValidType ();
253#if !qCompilerAndStdLib_RequiresNotMatchInlineOutOfLineForTemplateClassBeingDefined_Buggy
254 template <typename T>
255 template <IIterableOfTo<T> ITERABLE_OF_ADDABLE>
256 requires (not derived_from<remove_cvref_t<ITERABLE_OF_ADDABLE>, Sequence<T>>)
257 inline Sequence<T>::Sequence (ITERABLE_OF_ADDABLE&& src)
258 : Sequence{}
260 AppendAll (forward<ITERABLE_OF_ADDABLE> (src));
261 _AssertRepValidType ();
262 }
263#endif
264 template <typename T>
265 inline Sequence<T>::Sequence (const shared_ptr<_IRep>& rep) noexcept
266 : inherited{(RequireExpression (rep != nullptr), rep)}
267 {
268 _AssertRepValidType ();
269 }
270 template <typename T>
271 inline Sequence<T>::Sequence (shared_ptr<_IRep>&& rep) noexcept
272 : inherited{(RequireExpression (rep != nullptr), move (rep))}
273 {
274 _AssertRepValidType ();
275 }
276 template <typename T>
277 template <IInputIterator<T> ITERATOR_OF_ADDABLE, sentinel_for<remove_cvref_t<ITERATOR_OF_ADDABLE>> ITERATOR_OF_ADDABLE2>
278 inline Sequence<T>::Sequence (ITERATOR_OF_ADDABLE&& start, ITERATOR_OF_ADDABLE2&& end)
279 : Sequence{}
280 {
281 AppendAll (forward<ITERATOR_OF_ADDABLE> (start), forward<ITERATOR_OF_ADDABLE2> (end));
282 _AssertRepValidType ();
283 }
284 template <typename T>
286 {
287 return _SafeReadRepAccessor<_IRep>{this}._ConstGetRep ().GetBidirectionalIterator ();
288 }
289 template <typename T>
291 {
292 return _SafeReadRepAccessor<_IRep>{this}._ConstGetRep ().GetRandomAccessIterator ();
293 }
294 template <typename T>
295 template <typename RESULT_CONTAINER, invocable<T> ELEMENT_MAPPER>
296 nonvirtual RESULT_CONTAINER Sequence<T>::Map (ELEMENT_MAPPER&& elementMapper) const
297 requires (convertible_to<invoke_result_t<ELEMENT_MAPPER, T>, typename RESULT_CONTAINER::value_type> or
298 convertible_to<invoke_result_t<ELEMENT_MAPPER, T>, optional<typename RESULT_CONTAINER::value_type>>)
299 {
300 if constexpr (same_as<RESULT_CONTAINER, Sequence>) {
301 // clone the rep so we retain the rep type
302 return inherited::template Map<RESULT_CONTAINER> (forward<ELEMENT_MAPPER> (elementMapper),
303 RESULT_CONTAINER{_SafeReadRepAccessor<_IRep>{this}._ConstGetRep ().CloneEmpty ()});
304 }
305 else {
306 return inherited ::template Map<RESULT_CONTAINER> (forward<ELEMENT_MAPPER> (elementMapper));
307 }
308 }
309 template <typename T>
310 template <derived_from<Iterable<T>> RESULT_CONTAINER, predicate<T> INCLUDE_PREDICATE>
311 inline RESULT_CONTAINER Sequence<T>::Where (INCLUDE_PREDICATE&& includeIfTrue) const
312 {
313 if constexpr (same_as<RESULT_CONTAINER, Sequence>) {
314 // clone the rep so we retain the rep type
315 return inherited::template Where<RESULT_CONTAINER> (
316 forward<INCLUDE_PREDICATE> (includeIfTrue), RESULT_CONTAINER{_SafeReadRepAccessor<_IRep>{this}._ConstGetRep ().CloneEmpty ()});
317 }
318 else {
319 return inherited::template Where<RESULT_CONTAINER> (forward<INCLUDE_PREDICATE> (includeIfTrue));
320 }
321 }
322 template <typename T>
323 template <IPotentiallyComparer<T> INORDER_COMPARER_TYPE>
324 inline auto Sequence<T>::OrderBy (INORDER_COMPARER_TYPE&& inorderComparer) const -> Sequence
325 {
326 // @todo measure the crossover and auto-choose the policy here - eSeq is a placeholder, not a decision
327 return OrderBy (forward<INORDER_COMPARER_TYPE> (inorderComparer), Execution::SequencePolicy::eSeq);
328 }
329 template <typename T>
330 template <IPotentiallyComparer<T> INORDER_COMPARER_TYPE>
331 auto Sequence<T>::OrderBy (INORDER_COMPARER_TYPE&& inorderComparer, [[maybe_unused]] Execution::SequencePolicy seq) const -> Sequence
332 {
333 /*
334 * The copy into a vector<T> is unavoidable, not merely unoptimized. Sorting has to relocate elements,
335 * so it must be able to WRITE through the iterators: std::stable_sort requires them to be
336 * Cpp17ValueSwappable with a MoveAssignable value type (the equivalent for std::ranges::stable_sort
337 * is the std::sortable / std::permutable concept). Stroika's iterators are deliberately read-only -
338 * Iterator<T>::operator* and RandomAccessIterator<T>::operator[] both hand back a const T& - because
339 * exposing T& would break the copy-on-write sharing (see the note on Sequence<T>::operator[]).
340 *
341 * So this is about the CONSTNESS of the iterators, not their category: giving Sequence better (eg
342 * random-access) iterators does not help, and an earlier @todo here claiming otherwise was wrong.
343 *
344 * The copy is cheap-ish in the end, because Sequence_stdvector adopts the vector by move.
345 */
346 vector<T> tmp = this->As<vector<T>> ();
347 switch (seq) {
348#if __cpp_lib_execution >= 201603L
349 case Execution::SequencePolicy::ePar:
350 stable_sort (std::execution::par, tmp.begin (), tmp.end (), forward<INORDER_COMPARER_TYPE> (inorderComparer));
351 break;
352 // @todo add other Execution::SequencePolicy cases
353#endif
354 default:
355 stable_sort (tmp.begin (), tmp.end (), forward<INORDER_COMPARER_TYPE> (inorderComparer));
356 break;
357 }
358 return Concrete::Sequence_stdvector<T>{move (tmp)};
359 }
360 template <typename T>
362 {
363 _SafeReadRepAccessor<_IRep> accessor{this}; // important to use READ not WRITE accessor, because write accessor would have already cloned the data
364 if (not accessor._ConstGetRep ().empty ()) {
365 this->_fRep = accessor._ConstGetRep ().CloneEmpty ();
366 }
368 template <typename T>
369 template <predicate<T> PREDICATE>
370 size_t Sequence<T>::RemoveAll (PREDICATE&& p)
371 {
372 // @todo Consider migrating this method to _IRep? Doing so would allow for different (e.g. vector) implementations
373 // to be more efficient (for example, bubbling last to first); but at a small code-bloat cost, so not likely
374 // worthwhile tradeoff; if this is a performance issue, convert to Sequence_LinkedList{s}.RemoveAll(p) and then convert
375 // back to whatever backend-implementation sequence you wish... --LGP 2022-12-14
376 size_t nRemoved{};
377 for (Iterator<T> i = this->begin (); i != this->end ();) {
378 if (p (*i)) {
379 Remove (i, &i);
380 ++nRemoved;
381 }
382 else {
383 ++i;
384 }
385 }
386 return nRemoved;
387 }
388 template <typename T>
389 inline auto Sequence<T>::GetAt (size_t i) const -> value_type
390 {
391 _SafeReadRepAccessor<_IRep> accessor{this};
392 Require (i < accessor._ConstGetRep ().size ());
393 return accessor._ConstGetRep ().GetAt (i);
394 }
395 template <typename T>
396 inline void Sequence<T>::SetAt (size_t i, ArgByValueType<value_type> item)
397 {
398 _SafeReadWriteRepAccessor<_IRep> accessor{this};
399 Require (i < accessor._ConstGetRep ().size ());
400 accessor._GetWriteableRep ().SetAt (i, item);
401 }
402 template <typename T>
403 inline auto Sequence<T>::operator[] (size_t i) const -> const value_type
404 {
405 _SafeReadRepAccessor<_IRep> accessor{this};
406 Require (i < accessor._ConstGetRep ().size ());
407 return accessor._ConstGetRep ().GetAt (i);
408 }
409 template <typename T>
410 inline auto Sequence<T>::operator() (size_t i) -> TemporaryElementReference_
411 {
412 return TemporaryElementReference_{this, i};
413 }
414 template <typename T>
415 template <Common::IEqualsComparer<T> EQUALS_COMPARER>
416 inline optional<size_t> Sequence<T>::IndexOf (ArgByValueType<value_type> item, EQUALS_COMPARER&& equalsComparer) const
417 {
418 /*
419 * FAST PATH - an index is exactly what a contiguous buffer yields for free (pointer difference),
420 * where the general path below asks Find () to carry a counting side effect in its predicate
421 * (see Private::IndexOf_ ()) - so it pays type erasure into std::function, per-element virtual
422 * iteration, and an Iterator<T> construction, all to produce a number.
423 *
424 * Deliberately duplicated rather than shared with Iterable<T>::Contains (): a common helper
425 * would have to distinguish "backend has no contiguous storage" from "searched and not found",
426 * ie return a nested optional, which reads far worse than these few lines do twice.
427 *
428 * The index is the FIRST match in iteration order, as this method requires - overriders of
429 * PeekContiguousStorage () must hand back storage in iteration order, which is the same
430 * precondition As<> () and SequentialEquals () rely on.
431 */
432 {
433 _SafeReadRepAccessor<_IRep> accessor{this};
434 if (auto s = accessor._ConstGetRep ().PeekContiguousStorage ()) {
435 auto i = [&] () {
436 if constexpr (same_as<remove_cvref_t<EQUALS_COMPARER>, equal_to<T>> or same_as<remove_cvref_t<EQUALS_COMPARER>, equal_to<>>) {
437 return std::find (s->begin (), s->end (), item);
438 }
439 else {
440 return std::find_if (s->begin (), s->end (), [&] (const T& e) { return equalsComparer (e, item); });
441 }
442 }();
443 if (i == s->end ()) {
444 return optional<size_t>{};
445 }
446 return static_cast<size_t> (i - s->begin ());
447 }
448 }
449 return Private::IndexOf_<T, EQUALS_COMPARER> (*this, item, forward<EQUALS_COMPARER> (equalsComparer));
451 template <typename T>
452 template <Common::IEqualsComparer<T> EQUALS_COMPARER>
453 inline optional<size_t> Sequence<T>::IndexOf (const Sequence& s, EQUALS_COMPARER&& equalsComparer) const
454 {
455 return Private::IndexOf_<T, EQUALS_COMPARER> (*this, s, forward<EQUALS_COMPARER> (equalsComparer));
456 }
457 template <typename T>
458 template <typename IGNORED>
459 inline size_t Sequence<T>::IndexOf (const Iterator<value_type>& i) const
460 {
461 return _SafeReadRepAccessor<_IRep>{this}._ConstGetRep ().IndexOf (i);
462 }
463 template <typename T>
464 inline void Sequence<T>::Insert (size_t i, ArgByValueType<value_type> item)
465 {
466 _SafeReadWriteRepAccessor<_IRep> accessor{this};
467 Require (i <= accessor._ConstGetRep ().size ());
468 return accessor._GetWriteableRep ().Insert (i, span{&item, 1u});
469 }
470 template <typename T>
471 inline void Sequence<T>::Insert (const Iterator<value_type>& i, ArgByValueType<value_type> item)
472 {
473 // an AtEnd () iterator means append. Note this must be checked BEFORE calling _IRep::IndexOf (), because the
474 // end () sentinel (Iterator{default_sentinel}) has a nullptr rep, and IndexOf () downcasts i.ConstGetRep ()
475 if (i.AtEnd ()) {
476 Append (item);
477 return;
478 }
479 _SafeReadWriteRepAccessor<_IRep> accessor{this};
480 size_t idx = accessor._ConstGetRep ().IndexOf (i);
481 Require (idx <= accessor._ConstGetRep ().size ());
482 return accessor._GetWriteableRep ().Insert (idx, span{&item, 1u});
483 }
484 template <typename T>
485 inline void Sequence<T>::insert (const Iterator<value_type>& i, ArgByValueType<value_type> item)
486 {
487 Insert (i, item);
488 }
489 template <typename T>
490 template <IInputIterator<T> ITERATOR_OF_ADDABLE, sentinel_for<ITERATOR_OF_ADDABLE> ITERATOR_OF_ADDABLE2>
491 void Sequence<T>::InsertAll (size_t i, ITERATOR_OF_ADDABLE&& start, ITERATOR_OF_ADDABLE2&& end)
492 {
493 Require (i <= this->size ());
494 /*
495 * FAST PATH - the same three-part test as AppendAll (), and the same one-dispatch-per-range
496 * payoff, but here it fixes an ASYMPTOTIC cost and not just a constant one.
497 *
498 * The loop below inserts at an ADVANCING index, so every Insert () shifts whatever follows
499 * position i: inserting m elements ahead of n existing ones moves those n elements m separate
500 * times, O (m*n). One span insert shifts that tail exactly once, O (m+n).
501 *
502 * \note This is invisible when the target is EMPTY - then the advancing index lands at the end
503 * every time and shifts nothing, so the loop is already O (m). It only bites when
504 * inserting ahead of existing elements, which is exactly what PrependAll () does.
505 */
506 if constexpr (contiguous_iterator<remove_cvref_t<ITERATOR_OF_ADDABLE>> and
507 sized_sentinel_for<remove_cvref_t<ITERATOR_OF_ADDABLE2>, remove_cvref_t<ITERATOR_OF_ADDABLE>> and
508 same_as<remove_cvref_t<iter_value_t<remove_cvref_t<ITERATOR_OF_ADDABLE>>>, T>) {
509 if (start != end) [[likely]] {
510 _SafeReadWriteRepAccessor<_IRep>{this}._GetWriteableRep ().Insert (
511 i, span<const T>{to_address (start), static_cast<size_t> (end - start)});
512 }
514 else {
515 /*
516 * Chunked, for the same reasons as AppendAll () - see the long comment there. Here it also
517 * restores the ASYMPTOTICS for a non-contiguous source: inserting element-at-a-time at an
518 * advancing index shifts the tail once per element, O (m*n), and chunking makes that once per
519 * chunk. Measured 123x (int, 1000 elements) for a list source versus a vector one, which is
520 * the single largest gap any of these probes found.
521 */
522 constexpr size_t kChunkSize_ = Memory::StackBuffer<T>::kMinCapacity;
524 _SafeReadWriteRepAccessor<_IRep> accessor{this};
525 size_t insertAt = i;
526 for (auto ii = forward<ITERATOR_OF_ADDABLE> (start); ii != forward<ITERATOR_OF_ADDABLE2> (end); ++ii) {
527 buf.push_back (*ii);
528 if (buf.size () == kChunkSize_) [[unlikely]] {
529 accessor._GetWriteableRep ().Insert (insertAt, span<const T>{buf.begin (), buf.size ()});
530 insertAt += buf.size ();
531 buf.clear ();
532 }
534 if (buf.size () != 0) [[likely]] {
535 accessor._GetWriteableRep ().Insert (insertAt, span<const T>{buf.begin (), buf.size ()});
536 }
537 }
538 }
539 template <typename T>
540 template <IIterableOfTo<T> ITERABLE_OF_ADDABLE>
541 inline void Sequence<T>::InsertAll (size_t i, ITERABLE_OF_ADDABLE&& s)
542 {
543 Require (i <= this->size ());
544 /*
545 * A STROIKA SOURCE cannot reach the contiguous_iterator fast path in the iterator-pair overload,
546 * because Iterator<T> is not a contiguous_iterator - it is a virtual cursor. But an array-backed
547 * backend can still hand over its whole buffer at once through _IRep::PeekContiguousStorage (),
548 * which is the same hook As<vector<T>> (), SequentialEquals () and IndexOf () already use. So ask.
549 *
550 * Backends that have no contiguous storage (the linked lists, a lazy Where () pipeline, a
551 * generator) answer nullopt and we fall through to the element-at-a-time path unchanged - the hook
552 * is purely additive, and every caller is required to keep a working slow path.
553 *
554 * WHY BORROWING THE SOURCE'S BUFFER WHILE WRITING OURSELVES IS SAFE - it is copy-on-write that
555 * makes it so, not the order of the accessors:
556 *
557 * o if 's' and '*this' are distinct envelopes sharing one rep ('Sequence<T> b = a;
558 * a.AppendAll (b);'), _GetWriteableRep () sees a use count above one and CLONES. Our insert
559 * then mutates the clone, while the span still views the original buffer that 's' holds. So
560 * the span cannot be invalidated by our own write.
561 * o holding a read context on 's' and a write context on '*this' is likewise fine even for
562 * that shared-rep case, because the checker (Iterable<T>::_fThisAssertExternallySynchronized)
563 * is a member of the ENVELOPE, not of the rep - two envelopes never share one.
564 * o taking the destination accessor first is therefore not required for correctness; it just
565 * means the two reps are already provably distinct objects at the moment we borrow. It also
566 * costs nothing, since these methods always mutate and so the clone happens either way.
567 * o the accessors are scoped to end before the fallback, because the slow path takes its own.
568 *
569 * The one genuinely unsafe case is 's' being LITERALLY this same envelope ('a.InsertAll (0, a)'):
570 * there is no clone, so the span would view the very buffer being inserted into. Excluded up front,
571 * which leaves that call behaving exactly as it did before this fast path existed.
572 */
573 if constexpr (derived_from<remove_cvref_t<ITERABLE_OF_ADDABLE>, Iterable<T>>) {
574 if (static_cast<const Iterable<T>*> (this) != static_cast<const Iterable<T>*> (&s)) [[likely]] {
575 bool handled = false;
576 {
577 _SafeReadWriteRepAccessor<_IRep> destAccessor{this};
578 _IRep& destRep = destAccessor._GetWriteableRep ();
579 // NB: explicitly Iterable<T>::_IRep, NOT this class's _IRep - the source may be any
580 // Iterable<T> (a Collection, a Set, ...), and the accessor AssertMember-checks the
581 // dynamic type in debug builds. PeekContiguousStorage () is declared on the base anyway.
582 _SafeReadRepAccessor<typename Iterable<T>::_IRep> srcAccessor{&s};
583 if (auto srcSpan = srcAccessor._ConstGetRep ().PeekContiguousStorage ()) {
584 if (not srcSpan->empty ()) [[likely]] {
585 destRep.Insert (i, *srcSpan);
586 }
587 handled = true;
588 }
589 }
590 if (handled) {
591 return;
592 }
593 }
594 }
595 InsertAll (i, s.begin (), s.end ());
596 }
597 template <typename T>
598 inline void Sequence<T>::Prepend (ArgByValueType<value_type> item)
599 {
600 Insert (0, item);
601 }
602 template <typename T>
603 template <IIterableOfTo<T> ITERABLE_OF_ADDABLE>
604 inline void Sequence<T>::PrependAll (ITERABLE_OF_ADDABLE&& s)
605 {
606 InsertAll (0, forward<ITERABLE_OF_ADDABLE> (s));
607 }
608 template <typename T>
609 template <IInputIterator<T> ITERATOR_OF_ADDABLE>
610 inline void Sequence<T>::PrependAll (ITERATOR_OF_ADDABLE&& start, ITERATOR_OF_ADDABLE&& end)
611 {
612 InsertAll (0, forward<ITERATOR_OF_ADDABLE> (start), forward<ITERATOR_OF_ADDABLE> (end));
614 template <typename T>
615 inline void Sequence<T>::Append (ArgByValueType<value_type> item)
616 {
617 _SafeReadWriteRepAccessor<_IRep>{this}._GetWriteableRep ().Insert (_IRep::_kSentinelLastItemIndex, span{&item, 1});
618 }
619 template <typename T>
620 template <IIterableOfTo<T> ITERABLE_OF_ADDABLE>
621 inline void Sequence<T>::AppendAll (ITERABLE_OF_ADDABLE&& s)
622 {
623 // Same _IRep::PeekContiguousStorage () fast path as InsertAll (i, ITERABLE_OF_ADDABLE) - see the
624 // long comment there for why the accessors are ordered and scoped the way they are, and why the
625 // same-envelope case has to be excluded. Appends via the sentinel index rather than size (), so it
626 // stays a single virtual call with no size () query, exactly as the iterator-pair overload does.
627 if constexpr (derived_from<remove_cvref_t<ITERABLE_OF_ADDABLE>, Iterable<T>>) {
628 if (static_cast<const Iterable<T>*> (this) != static_cast<const Iterable<T>*> (&s)) [[likely]] {
629 bool handled = false;
630 {
631 _SafeReadWriteRepAccessor<_IRep> destAccessor{this};
632 _IRep& destRep = destAccessor._GetWriteableRep ();
633 // NB: explicitly Iterable<T>::_IRep, NOT this class's _IRep - the source may be any
634 // Iterable<T> (a Collection, a Set, ...), and the accessor AssertMember-checks the
635 // dynamic type in debug builds. PeekContiguousStorage () is declared on the base anyway.
636 _SafeReadRepAccessor<typename Iterable<T>::_IRep> srcAccessor{&s};
637 if (auto srcSpan = srcAccessor._ConstGetRep ().PeekContiguousStorage ()) {
638 if (not srcSpan->empty ()) [[likely]] {
639 destRep.Insert (_IRep::_kSentinelLastItemIndex, *srcSpan);
640 }
641 handled = true;
642 }
643 }
644 if (handled) {
645 return;
646 }
647 }
648 }
649 AppendAll (s.begin (), s.end ());
650 }
651 template <typename T>
652 template <IInputIterator<T> ITERATOR_OF_ADDABLE, sentinel_for<remove_cvref_t<ITERATOR_OF_ADDABLE>> ITERATOR_OF_ADDABLE2>
653 inline void Sequence<T>::AppendAll (ITERATOR_OF_ADDABLE&& start, ITERATOR_OF_ADDABLE2&& end)
655 _SafeReadWriteRepAccessor<_IRep> accessor = {this};
656 /*
657 * FAST PATH - when the source is contiguous and already T, hand the whole range to the rep as ONE
658 * span in ONE virtual call. _IRep::Insert () has always taken a span<const value_type>, and every
659 * backend implements the bulk case properly (DataStructures::Array<T> does a single
660 * ReserveAtLeast () then one Memory::Insert (), which for a trivially-copyable T is a memmove), so
661 * this needs no rep or backend change at all.
662 *
663 * It matters more than it looks: appending N elements one span-of-1 at a time costs N virtual
664 * dispatches and N capacity checks, and for a cheap T that IS essentially the whole cost. Measured
665 * before this, Sequence<int>::append_range () ran ~30-56x std::vector<int>::append_range (), and
666 * Stroika's absolute time barely differed between int and String even though a String copy costs
667 * far more - the per-element overhead was swamping the element work.
668 *
669 * Requires all three: contiguous_iterator (so to_address () yields a real pointer), a sized
670 * sentinel (so the length is known without walking), and a value type that is exactly T (a
671 * convertible-but-different type cannot be viewed as span<const T> - appending vector<short> to a
672 * Sequence<int> must still go element by element).
673 */
674 if constexpr (contiguous_iterator<remove_cvref_t<ITERATOR_OF_ADDABLE>> and
675 sized_sentinel_for<remove_cvref_t<ITERATOR_OF_ADDABLE2>, remove_cvref_t<ITERATOR_OF_ADDABLE>> and
676 same_as<remove_cvref_t<iter_value_t<remove_cvref_t<ITERATOR_OF_ADDABLE>>>, T>) {
677 if (start != end) [[likely]] {
678 accessor._GetWriteableRep ().Insert (_IRep::_kSentinelLastItemIndex,
679 span<const T>{to_address (start), static_cast<size_t> (end - start)});
680 }
681 }
682 else {
683 /*
684 * A source that is neither contiguous nor able to offer PeekContiguousStorage () - a std::list,
685 * a generator, a lazy Where () pipeline - still has to be walked one element at a time. But it
686 * does NOT have to be handed over one element at a time: buffer a chunk and pass that as a span,
687 * so the rep sees one call per CHUNK instead of one per element.
688 *
689 * Measured (g++-15 release, 1000 elements, Tests/52 "vector source vs LIST source"): a list
690 * source cost 49x a vector one for int and 9.8x for String. The String number is the
691 * interesting one - it says the dominant cost is NOT the virtual dispatch but the backend
692 * growing its buffer incrementally, reallocating and moving every element already there, where
693 * one span insert reserves once. That is also why this is NOT gated on T being cheap to copy:
694 * the extra source->buffer copy per element was the obvious objection to chunking, and it is
695 * swamped by what amortizing the growth saves, for String as much as for int.
696 *
697 * StackBuffer (not InlineBuffer) because this is exactly a scratch buffer living in a stack
698 * frame; its default inline element count is already tuned to stay under the frame size where
699 * Windows calls _chkstk, so flushing at kMinCapacity means it never touches the free store.
700 */
701 constexpr size_t kChunkSize_ = Memory::StackBuffer<T>::kMinCapacity;
703 for (auto i = forward<ITERATOR_OF_ADDABLE> (start); i != forward<ITERATOR_OF_ADDABLE2> (end); ++i) {
704 buf.push_back (*i);
705 if (buf.size () == kChunkSize_) [[unlikely]] {
706 accessor._GetWriteableRep ().Insert (_IRep::_kSentinelLastItemIndex, span<const T>{buf.begin (), buf.size ()});
707 buf.clear ();
708 }
709 }
710 if (buf.size () != 0) [[likely]] {
711 accessor._GetWriteableRep ().Insert (_IRep::_kSentinelLastItemIndex, span<const T>{buf.begin (), buf.size ()});
712 }
713 }
714 }
715 template <typename T>
716 inline void Sequence<T>::Update (const Iterator<value_type>& i, ArgByValueType<value_type> newValue, Iterator<value_type>* nextI)
717 {
718 Require (not i.AtEnd ());
719 auto [writerRep, patchedIterator] = _GetWritableRepAndPatchAssociatedIterator (i);
720 writerRep->Update (patchedIterator, newValue, nextI);
721 }
722 template <typename T>
723 inline void Sequence<T>::Remove (size_t i)
724 {
725 Require (i < this->size ());
726 _SafeReadWriteRepAccessor<_IRep>{this}._GetWriteableRep ().Remove (i, i + 1);
727 }
728 template <typename T>
729 inline void Sequence<T>::Remove (size_t start, size_t end)
730 {
731 Require (start <= end and end <= this->size ());
732 _SafeReadWriteRepAccessor<_IRep>{this}._GetWriteableRep ().Remove (start, end);
733 }
734 template <typename T>
735 inline void Sequence<T>::Remove (const Iterator<value_type>& i, Iterator<value_type>* nextI)
736 {
737 Require (not i.AtEnd ());
738 auto [writerRep, patchedIterator] = _GetWritableRepAndPatchAssociatedIterator (i);
739 writerRep->Remove (patchedIterator, nextI);
740 }
741 template <typename T>
742 template <typename CONTAINER_OF_ADDABLE>
743 inline void Sequence<T>::As (CONTAINER_OF_ADDABLE* into) const
744 {
745 RequireNotNull (into);
746 *into = this->template As<CONTAINER_OF_ADDABLE> (); // ie Iterable<T>::As, so this gets its fast paths too
747 }
748 template <typename T>
749 inline auto Sequence<T>::First () const -> optional<value_type>
750 {
751 return this->empty () ? optional<T>{} : GetAt (0);
752 }
753 template <typename T>
754 inline auto Sequence<T>::First (const function<bool (ArgByValueType<value_type>)>& that) const -> optional<value_type>
755 {
756 return inherited::First (that);
757 }
758 template <typename T>
759 inline auto Sequence<T>::FirstValue (ArgByValueType<value_type> defaultValue) const -> value_type
760 {
761 return this->empty () ? defaultValue : GetAt (0);
762 }
763 template <typename T>
764 inline auto Sequence<T>::Last () const -> optional<value_type>
765 {
766 // IRep::GetAt() defined to allow special _IRep::_kSentinelLastItemIndex
767 return this->empty () ? optional<T>{} : _SafeReadRepAccessor<_IRep>{this}._ConstGetRep ().GetAt (_IRep::_kSentinelLastItemIndex);
768 }
769 template <typename T>
770 inline auto Sequence<T>::Last (const function<bool (ArgByValueType<value_type>)>& that) const -> optional<value_type>
771 {
772 // @todo when we have reverse iterators - we could implement this more efficiently by walking the sequence backwards
773 return inherited::Last (that);
774 }
775 template <typename T>
776 inline auto Sequence<T>::LastValue (ArgByValueType<value_type> defaultValue) const -> value_type
777 {
778 // IRep::GetAt() defined to allow special _IRep::_kSentinelLastItemIndex
779 return this->empty () ? defaultValue : _SafeReadRepAccessor<_IRep>{this}._ConstGetRep ().GetAt (_IRep::_kSentinelLastItemIndex);
780 }
781 template <typename T>
782 inline void Sequence<T>::push_back (ArgByValueType<value_type> item)
783 {
784 Append (item);
785 }
786 template <typename T>
787 template <IIterableOfTo<T> ITERABLE_OF_ADDABLE>
788 inline void Sequence<T>::append_range (ITERABLE_OF_ADDABLE&& s)
789 {
790 AppendAll (forward<ITERABLE_OF_ADDABLE> (s));
791 }
792 template <typename T>
793 inline auto Sequence<T>::back () const -> value_type
794 {
795 return *Last ();
796 }
797 template <typename T>
798 inline auto Sequence<T>::front () const -> value_type
799 {
800 return *First ();
801 }
802 template <typename T>
803 inline void Sequence<T>::clear ()
804 {
805 RemoveAll ();
806 }
807 template <typename T>
808 inline void Sequence<T>::erase (size_t i)
809 {
810 this->Remove (i);
811 }
812 template <typename T>
814 {
815 Iterator<T> nextI{nullptr};
816 this->Remove (i, &nextI);
817 return nextI;
818 }
819 template <typename T>
820 inline auto Sequence<T>::operator+= (ArgByValueType<value_type> item) -> Sequence&
821 {
822 Append (item);
823 return *this;
824 }
825 template <typename T>
826 inline auto Sequence<T>::operator+= (const Sequence& items) -> Sequence&
828 AppendAll (items);
829 return *this;
830 }
831 template <typename T>
832 auto Sequence<T>::_GetWritableRepAndPatchAssociatedIterator (const Iterator<value_type>& i) -> tuple<_IRep*, Iterator<value_type>>
833 {
834 Require (not i.AtEnd ());
835 using element_type = typename inherited::_SharedByValueRepType::element_type;
836 Iterator<value_type> patchedIterator = i;
837 element_type* writableRep = this->_fRep.rwget ([&] (const element_type& prevRepPtr) -> typename inherited::_SharedByValueRepType::shared_ptr_type {
838 return Debug::UncheckedDynamicCast<const _IRep&> (prevRepPtr).CloneAndPatchIterator (&patchedIterator);
839 });
840 AssertNotNull (writableRep);
841 return make_tuple (Debug::UncheckedDynamicCast<_IRep*> (writableRep), move (patchedIterator));
842 }
843 template <typename T>
844 inline void Sequence<T>::_AssertRepValidType () const
845 {
847 _SafeReadRepAccessor<_IRep>{this};
848 }
849 }
850 template <typename T>
851 inline bool Sequence<T>::operator== (const Sequence& rhs) const
852 requires (equality_comparable<T>)
853 {
854 return EqualsComparer<>{}(*this, rhs);
855 }
856 template <typename T>
857 inline auto Sequence<T>::operator<=> (const Sequence& rhs) const
858 requires (three_way_comparable<T>)
859 {
860 return ThreeWayComparer<>{}(*this, rhs);
861 }
862
863 /*
864 ********************************************************************************
865 ********************************* operator+ ************************************
866 ********************************************************************************
867 */
868 template <typename T>
869 Sequence<T> operator+ (const Iterable<T>& lhs, const Sequence<T>& rhs)
870 {
871 Sequence<T> result{lhs};
872 result += rhs;
873 return result;
874 }
875 template <typename T>
876 Sequence<T> operator+ (const Sequence<T>& lhs, const Iterable<T>& rhs)
877 {
878 Sequence<T> result{lhs};
879 result += rhs;
880 return result;
881 }
882 template <typename T>
883 Sequence<T> operator+ (const Sequence<T>& lhs, const Sequence<T>& rhs)
884 {
885 Sequence<T> result{lhs};
886 result += rhs;
887 return result;
888 }
889
890}
#define AssertNotNull(p)
Definition Assertions.h:334
#define qStroika_Foundation_Debug_AssertionsChecked
The qStroika_Foundation_Debug_AssertionsChecked flag determines if assertions are checked and validat...
Definition Assertions.h:49
#define RequireNotNull(p)
Definition Assertions.h:348
#define RequireExpression(c)
Definition Assertions.h:268
bool Equals(const T *lhs, const T *rhs)
strcmp or wsccmp() as appropriate == 0
#define qStroika_ATTRIBUTE_NO_UNIQUE_ADDRESS_VCFORCE
[[msvc::no_unique_address]] isn't always broken in MSVC. Annotate with this on things where its not b...
Definition StdCompat.h:443
Sequence_stdvector<T> is an std::vector-based concrete implementation of the Sequence<T> container pa...
nonvirtual BidirectionalIterator< T > _MakeBidirectionalIterator_ViaGetAt() const
Generic (backend-independent) implementations of GetBidirectionalIterator (), implemented purely in t...
Definition Sequence.inl:225
nonvirtual RandomAccessIterator< T > _MakeRandomAccessIterator_ViaGetAt() const
Generic (backend-independent) implementations of GetRandomAccessIterator (), implemented purely in te...
Definition Sequence.inl:230
A generalization of a vector: a container whose elements are keyed by the natural numbers.
nonvirtual void As(CONTAINER_OF_ADDABLE *into) const
write this Sequence into an existing container
typename Iterable< value_type >::template SequentialEqualsComparer< T_EQUALS_COMPARER > EqualsComparer
nonvirtual void Insert(size_t i, ArgByValueType< value_type > item)
Definition Sequence.inl:464
nonvirtual value_type GetAt(size_t i) const
Definition Sequence.inl:389
nonvirtual void AppendAll(ITERABLE_OF_ADDABLE &&s)
Logically halfway between std::array and std::vector; Smart 'direct memory array' - which when needed...
nonvirtual void push_back(Common::ArgByValueType< T > e)
nonvirtual size_t size() const noexcept
A BidirectionalIterator is an Iterator that can be moved both forward and backward.
Implementation detail for iterator implementors.
Definition Iterable.h:1743
Iterable<T> is a base class for containers which easily produce an Iterator<T> to traverse them.
Definition Iterable.h:238
nonvirtual size_t size() const
Returns the number of items contained.
Definition Iterable.inl:311
An Iterator<T> is a copyable object which allows traversing the contents of some container.
Definition Iterator.h:253
nonvirtual bool AtEnd() const
AtEnd () means there is nothing left in this iterator (a synonym for (it == container....
Definition Iterator.inl:143