Stroika Library 3.0d24
 
Loading...
Searching...
No Matches
Containers/Collection.inl
1/*
2 * Copyright(c) Sophist Solutions, Inc. 1990-2026. All rights reserved
3 */
4
5/*
6 ********************************************************************************
7 ***************************** Implementation Details ***************************
8 ********************************************************************************
9 */
10
14
16
17 /*
18 ********************************************************************************
19 ******************************** Collection<T> *********************************
20 ********************************************************************************
21 */
22 template <typename T>
24 : inherited{Factory::Collection_Factory<value_type>::Default () ()}
25 {
26 _AssertRepValidType ();
27 }
28 template <typename T>
29 template <IInputIterator<T> ITERATOR_OF_ADDABLE, sentinel_for<remove_cvref_t<ITERATOR_OF_ADDABLE>> ITERATOR_OF_ADDABLE2>
30 inline Collection<T>::Collection (ITERATOR_OF_ADDABLE&& start, ITERATOR_OF_ADDABLE2&& end)
31 : Collection{}
32 {
34 _AssertRepValidType ();
35 }
36 template <typename T>
37 inline Collection<T>::Collection (const shared_ptr<_IRep>& src) noexcept
38 : inherited{src}
39 {
40 RequireNotNull (src);
41 _AssertRepValidType ();
42 }
43 template <typename T>
44 inline Collection<T>::Collection (shared_ptr<_IRep>&& src) noexcept
45 : inherited{(RequireExpression (src != nullptr), move (src))}
46 {
47 _AssertRepValidType ();
48 }
49 template <typename T>
50 inline Collection<T>::Collection (const initializer_list<value_type>& src)
51 : Collection{}
52 {
53 AddAll (src);
54 _AssertRepValidType ();
55 }
56#if !qCompilerAndStdLib_RequiresNotMatchInlineOutOfLineForTemplateClassBeingDefined_Buggy
57 template <typename T>
58 template <IIterableOfTo<T> ITERABLE_OF_ADDABLE>
59 requires (not derived_from<remove_cvref_t<ITERABLE_OF_ADDABLE>, Collection<T>>)
60 inline Collection<T>::Collection (ITERABLE_OF_ADDABLE&& src)
61 : Collection{}
62 {
63 AddAll (forward<ITERABLE_OF_ADDABLE> (src));
64 _AssertRepValidType ();
65 }
66#endif
67 template <typename T>
68 template <Common::IEqualsComparer<T> EQUALS_COMPARER>
69 inline bool Collection<T>::Contains (ArgByValueType<value_type> item, EQUALS_COMPARER&& equalsComparer) const
70 {
71 return this->Find (item, forward<EQUALS_COMPARER> (equalsComparer)) != this->end ();
72 }
73 template <typename T>
74 template <IInputIterator<T> ITERATOR_OF_ADDABLE, sentinel_for<remove_cvref_t<ITERATOR_OF_ADDABLE>> ITERATOR_OF_ADDABLE2>
75 void Collection<T>::AddAll (ITERATOR_OF_ADDABLE&& start, ITERATOR_OF_ADDABLE2&& end)
76 {
77 _SafeReadWriteRepAccessor<_IRep> tmp{this};
78 /*
79 * A CONTIGUOUS source of exactly value_type goes to the rep as ONE span: one virtual
80 * dispatch and one change-count bump for the whole range, instead of one of each per
81 * element. Mirrors what Sequence<T>::AppendAll () does (c384915a32).
82 *
83 * Note this does NOT make the backend's own work cheaper - a sorted multiset still pays
84 * per-element tree insertion - so the win is large for array-backed reps and small for
85 * node-based ones. See the Tests/52 "add many at once" entries.
86 */
87 if constexpr (contiguous_iterator<remove_cvref_t<ITERATOR_OF_ADDABLE>> and
88 sized_sentinel_for<remove_cvref_t<ITERATOR_OF_ADDABLE2>, remove_cvref_t<ITERATOR_OF_ADDABLE>> and
89 same_as<remove_cvref_t<iter_value_t<remove_cvref_t<ITERATOR_OF_ADDABLE>>>, value_type>) {
90 if (start != end) [[likely]] {
91 tmp._GetWriteableRep ().Add (span<const value_type>{to_address (start), static_cast<size_t> (end - start)}, nullptr);
92 }
93 }
94 else {
95 /*
96 * Neither contiguous nor offering PeekContiguousStorage () (a std::list, a generator, a lazy
97 * pipeline) - so it must be walked one element at a time, but it can still be HANDED OVER a
98 * chunk at a time. See the long comment on Sequence<T>::AppendAll () for why this is worth
99 * doing even when T is expensive to copy: the saving is mostly the backend reserving once per
100 * chunk instead of growing per element, which swamps the extra source->buffer copy.
101 */
102 constexpr size_t kChunkSize_ = Memory::StackBuffer<value_type>::kMinCapacity;
103 Memory::StackBuffer<value_type> buf;
104 for (auto i = forward<ITERATOR_OF_ADDABLE> (start); i != forward<ITERATOR_OF_ADDABLE2> (end); ++i) {
105 buf.push_back (*i);
106 if (buf.size () == kChunkSize_) [[unlikely]] {
107 tmp._GetWriteableRep ().Add (span<const value_type>{buf.begin (), buf.size ()}, nullptr);
108 buf.clear ();
109 }
110 }
111 if (buf.size () != 0) [[likely]] {
112 tmp._GetWriteableRep ().Add (span<const value_type>{buf.begin (), buf.size ()}, nullptr);
113 }
114 }
115 }
116 template <typename T>
117 template <IIterableOfTo<T> ITERABLE_OF_ADDABLE>
118 inline void Collection<T>::AddAll (ITERABLE_OF_ADDABLE&& items)
119 {
120 if constexpr (std::is_convertible_v<remove_cvref_t<ITERABLE_OF_ADDABLE>*, Collection<value_type>*>) {
121 // very rare corner case
122 if (static_cast<const Iterable<value_type>*> (this) == static_cast<const Iterable<value_type>*> (&items)) [[unlikely]] {
123 // vector iterator CTOR does't support sentinel for second iterator arg...
124 vector<value_type> copy{std::begin (items), Iterator<value_type>{std::end (items)}}; // because you can not iterate over a container while modifying it
125 AddAll (std::begin (copy), std::end (copy));
126 return;
127 }
128 }
129 /*
130 * A STROIKA SOURCE takes the per-element branch of the iterator-pair overload, because Iterator<T>
131 * is not a contiguous_iterator. An array-backed source can still offer its whole buffer at once via
132 * _IRep::PeekContiguousStorage (), so ask before falling back. Mirrors what
133 * Sequence<T>::InsertAll (i, ITERABLE_OF_ADDABLE) does - see the long comment there for why borrowing
134 * the source's buffer while writing our own rep is safe (copy-on-write, plus a per-envelope rather
135 * than per-rep synchronization checker) and why the accessors are scoped to end before the fallback.
136 *
137 * The same-envelope case is already handled above for a Collection source; the pointer check here
138 * covers it for any other Iterable that could alias, and costs one comparison.
139 *
140 * \note Batching only helps where the BACKEND has a bulk insert. A sorted multiset - the default
141 * Collection<T> for an ordered T - still pays per-element tree insertion, so expect this to
142 * matter for the array-backed reps and to be nearly free elsewhere. See the Tests/52
143 * "AddAll from vector vs from list" probes.
144 */
145 if constexpr (derived_from<remove_cvref_t<ITERABLE_OF_ADDABLE>, Iterable<value_type>>) {
146 if (static_cast<const Iterable<value_type>*> (this) != static_cast<const Iterable<value_type>*> (&items)) [[likely]] {
147 bool handled = false;
148 {
149 _SafeReadWriteRepAccessor<_IRep> destAccessor{this};
150 _IRep& destRep = destAccessor._GetWriteableRep ();
151 // explicitly the BASE rep - see the matching note in Sequence.inl
152 _SafeReadRepAccessor<typename Iterable<value_type>::_IRep> srcAccessor{&items};
153 if (auto srcSpan = srcAccessor._ConstGetRep ().PeekContiguousStorage ()) {
154 if (not srcSpan->empty ()) [[likely]] {
155 destRep.Add (*srcSpan, nullptr);
156 }
157 handled = true;
158 }
159 }
160 if (handled) {
161 return;
162 }
163 }
164 }
165 AddAll (std::begin (items), std::end (items));
166 }
167 template <typename T>
168 inline void Collection<T>::Add (ArgByValueType<value_type> item)
169 {
170 _SafeReadWriteRepAccessor<_IRep>{this}._GetWriteableRep ().Add (span<const value_type>{&item, 1}, nullptr);
171 Ensure (not this->empty ());
172 }
173 template <typename T>
174 inline void Collection<T>::Add (ArgByValueType<value_type> item, Iterator<T>* addedAt)
175 {
176 RequireNotNull (addedAt);
177 _SafeReadWriteRepAccessor<_IRep>{this}._GetWriteableRep ().Add (span<const value_type>{&item, 1}, addedAt);
178 Ensure (not this->empty ());
179 Ensure (not addedAt->Done ());
180 }
181 template <typename T>
182 inline void Collection<T>::Update (const Iterator<value_type>& i, ArgByValueType<value_type> newValue, Iterator<value_type>* nextI)
183 {
184 Require (not i.AtEnd ());
185 auto [writerRep, patchedIterator] = _GetWritableRepAndPatchAssociatedIterator (i);
186 writerRep->Update (patchedIterator, newValue, nextI);
187 }
188 template <typename T>
191 Require (not i.AtEnd ());
192 auto [writerRep, patchedIterator] = _GetWritableRepAndPatchAssociatedIterator (i);
193 writerRep->Remove (patchedIterator, nextI);
194 }
195 template <typename T>
196 template <Common::IPotentiallyComparer<T> EQUALS_COMPARER>
197 inline void Collection<T>::Remove (ArgByValueType<value_type> item, EQUALS_COMPARER&& equalsComparer)
198 {
199 auto i = this->Find (item, forward<EQUALS_COMPARER> (equalsComparer));
200 Require (i != this->end ()); // use remove-if if the item might not exist
201 auto [writerRep, patchedIterator] = _GetWritableRepAndPatchAssociatedIterator (i);
202 writerRep->Remove (patchedIterator, nullptr);
203 }
204 template <typename T>
205 template <Common::IEqualsComparer<T> EQUALS_COMPARER>
206 inline bool Collection<T>::RemoveIf (ArgByValueType<value_type> item, EQUALS_COMPARER&& equalsComparer)
207 {
208 if (auto i = this->Find (item, forward<EQUALS_COMPARER> (equalsComparer))) {
209 auto [writerRep, patchedIterator] = _GetWritableRepAndPatchAssociatedIterator (i);
210 writerRep->Remove (patchedIterator, nullptr);
211 return true;
212 }
213 return false;
214 }
215 template <typename T>
216 template <predicate<T> PREDICATE>
217 bool Collection<T>::RemoveIf (PREDICATE&& p)
218 {
219 if (auto i = this->Find (forward<PREDICATE> (p))) {
220 Remove (i);
221 return true;
222 }
223 return false;
224 }
225 template <typename T>
227 {
228 _SafeReadRepAccessor<_IRep> tmp{this}; // important to use READ not WRITE accessor, because write accessor would have already cloned the data
229 if (not tmp._ConstGetRep ().empty ()) {
230 this->_fRep = tmp._ConstGetRep ().CloneEmpty ();
231 }
232 }
233 template <typename T>
234 template <Common::IEqualsComparer<T> EQUALS_COMPARER>
235 size_t Collection<T>::RemoveAll (const Iterator<value_type>& start, const Iterator<value_type>& end, EQUALS_COMPARER&& equalsComparer)
236 {
237 size_t cnt{};
238 for (auto i = start; i != end;) {
239 if (RemoveIf (*i, equalsComparer, &i)) {
240 ++cnt;
241 }
242 }
243 return cnt;
244 }
245 template <typename T>
246 template <IIterableOfTo<T> ITERABLE_OF_ADDABLE, typename EQUALS_COMPARER>
247 inline size_t Collection<T>::RemoveAll (const ITERABLE_OF_ADDABLE& c, EQUALS_COMPARER&& equalsComparer)
248 {
249 if (static_cast<const void*> (this) == static_cast<const void*> (addressof (c))) {
250 return RemoveAll (forward<EQUALS_COMPARER> (equalsComparer));
251 }
252 else {
253 return RemoveAll (std::begin (c), std::end (c), forward<EQUALS_COMPARER> (equalsComparer));
254 }
255 }
256 template <typename T>
257 template <predicate<T> PREDICATE>
258 size_t Collection<T>::RemoveAll (PREDICATE&& p)
259 {
260 size_t nRemoved{};
261 for (Iterator<T> i = this->begin (); i != this->end ();) {
262 if (p (*i)) {
263 Remove (i, &i);
264 ++nRemoved;
265 }
266 else {
267 ++i;
268 }
269 }
270 return nRemoved;
272 template <typename T>
273 inline void Collection<T>::clear ()
274 {
275 RemoveAll ();
276 }
277 template <typename T>
278 template <Common::IEqualsComparer<T> EQUALS_COMPARER>
279 inline void Collection<T>::erase (ArgByValueType<value_type> item, EQUALS_COMPARER&& equalsComparer)
280 {
281 Remove (item, forward<EQUALS_COMPARER> (equalsComparer));
282 }
283 template <typename T>
284 inline auto Collection<T>::erase (const Iterator<value_type>& i) -> Iterator<value_type>
285 {
286 Iterator<value_type> nextI{nullptr};
287 Remove (i, &nextI);
288 return nextI;
289 }
290 template <typename T>
291 template <typename RESULT_CONTAINER, invocable<T> ELEMENT_MAPPER>
292 inline RESULT_CONTAINER Collection<T>::Map (ELEMENT_MAPPER&& elementMapper) const
293 requires (convertible_to<invoke_result_t<ELEMENT_MAPPER, T>, typename RESULT_CONTAINER::value_type> or
294 convertible_to<invoke_result_t<ELEMENT_MAPPER, T>, optional<typename RESULT_CONTAINER::value_type>>)
295 {
296 if constexpr (same_as<RESULT_CONTAINER, Collection>) {
297 // clone the rep so we retain any ordering function/etc, rep type
298 return inherited::template Map<RESULT_CONTAINER> (forward<ELEMENT_MAPPER> (elementMapper),
299 RESULT_CONTAINER{_SafeReadRepAccessor<_IRep>{this}._ConstGetRep ().CloneEmpty ()});
300 }
301 else {
302 return inherited::template Map<RESULT_CONTAINER> (forward<ELEMENT_MAPPER> (elementMapper)); // default Iterable<> implementation then...
303 }
304 }
305 template <typename T>
306 template <derived_from<Iterable<T>> RESULT_CONTAINER, predicate<T> INCLUDE_PREDICATE>
307 inline RESULT_CONTAINER Collection<T>::Where (INCLUDE_PREDICATE&& includeIfTrue) const
308 {
309 if constexpr (same_as<RESULT_CONTAINER, Collection>) {
310 // clone the rep so we retain any ordering function/etc, rep type
311 return inherited::template Where<RESULT_CONTAINER> (
312 forward<INCLUDE_PREDICATE> (includeIfTrue), RESULT_CONTAINER{_SafeReadRepAccessor<_IRep>{this}._ConstGetRep ().CloneEmpty ()});
313 }
314 else {
315 return inherited::template Where<RESULT_CONTAINER> (forward<INCLUDE_PREDICATE> (includeIfTrue)); // default Iterable<> implementation then...
316 }
317 }
318 template <typename T>
319 inline auto Collection<T>::operator+= (ArgByValueType<value_type> item) -> Collection&
320 {
321 Add (item);
322 return *this;
323 }
324 template <typename T>
326 {
327 AddAll (items);
328 return *this;
329 }
330 template <typename T>
331 auto Collection<T>::_GetWritableRepAndPatchAssociatedIterator (const Iterator<value_type>& i) -> tuple<_IRep*, Iterator<value_type>>
332 {
333 Require (not i.AtEnd ());
334 using element_type = typename inherited::_SharedByValueRepType::element_type;
335 Iterator<value_type> patchedIterator = i;
336 element_type* writableRep = this->_fRep.rwget ([&] (const element_type& prevRepPtr) -> typename inherited::_SharedByValueRepType::shared_ptr_type {
337 return Debug::UncheckedDynamicCast<const _IRep&> (prevRepPtr).CloneAndPatchIterator (&patchedIterator);
338 });
339 AssertNotNull (writableRep);
340 return make_tuple (Debug::UncheckedDynamicCast<_IRep*> (writableRep), move (patchedIterator));
341 }
342 template <typename T>
343 inline void Collection<T>::_AssertRepValidType () const
344 {
346 _SafeReadRepAccessor<_IRep>{this};
347 }
348 }
349
350 /*
351 ********************************************************************************
352 ********************************* operator+ ************************************
353 ********************************************************************************
354 */
355 template <typename T>
357 {
358 Collection<T> result{lhs};
359 result += rhs;
360 return result;
361 }
362 template <typename T>
363 Collection<T> operator+ (const Collection<T>& lhs, const Iterable<T>& rhs)
364 {
365 Collection<T> result{lhs};
366 result += rhs;
367 return result;
368 }
369 template <typename T>
370 Collection<T> operator+ (const Collection<T>& lhs, const Collection<T>& rhs)
371 {
372 Collection<T> result{lhs};
373 result += rhs;
374 return result;
375 }
376}
#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
A Collection<T> is a container to manage an un-ordered collection of items, without equality defined ...
nonvirtual void AddAll(ITERATOR_OF_ADDABLE &&start, ITERATOR_OF_ADDABLE2 &&end)
nonvirtual void Update(const Iterator< value_type > &i, ArgByValueType< value_type > newValue, Iterator< value_type > *nextI=nullptr)
nonvirtual bool Contains(ArgByValueType< value_type > item, EQUALS_COMPARER &&equalsComparer={}) const
Compares items with TRAITS::EqualsCompareFunctionType::Equals, and returns true if any match.
nonvirtual void Remove(ArgByValueType< value_type > item, EQUALS_COMPARER &&equalsComparer={})
Remove () the argument value (which must exist)
nonvirtual RESULT_CONTAINER Map(ELEMENT_MAPPER &&elementMapper) const
'override' Iterable<>::Map () function so RESULT_CONTAINER defaults to Collection,...
nonvirtual void erase(ArgByValueType< value_type > item, EQUALS_COMPARER &&equalsComparer={})
nonvirtual bool RemoveIf(ArgByValueType< value_type > item, EQUALS_COMPARER &&equalsComparer={})
RemoveIf () the (the first matching) argument value, if present. Returns true if item removed.
nonvirtual RESULT_CONTAINER Where(INCLUDE_PREDICATE &&includeIfTrue) const
return a subset of the Collection for which includeIfTrue returns true.
nonvirtual void Add(ArgByValueType< value_type > item)
nonvirtual tuple< _IRep *, Iterator< value_type > > _GetWritableRepAndPatchAssociatedIterator(const Iterator< value_type > &i)
Utility to get WRITABLE underlying shared_ptr (replacement for what we normally do - _SafeReadWriteRe...
nonvirtual Collection & operator+=(ArgByValueType< value_type > item)
nonvirtual void RemoveAll()
RemoveAll removes all, or all matching (predicate, iterator range, equals comparer or whatever) items...
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 CONTAINER_OF_T As(CONTAINER_OF_T_CONSTRUCTOR_ARGS... args) const
static constexpr default_sentinel_t end() noexcept
Support for ranged for, and STL syntax in general.
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
Collection< T > operator+(const Iterable< T > &lhs, const Collection< T > &rhs)