Stroika Library 3.0d25
 
Loading...
Searching...
No Matches
InlineBuffer.inl
1/*
2 * Copyright(c) Sophist Solutions, Inc. 1990-2026. All rights reserved
3 */
4#include <algorithm>
5#include <cstring>
6#include <ranges>
7#include <type_traits>
8
9#include "Stroika/Foundation/Containers/Support/ReserveTweaks.h"
11#include "Stroika/Foundation/Execution/Throw.h"
12#include "Stroika/Foundation/Memory/Common.h"
13
14namespace Stroika::Foundation::Memory {
15
16 /*
17 ********************************************************************************
18 ************************** InlineBuffer<T, BUF_SIZE> ***************************
19 ********************************************************************************
20 */
21 template <typename T, size_t BUF_SIZE>
23 : fLiveData_{BufferAsT_ ()}
24 {
25#if qStroika_Foundation_Debug_AssertionsChecked
26 Assert (UsingInlinePreallocatedBuffer_ ()); // cuz empty size so always fits
27 (void)::memcpy (fGuard1_, kGuard1_, sizeof (kGuard1_));
28 (void)::memcpy (fGuard2_, kGuard2_, sizeof (kGuard2_));
29#endif
30 Invariant ();
31 }
32 template <typename T, size_t BUF_SIZE>
33 inline InlineBuffer<T, BUF_SIZE>::InlineBuffer (size_t nElements)
34 requires (default_initializable<T>)
35 : InlineBuffer{}
36 {
37 resize (nElements);
38 Invariant ();
39 }
40 template <typename T, size_t BUF_SIZE>
42 : InlineBuffer{}
43 {
44 resize (nElements, fillValue);
45 Invariant ();
46 }
47 template <typename T, size_t BUF_SIZE>
48 inline InlineBuffer<T, BUF_SIZE>::InlineBuffer (UninitializedConstructorFlag flag, size_t nElements)
49 : InlineBuffer{}
50 {
51 if constexpr (is_trivially_copyable_v<T>) {
52 resize_uninitialized (nElements);
53 }
54 else {
55 Require (flag == UninitializedConstructorFlag::eUninitializedIfTrivial);
56 resize (nElements);
57 }
58 Invariant ();
59 }
60 template <typename T, size_t BUF_SIZE>
61 template <input_iterator ITERATOR_OF_T, sentinel_for<remove_cvref_t<ITERATOR_OF_T>> ITERATOR_OF_T2>
62 InlineBuffer<T, BUF_SIZE>::InlineBuffer (const ITERATOR_OF_T& start, ITERATOR_OF_T2&& end)
63 : InlineBuffer{}
64 {
65 static_assert (is_convertible_v<Common::ExtractValueType_t<ITERATOR_OF_T>, T>);
66 if constexpr (forward_iterator<ITERATOR_OF_T>) {
67 // count, then copy into exactly enough room - which walks start twice, so needs a multi-pass iterator
68#if qCompilerAndStdLib_stdlib_ranges_pretty_broken_Buggy
69 auto sz = static_cast<size_t> (distance (start, ITERATOR_OF_T{end}));
70#else
71 // not forward<ITERATOR_OF_T2> (end) - ranges::distance takes it by value, and end is used again below
72 auto sz = static_cast<size_t> (ranges::distance (start, end));
73#endif
74 if (not this->HasEnoughCapacity_ (sz)) [[unlikely]] {
75 reserve (sz, true); // reserve not resize() so we can do uninitialized_copy (avoid constructing empty objects to be assigned over)
76 }
77#if qCompilerAndStdLib_stdlib_ranges_pretty_broken_Buggy
78 uninitialized_copy (start, ITERATOR_OF_T (end), this->begin ());
79#else
80 ranges::uninitialized_copy (start, forward<ITERATOR_OF_T2> (end), this->begin (), this->begin () + sz);
81#endif
82 fSize_ = sz;
83 }
84 else {
85 // a single-pass iterator (e.g. istream_iterator): reading it consumes it, so it cannot be counted first
86 for (ITERATOR_OF_T i = start; i != end; ++i) {
87 push_back (*i);
88 }
89 }
90 Invariant ();
91 }
92 template <typename T, size_t BUF_SIZE>
93 template <ISpanOfT<T> SPAN_T>
94 inline InlineBuffer<T, BUF_SIZE>::InlineBuffer (const SPAN_T& copyFrom)
95 : InlineBuffer{}
96 {
97 if (not this->HasEnoughCapacity_ (copyFrom.size ())) [[unlikely]] {
98 reserve (copyFrom.size (), true); // reserve not resize() so we can do uninitialized_copy (avoid constructing empty objects to be assigned over)
99 }
100 uninitialized_copy (copyFrom.begin (), copyFrom.end (), this->begin ());
101 fSize_ = copyFrom.size ();
102 Invariant ();
103 }
104 template <typename T, size_t BUF_SIZE>
105 template <size_t FROM_BUF_SIZE>
106 inline InlineBuffer<T, BUF_SIZE>::InlineBuffer (const InlineBuffer<T, FROM_BUF_SIZE>& src)
107 : InlineBuffer{src.begin (), src.end ()}
108 {
109 }
110 template <typename T, size_t BUF_SIZE>
111 inline InlineBuffer<T, BUF_SIZE>::InlineBuffer (const InlineBuffer& src)
112 : InlineBuffer{src.begin (), src.end ()}
113 {
114 }
115 template <typename T, size_t BUF_SIZE>
116 inline InlineBuffer<T, BUF_SIZE>::InlineBuffer (InlineBuffer&& src)
117 : InlineBuffer{}
118 {
119#if qStroika_Foundation_Debug_AssertionsChecked
120 size_t origSize = src.size ();
121 size_t origCapacity = src.capacity ();
122#endif
123 if (src.UsingInlinePreallocatedBuffer_ ()) [[likely]] {
124 // then little to be saved from a move, and technically we really cannot do much, except that we can 'move' the data elements
125 // This 'moving' is done via make_move_iterator () - rather that magically makes the uninitialized_copy really move instead of copying
126 Assert (capacity () == src.capacity ());
127 size_t sz = src.size ();
128 uninitialized_copy (make_move_iterator (src.begin ()), make_move_iterator (src.begin () + sz), this->begin ());
129 fSize_ = sz;
130 }
131 else {
132 // OK - we can do some trickery here to steal the underlying pointers
133 this->fLiveData_ = src.fLiveData_;
134 this->fCapacityOfFreeStoreAllocation_ = src.fCapacityOfFreeStoreAllocation_;
135 src.fLiveData_ = src.BufferAsT_ ();
136 this->fSize_ = src.fSize_;
137 src.fSize_ = 0;
138 Ensure (src.fSize_ == 0);
139 Ensure (src.capacity () == BUF_SIZE);
140 }
141#if qStroika_Foundation_Debug_AssertionsChecked
142 Ensure (this->size () == origSize);
143 Ensure (this->capacity () == origCapacity);
144#endif
145 }
146 template <typename T, size_t BUF_SIZE>
147 inline InlineBuffer<T, BUF_SIZE>::~InlineBuffer ()
148 {
149 Invariant ();
150 DestroyElts_ (this->begin (), this->end ());
151 if (not UsingInlinePreallocatedBuffer_ ()) [[unlikely]] {
152 // we must have used the heap...
153 Deallocate_ (LiveDataAsAllocatedBytes_ ());
154 }
155 }
156 template <typename T, size_t BUF_SIZE>
157 InlineBuffer<T, BUF_SIZE>& InlineBuffer<T, BUF_SIZE>::operator= (const InlineBuffer& rhs)
158 {
159 Invariant ();
160 if (this != &rhs) [[likely]] {
161 // @todo this simple implementation could be more efficient
162 DestroyElts_ (this->begin (), this->end ());
163 fSize_ = 0;
164 if (not this->HasEnoughCapacity_ (rhs.size ())) [[unlikely]] {
165 reserve (rhs.size ());
166 }
167 uninitialized_copy (rhs.begin (), rhs.end (), this->begin ());
168 fSize_ = rhs.size ();
169 Invariant ();
170 }
171 return *this;
172 }
173 template <typename T, size_t BUF_SIZE>
174 inline InlineBuffer<T, BUF_SIZE>& InlineBuffer<T, BUF_SIZE>::operator= (InlineBuffer&& rhs)
175 {
176 Invariant ();
177#if qStroika_Foundation_Debug_AssertionsChecked
178 size_t origFromSize = rhs.size ();
179 size_t origFromCapacity = rhs.capacity ();
180#endif
181 // destroy any existing elts (dont bother resetting size til end), and deallocate any RAM in use
182 DestroyElts_ (this->begin (), this->end ());
183 if (not this->UsingInlinePreallocatedBuffer_ ()) [[unlikely]] {
184 Deallocate_ (LiveDataAsAllocatedBytes_ ());
185 fLiveData_ = BufferAsT_ ();
186 }
187
188 if (rhs.UsingInlinePreallocatedBuffer_ ()) [[likely]] {
189 // then little to be saved from a move, and technically we really cannot do much, except that we can 'move' the data elements
190 // This 'moving' is done via make_move_iterator () - rather that magically makes the uninitialized_copy really move instead of copying
191 Assert (capacity () == rhs.capacity ());
192 size_t sz = rhs.size ();
193 uninitialized_copy (make_move_iterator (rhs.begin ()), make_move_iterator (rhs.begin () + sz), this->begin ());
194 fSize_ = sz;
196 else {
197 // OK - we can do some trickery here to steal the underlying pointers
198 this->fLiveData_ = rhs.fLiveData_;
199 this->fCapacityOfFreeStoreAllocation_ = rhs.fCapacityOfFreeStoreAllocation_;
200 rhs.fLiveData_ = rhs.BufferAsT_ ();
201 this->fSize_ = rhs.fSize_;
202 rhs.fSize_ = 0;
203 Ensure (rhs.fSize_ == 0);
204 Ensure (rhs.capacity () == BUF_SIZE);
206#if qStroika_Foundation_Debug_AssertionsChecked
207 Ensure (this->size () == origFromSize);
208 Ensure (this->capacity () == origFromCapacity);
209#endif
210 return *this;
211 }
212 template <typename T, size_t BUF_SIZE>
213 template <ISpanOfT<T> SPAN_T>
214 InlineBuffer<T, BUF_SIZE>& InlineBuffer<T, BUF_SIZE>::operator= (const SPAN_T& copyFrom)
215 {
216 Invariant ();
217 DestroyElts_ (this->begin (), this->end ());
218 fSize_ = 0;
219 if (not this->HasEnoughCapacity_ (copyFrom.size ())) [[unlikely]] {
220 reserve (copyFrom.size ());
221 }
222 uninitialized_copy (copyFrom.begin (), copyFrom.end (), this->begin ());
223 Invariant ();
224 return *this;
225 }
226 template <typename T, size_t BUF_SIZE>
227 inline void InlineBuffer<T, BUF_SIZE>::GrowToSize (size_t nElements)
228 requires (default_initializable<T>)
229 {
230 if (nElements > size ()) {
231 resize (nElements);
232 }
233 Ensure (size () >= nElements);
234 }
235 template <typename T, size_t BUF_SIZE>
236 inline void InlineBuffer<T, BUF_SIZE>::GrowToSize (size_t nElements, Common::ArgByValueType<T> fillValue)
237 {
238 if (nElements > size ()) {
239 resize (nElements, fillValue);
240 }
241 Ensure (size () >= nElements);
242 }
243 template <typename T, size_t BUF_SIZE>
245 requires (is_trivially_copyable_v<T>)
246 {
247 static_assert (is_trivially_copyable_v<T>);
248 if (nElements > size ()) {
249 resize_uninitialized (nElements);
250 }
251 Ensure (size () >= nElements);
252 }
253 template <typename T, size_t BUF_SIZE>
254 inline void InlineBuffer<T, BUF_SIZE>::resize (size_t nElements)
255 requires (default_initializable<T>)
256 {
257 // only build a fill value when actually GROWING - a shrink constructs nothing, as before
258 if (nElements > fSize_) {
259 resize (nElements, T{});
260 }
261 else {
262 ShrinkTo (nElements);
263 }
264 }
265 template <typename T, size_t BUF_SIZE>
266 inline void InlineBuffer<T, BUF_SIZE>::resize (size_t nElements, Common::ArgByValueType<T> fillValue)
267 {
268 if (nElements > fSize_) {
269 // Growing
270 if (not this->HasEnoughCapacity_ (nElements)) [[unlikely]] {
271 reserve (nElements, true);
272 }
273 Assert (nElements <= capacity ());
274 Assert (this->begin () + fSize_ == this->end ()); // docs/clarity
275 auto newEnd = this->begin () + nElements;
276 Assert (this->end () < newEnd);
277 // uninitialized_fill() guarantees filling all or none - if an exception doing some, it undoes the ones it did (so setting fsize after safe)
278 uninitialized_fill (this->end (), newEnd, fillValue);
279 fSize_ = nElements;
280 Assert (this->end () == newEnd);
281 }
282 else if (nElements < fSize_) {
283 // Shrinking
284 DestroyElts_ (this->begin () + nElements, this->end ());
285 fSize_ = nElements;
286 }
287 Assert (fSize_ == nElements);
288 Ensure (size () <= capacity ());
289 }
290 template <typename T, size_t BUF_SIZE>
292 requires (is_trivially_copyable_v<T> and is_trivially_destructible_v<T>)
293 {
294 if (not this->HasEnoughCapacity_ (nElements)) [[unlikely]] {
295 reserve (nElements);
296 }
297 fSize_ = nElements;
298 Assert (fSize_ == nElements);
299 Ensure (size () <= capacity ());
300 }
301 template <typename T, size_t BUF_SIZE>
302 inline void InlineBuffer<T, BUF_SIZE>::ShrinkTo (size_t nElements)
303 {
304 Require (nElements <= fSize_);
305 if (nElements != fSize_) {
306 DestroyElts_ (this->begin () + nElements, this->end ());
307 fSize_ = nElements;
308 }
309 Assert (fSize_ == nElements);
310 Ensure (size () <= capacity ());
311 }
312 template <typename T, size_t BUF_SIZE>
313 inline typename InlineBuffer<T, BUF_SIZE>::iterator InlineBuffer<T, BUF_SIZE>::begin () noexcept
315 return fLiveData_;
316 }
317 template <typename T, size_t BUF_SIZE>
318 inline typename InlineBuffer<T, BUF_SIZE>::iterator InlineBuffer<T, BUF_SIZE>::end () noexcept
319 {
320 return fLiveData_ + fSize_;
321 }
322 template <typename T, size_t BUF_SIZE>
323 inline typename InlineBuffer<T, BUF_SIZE>::const_iterator InlineBuffer<T, BUF_SIZE>::begin () const noexcept
324 {
325 return fLiveData_;
326 }
327 template <typename T, size_t BUF_SIZE>
328 inline typename InlineBuffer<T, BUF_SIZE>::const_iterator InlineBuffer<T, BUF_SIZE>::end () const noexcept
329 {
330 return fLiveData_ + fSize_;
331 }
332 template <typename T, size_t BUF_SIZE>
333 inline typename InlineBuffer<T, BUF_SIZE>::pointer InlineBuffer<T, BUF_SIZE>::data () noexcept
334 {
335 return fLiveData_;
336 }
337 template <typename T, size_t BUF_SIZE>
338 inline typename InlineBuffer<T, BUF_SIZE>::const_pointer InlineBuffer<T, BUF_SIZE>::data () const noexcept
339 {
340 return fLiveData_;
341 }
342 template <typename T, size_t BUF_SIZE>
343 constexpr size_t InlineBuffer<T, BUF_SIZE>::capacity () const noexcept
344 {
345 // @see class Design Note
346 if (UsingInlinePreallocatedBuffer_ ()) [[likely]] {
347 return BUF_SIZE;
348 }
349 else {
350 // this case happens precisely in InlineBuffer<T, BUF_SIZE>::reserve() when the capacity is set > BUF_SIZE so initialized then...
351 return fCapacityOfFreeStoreAllocation_;
352 }
353 }
354 template <typename T, size_t BUF_SIZE>
355 void InlineBuffer<T, BUF_SIZE>::reserve (size_t newCapacity, bool atLeast)
356 {
357 Require (newCapacity >= size ());
358 size_t useNewCapacity = newCapacity;
359 size_t oldCapacity = capacity ();
360 if (atLeast) [[likely]] {
361 if (useNewCapacity <= oldCapacity) [[likely]] {
362 return; // no work todo here....
363 }
364 // if fits in inline buffer, round up to that size. If exceeding that, use ScaledUpCapacity exponential growth algorithm
365 if (useNewCapacity < BUF_SIZE) [[likely]] {
366 useNewCapacity = BUF_SIZE;
367 }
368 else {
369 useNewCapacity = Foundation::Containers::Support::ReserveTweaks::GetScaledUpCapacity (useNewCapacity, sizeof (T),
370 Math::AtLeast<size_t> (BUF_SIZE, 1));
371 }
372 }
373 Invariant ();
374 // NOTE - could be upsizing or downsizing, and could be moving to for from allocated or inline buffers
375 Assert (useNewCapacity >= newCapacity);
376 if (useNewCapacity != oldCapacity) [[unlikely]] {
377 bool oldInPlaceBuffer = oldCapacity <= BUF_SIZE;
378 bool newInPlaceBuffer = useNewCapacity <= BUF_SIZE;
379 if constexpr (is_trivially_copyable_v<T>) {
380 // we only need to copy if going from oldInPlaceBuffer != newInPlaceBuffer, else just use realloc
381 if (not oldInPlaceBuffer and not newInPlaceBuffer) {
382 // realloc
383 fLiveData_ = reinterpret_cast<T*> (Reallocate_ (LiveDataAsAllocatedBytes_ (), SizeInBytes_ (useNewCapacity)));
384 fCapacityOfFreeStoreAllocation_ = useNewCapacity;
385 }
386 else if (oldInPlaceBuffer and newInPlaceBuffer) {
387 // pretty common case - just do nothing
388 }
389 else if (oldInPlaceBuffer and not newInPlaceBuffer) {
390 // malloc in this case
391 byte* newPtr = Allocate_ (SizeInBytes_ (useNewCapacity));
392 // Initialize new memory from old
393 Assert (this->begin () != reinterpret_cast<T*> (newPtr));
394 Assert (static_cast<size_t> (this->end () - this->begin ()) <= useNewCapacity); // so no possible overflow
395 uninitialized_copy (this->begin (), this->end (), reinterpret_cast<T*> (newPtr));
396 fLiveData_ = reinterpret_cast<T*> (newPtr);
397 if (not newInPlaceBuffer) {
398 fCapacityOfFreeStoreAllocation_ = useNewCapacity;
399 }
400 }
401 else if (not oldInPlaceBuffer and newInPlaceBuffer) {
402 // was malloced, but not needed anymore - so free
403 byte* newPtr = std::begin (fInlinePreallocatedBuffer_);
404 // Initialize new memory from old
405 Assert (this->begin () != reinterpret_cast<T*> (newPtr));
406 Assert (static_cast<size_t> (this->end () - this->begin ()) <= useNewCapacity); // so no possible overflow
407 uninitialized_copy (this->begin (), this->end (), reinterpret_cast<T*> (newPtr));
408 Deallocate_ (LiveDataAsAllocatedBytes_ ());
409 fLiveData_ = reinterpret_cast<T*> (newPtr);
410 }
411 }
412 else {
413 // Only if we changed if using inplace buffer, or if was and is using ramBuffer, and eltCount changed do we need to do anything
414 if (oldInPlaceBuffer != newInPlaceBuffer or (not newInPlaceBuffer)) {
415 byte* newPtr = newInPlaceBuffer ? std::begin (fInlinePreallocatedBuffer_) : Allocate_ (SizeInBytes_ (useNewCapacity));
416
417 // Initialize new memory from old
418 Assert (this->begin () != reinterpret_cast<T*> (newPtr));
419 Assert (static_cast<size_t> (this->end () - this->begin ()) <= useNewCapacity); // so no possible overflow
420 uninitialized_copy (this->begin (), this->end (), reinterpret_cast<T*> (newPtr));
421
422 // destroy objects in old memory
423 DestroyElts_ (this->begin (), this->end ());
424
425 // free old memory if needed
426 if (not oldInPlaceBuffer) {
427 Assert (not UsingInlinePreallocatedBuffer_ ());
428 Deallocate_ (LiveDataAsAllocatedBytes_ ());
429 }
430
431 fLiveData_ = reinterpret_cast<T*> (newPtr);
432 if (not newInPlaceBuffer) {
433 fCapacityOfFreeStoreAllocation_ = useNewCapacity;
434 }
435 }
436 }
437 }
438 Ensure ((useNewCapacity <= BUF_SIZE and capacity () == BUF_SIZE) or (useNewCapacity > BUF_SIZE and useNewCapacity == capacity ()));
439 Invariant ();
440 }
441 template <typename T, size_t BUF_SIZE>
442 inline size_t InlineBuffer<T, BUF_SIZE>::GetSize () const noexcept
443 {
444 Ensure (fSize_ <= capacity ());
445 return fSize_;
446 }
447 template <typename T, size_t BUF_SIZE>
448 inline size_t InlineBuffer<T, BUF_SIZE>::size () const noexcept
449 {
450 Ensure (fSize_ <= capacity ());
451 return fSize_;
452 }
453 template <typename T, size_t BUF_SIZE>
454 inline bool InlineBuffer<T, BUF_SIZE>::empty () const noexcept
455 {
456 return fSize_ == 0;
457 }
458 template <typename T, size_t BUF_SIZE>
459 inline typename InlineBuffer<T, BUF_SIZE>::reference InlineBuffer<T, BUF_SIZE>::at (size_t i) noexcept
460 {
461 Require (i < fSize_);
462 return *(fLiveData_ + i);
463 }
464 template <typename T, size_t BUF_SIZE>
465 inline typename InlineBuffer<T, BUF_SIZE>::const_reference InlineBuffer<T, BUF_SIZE>::at (size_t i) const noexcept
466 {
467 Require (i < fSize_);
468 return *(fLiveData_ + i);
469 }
470 template <typename T, size_t BUF_SIZE>
471 inline auto InlineBuffer<T, BUF_SIZE>::operator[] (size_t i) noexcept -> reference
472 {
473 return at (i);
474 }
475 template <typename T, size_t BUF_SIZE>
476 inline auto InlineBuffer<T, BUF_SIZE>::operator[] (size_t i) const noexcept -> const_reference
477 {
478 return at (i);
479 }
480 template <typename T, size_t BUF_SIZE>
481 template <ISpanOfT<T> SPAN_T>
482 inline void InlineBuffer<T, BUF_SIZE>::Insert (size_t at, const SPAN_T& copyFrom)
483 {
484 size_t s = size ();
485 size_t n2Add = copyFrom.size ();
486 size_t newS = s + n2Add;
487 if (not this->HasEnoughCapacity_ (newS)) [[unlikely]] {
488 reserve (newS);
489 }
490 Assert (this->HasEnoughCapacity_ (newS));
491 this->fSize_ = Memory::Insert (span{this->begin (), size ()}, span{this->begin (), capacity ()}, at, copyFrom).size ();
492 Assert (this->fSize_ == newS);
493 }
494 template <typename T, size_t BUF_SIZE>
495 inline void InlineBuffer<T, BUF_SIZE>::Insert (size_t at, const T& item)
496 {
497 Insert (at, span{&item, 1});
498 }
499 template <typename T, size_t BUF_SIZE>
500 inline void InlineBuffer<T, BUF_SIZE>::insert (iterator i, const_pointer from, const_pointer to)
501 {
502 Insert (i - begin (), span{from, to});
503 }
504 template <typename T, size_t BUF_SIZE>
506 {
507 size_t s = size ();
508 size_t newS = s + 1;
509 if (not this->HasEnoughCapacity_ (newS)) [[unlikely]] {
510 reserve (newS);
511 }
512 if constexpr (is_trivially_copyable_v<T>) {
513 fLiveData_[s] = e;
514 }
515 else {
516 uninitialized_copy (&e, &e + 1, this->begin () + s); // copy into uninitialized memory so careful
517 }
518 ++this->fSize_;
519 Invariant ();
520 }
521 template <typename T, size_t BUF_SIZE>
522 template <ISpanOfT<T> SPAN_T>
523 void InlineBuffer<T, BUF_SIZE>::push_back (const SPAN_T& copyFrom)
524 {
525 size_t s = size ();
526 size_t newS = s + copyFrom.size ();
527 if (not this->HasEnoughCapacity_ (newS)) [[unlikely]] {
528 reserve (newS);
529 }
530 Assert (this->HasEnoughCapacity_ (newS));
531 if constexpr (is_trivially_copyable_v<T>) {
532 CopySpanData (copyFrom, span{this->begin () + s, copyFrom.size ()});
533 }
534 else {
535 uninitialized_copy (copyFrom.begin (), copyFrom.end (), this->begin () + s); // copy into uninitialized memory so careful
536 }
537 this->fSize_ = newS;
538 }
539 template <typename T, size_t BUF_SIZE>
540 template <ISpan SPAN_T>
541 void InlineBuffer<T, BUF_SIZE>::push_back_coerced (const SPAN_T& copyFrom)
542 {
543 size_t s = size ();
544 size_t newS = s + copyFrom.size ();
545 if (not this->HasEnoughCapacity_ (newS)) [[unlikely]] {
546 reserve (newS);
547 }
548 Assert (this->HasEnoughCapacity_ (newS));
549 auto outPtr = this->begin () + s;
550 if constexpr (is_trivially_copyable_v<T>) {
551 CopySpanData (copyFrom, span{outPtr, copyFrom.size ()});
552 }
553 else {
554 uninitialized_copy (copyFrom.begin (), copyFrom.end (), outPtr); // copy into uninitialized memory so careful
555 }
556 this->fSize_ = newS;
557 }
558 template <typename T, size_t BUF_SIZE>
559 inline void InlineBuffer<T, BUF_SIZE>::Remove (size_t at)
560 {
561 Remove (at, at + 1);
562 }
563 template <typename T, size_t BUF_SIZE>
564 void InlineBuffer<T, BUF_SIZE>::Remove (size_t from, size_t to)
565 {
566 Require (from <= to);
567 Require (to <= size ());
568#if qStroika_Foundation_Debug_AssertionsChecked
569 size_t newSz = size () - (to - from);
570#endif
571 this->fSize_ = Memory::Remove (span{this->data (), size ()}, span{this->data (), capacity ()}, from, to).size ();
572#if qStroika_Foundation_Debug_AssertionsChecked
573 Assert (this->fSize_ == newSz);
574#endif
575 }
576 template <typename T, size_t BUF_SIZE>
577 inline void InlineBuffer<T, BUF_SIZE>::clear () noexcept
578 {
579 // ShrinkTo (), NOT resize (0): both do the same thing for a shrink, but resize () also contains the
580 // GROW branch's uninitialized_fill (..., T{}), and instantiating it compiles that T{} even though a
581 // shrink can never reach it. That made clear () - and so any code merely CALLING clear () - require
582 // default_initializable<T>, for a code path that cannot run. See the note on resize ().
583 ShrinkTo (0);
584 }
585 template <typename T, size_t BUF_SIZE>
586 inline InlineBuffer<T, BUF_SIZE>::operator T*() noexcept
587 {
588 AssertNotNull (fLiveData_);
589 return fLiveData_;
590 }
591 template <typename T, size_t BUF_SIZE>
592 inline InlineBuffer<T, BUF_SIZE>::operator const T*() const noexcept
593 {
594 AssertNotNull (fLiveData_);
595 return fLiveData_;
596 }
597 template <typename T, size_t BUF_SIZE>
598 inline void InlineBuffer<T, BUF_SIZE>::Invariant () const noexcept
599 {
600#if qStroika_Foundation_Debug_AssertionsChecked
601 Invariant_ ();
602#endif
603 }
604#if qStroika_Foundation_Debug_AssertionsChecked
605 template <typename T, size_t BUF_SIZE>
606 void InlineBuffer<T, BUF_SIZE>::Invariant_ () const noexcept
607 {
608 Assert (capacity () >= size ());
609 ValidateGuards_ ();
610 }
611 template <typename T, size_t BUF_SIZE>
612 void InlineBuffer<T, BUF_SIZE>::ValidateGuards_ () const noexcept
613 {
614 Assert (::memcmp (kGuard1_, fGuard1_, sizeof (kGuard1_)) == 0);
615 Assert (::memcmp (kGuard2_, fGuard2_, sizeof (kGuard2_)) == 0);
616 }
617#endif
618 template <typename T, size_t BUF_SIZE>
619 constexpr T* InlineBuffer<T, BUF_SIZE>::BufferAsT_ () noexcept
620 {
621 return reinterpret_cast<T*> (&fInlinePreallocatedBuffer_[0]);
622 }
623 template <typename T, size_t BUF_SIZE>
624 constexpr const T* InlineBuffer<T, BUF_SIZE>::BufferAsT_ () const noexcept
625 {
626 return reinterpret_cast<const T*> (&fInlinePreallocatedBuffer_[0]);
627 }
628 template <typename T, size_t BUF_SIZE>
629 inline void InlineBuffer<T, BUF_SIZE>::DestroyElts_ (T* start, T* end) noexcept
630 {
631 if constexpr (not is_trivially_destructible_v<T>) {
632 for (auto i = start; i != end; ++i) {
633 destroy_at (i);
634 }
635 }
636 }
637 template <typename T, size_t BUF_SIZE>
638 inline byte* InlineBuffer<T, BUF_SIZE>::LiveDataAsAllocatedBytes_ () noexcept
639 {
640 Require (not UsingInlinePreallocatedBuffer_ ());
641 return reinterpret_cast<byte*> (fLiveData_);
642 }
643 template <typename T, size_t BUF_SIZE>
644 inline byte* InlineBuffer<T, BUF_SIZE>::Allocate_ (size_t bytes)
645 {
646 void* p = ::malloc (bytes);
648 return reinterpret_cast<byte*> (p);
649 }
650 template <typename T, size_t BUF_SIZE>
651 inline void InlineBuffer<T, BUF_SIZE>::Deallocate_ (byte* bytes) noexcept
652 {
653 if (bytes != nullptr) [[likely]] {
654 ::free (bytes);
655 }
656 }
657 template <typename T, size_t BUF_SIZE>
658 inline byte* InlineBuffer<T, BUF_SIZE>::Reallocate_ (byte* bytes, size_t n)
659 requires (is_trivially_copyable_v<T>)
660 {
661 if (n == 0) {
662 Deallocate_ (bytes);
663 return nullptr;
664 }
665 else {
666 if (bytes == nullptr) {
667 return Allocate_ (n);
668 }
669 else {
670 byte* p = reinterpret_cast<byte*> (::realloc (bytes, n));
672 return p;
673 }
674 }
675 }
676 template <typename T, size_t BUF_SIZE>
677 constexpr bool InlineBuffer<T, BUF_SIZE>::UsingInlinePreallocatedBuffer_ () const noexcept
678 {
679 return fLiveData_ == BufferAsT_ ();
680 }
681
682}
#define AssertNotNull(p)
Definition Assertions.h:336
Logically halfway between std::array and std::vector; Smart 'direct memory array' - which when needed...
nonvirtual void GrowToSize_uninitialized(size_t nElements)
same as GrowToSize (), except leaves newly created elements uninitialized (requires is_trivially_copy...
nonvirtual pointer data() noexcept
returns a (possibly const) pointer to the start of the live buffer data. This return value can be inv...
nonvirtual void push_back(Common::ArgByValueType< T > e)
constexpr size_t capacity() const noexcept
nonvirtual size_t size() const noexcept
nonvirtual reference at(size_t i) noexcept
nonvirtual void push_back_coerced(const SPAN_T &copyFrom)
same as push_back (span{}) except that the span type doesn't need to match exactly,...
nonvirtual void GrowToSize(size_t nElements)
nonvirtual void reserve(size_t newCapacity, bool atLeast=true)
nonvirtual reference operator[](size_t i) noexcept
nonvirtual void resize(size_t nElements)
Grow or shrink the buffer. The 'size' is the number of constructed elements, and this function automa...
nonvirtual void ShrinkTo(size_t nElements)
nonvirtual size_t GetSize() const noexcept
nonvirtual void insert(iterator i, const_pointer from, const_pointer to)
nonvirtual bool empty() const noexcept
nonvirtual void resize_uninitialized(size_t nElements)
same as resize (), except leaves newly created elements uninitialized (requires is_trivially_copyable...
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
void ThrowIfNull(const Private_::ConstVoidStar &p, const HRESULT &hr)
Template specialization for ThrowIfNull (), for thing being thrown HRESULT - really throw HRESULTErro...