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