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