Stroika Library 3.0d18
 
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 this->fCapacityOfFreeStoreAllocation_ = rhs.fCapacityOfFreeStoreAllocation_;
187 rhs.fLiveData_ = rhs.BufferAsT_ ();
188 this->fSize_ = rhs.fSize_;
189 rhs.fSize_ = 0;
190 Ensure (rhs.fSize_ == 0);
191 Ensure (rhs.capacity () == BUF_SIZE);
192 }
193#if qStroika_Foundation_Debug_AssertionsChecked
194 Ensure (this->size () == origFromSize);
195 Ensure (this->capacity () == origFromCapacity);
196#endif
197 return *this;
198 }
199 template <typename T, size_t BUF_SIZE>
200 template <ISpanOfT<T> SPAN_T>
202 {
203 Invariant ();
204 DestroyElts_ (this->begin (), this->end ());
205 fSize_ = 0;
206 if (not this->HasEnoughCapacity_ (copyFrom.size ())) [[unlikely]] {
207 reserve (copyFrom.size ());
208 }
209 uninitialized_copy (copyFrom.begin (), copyFrom.end (), this->begin ());
210 Invariant ();
211 return *this;
212 }
213 template <typename T, size_t BUF_SIZE>
214 inline void InlineBuffer<T, BUF_SIZE>::GrowToSize (size_t nElements)
215 {
216 if (nElements > size ()) {
217 resize (nElements);
218 }
219 Ensure (size () >= nElements);
220 }
221 template <typename T, size_t BUF_SIZE>
223 requires (is_trivially_copyable_v<T>)
224 {
225 static_assert (is_trivially_copyable_v<T>);
226 if (nElements > size ()) {
227 resize_uninitialized (nElements);
228 }
229 Ensure (size () >= nElements);
230 }
231 template <typename T, size_t BUF_SIZE>
232 inline void InlineBuffer<T, BUF_SIZE>::resize (size_t nElements)
233 {
234 if (nElements > fSize_) {
235 // Growing
236 if (not this->HasEnoughCapacity_ (nElements)) [[unlikely]] {
237 reserve (nElements, true);
239 Assert (nElements <= capacity ());
240 Assert (this->begin () + fSize_ == this->end ()); // docs/clarity
241 auto newEnd = this->begin () + nElements;
242 Assert (this->end () < newEnd);
243 // uninitialized_fill() guarantees filling all or none - if an exception doing some, it undoes the ones it did (so setting fsize after safe)
244 uninitialized_fill (this->end (), newEnd, T{});
245 fSize_ = nElements;
246 Assert (this->end () == newEnd);
247 }
248 else if (nElements < fSize_) {
249 // Shrinking
250 DestroyElts_ (this->begin () + nElements, this->end ());
251 fSize_ = nElements;
252 }
253 Assert (fSize_ == nElements);
254 Ensure (size () <= capacity ());
256 template <typename T, size_t BUF_SIZE>
258 requires (is_trivially_copyable_v<T> and is_trivially_destructible_v<T>)
259 {
260 if (not this->HasEnoughCapacity_ (nElements)) [[unlikely]] {
261 reserve (nElements);
262 }
263 fSize_ = nElements;
264 Assert (fSize_ == nElements);
265 Ensure (size () <= capacity ());
266 }
267 template <typename T, size_t BUF_SIZE>
268 inline void InlineBuffer<T, BUF_SIZE>::ShrinkTo (size_t nElements)
269 {
270 Require (nElements <= fSize_);
271 if (nElements != fSize_) {
272 DestroyElts_ (this->begin () + nElements, this->end ());
273 fSize_ = nElements;
274 }
275 Assert (fSize_ == nElements);
276 Ensure (size () <= capacity ());
277 }
278 template <typename T, size_t BUF_SIZE>
279 inline typename InlineBuffer<T, BUF_SIZE>::iterator InlineBuffer<T, BUF_SIZE>::begin () noexcept
280 {
281 return fLiveData_;
283 template <typename T, size_t BUF_SIZE>
284 inline typename InlineBuffer<T, BUF_SIZE>::iterator InlineBuffer<T, BUF_SIZE>::end () noexcept
285 {
286 return fLiveData_ + fSize_;
287 }
288 template <typename T, size_t BUF_SIZE>
289 inline typename InlineBuffer<T, BUF_SIZE>::const_iterator InlineBuffer<T, BUF_SIZE>::begin () const noexcept
290 {
291 return fLiveData_;
292 }
293 template <typename T, size_t BUF_SIZE>
294 inline typename InlineBuffer<T, BUF_SIZE>::const_iterator InlineBuffer<T, BUF_SIZE>::end () const noexcept
295 {
296 return fLiveData_ + fSize_;
297 }
298 template <typename T, size_t BUF_SIZE>
299 inline typename InlineBuffer<T, BUF_SIZE>::pointer InlineBuffer<T, BUF_SIZE>::data () noexcept
300 {
301 return fLiveData_;
302 }
303 template <typename T, size_t BUF_SIZE>
304 inline typename InlineBuffer<T, BUF_SIZE>::const_pointer InlineBuffer<T, BUF_SIZE>::data () const noexcept
306 return fLiveData_;
307 }
308 template <typename T, size_t BUF_SIZE>
309 constexpr size_t InlineBuffer<T, BUF_SIZE>::capacity () const noexcept
310 {
311 // @see class Design Note
312 if (UsingInlinePreallocatedBuffer_ ()) [[likely]] {
313 return BUF_SIZE;
314 }
315 else {
316 // this case happens precisely in InlineBuffer<T, BUF_SIZE>::reserve() when the capacity is set > BUF_SIZE so initialized then...
317 DISABLE_COMPILER_GCC_WARNING_START ("GCC diagnostic ignored \"-Wmaybe-uninitialized\""); // RASPI RELEASE COMPILER gcc12 ONLY
318 return fCapacityOfFreeStoreAllocation_;
319 DISABLE_COMPILER_GCC_WARNING_END ("GCC diagnostic ignored \"-Wmaybe-uninitialized\"");
320 }
321 }
322 template <typename T, size_t BUF_SIZE>
323 void InlineBuffer<T, BUF_SIZE>::reserve (size_t newCapacity, bool atLeast)
324 {
325 Require (newCapacity >= size ());
326 size_t useNewCapacity = newCapacity;
327 size_t oldCapacity = capacity ();
328 if (atLeast) [[likely]] {
329 if (useNewCapacity <= oldCapacity) [[likely]] {
330 return; // no work todo here....
331 }
332 // if fits in inline buffer, round up to that size. If exceeding that, use ScaledUpCapacity exponential growth algorithm
333 if (useNewCapacity < BUF_SIZE) [[likely]] {
334 useNewCapacity = BUF_SIZE;
336 else {
337 useNewCapacity = Foundation::Containers::Support::ReserveTweaks::GetScaledUpCapacity (useNewCapacity, sizeof (T),
338 Math::AtLeast<size_t> (BUF_SIZE, 1));
339 }
340 }
341 Invariant ();
342 // NOTE - could be upsizing or downsizing, and could be moving to for from allocated or inline buffers
343 Assert (useNewCapacity >= newCapacity);
344 if (useNewCapacity != oldCapacity) [[unlikely]] {
345 bool oldInPlaceBuffer = oldCapacity <= BUF_SIZE;
346 bool newInPlaceBuffer = useNewCapacity <= BUF_SIZE;
347 if constexpr (is_trivially_copyable_v<T>) {
348 // we only need to copy if going from oldInPlaceBuffer != newInPlaceBuffer, else just use realloc
349 if (not oldInPlaceBuffer and not newInPlaceBuffer) {
350 // realloc
351 fLiveData_ = reinterpret_cast<T*> (Reallocate_ (LiveDataAsAllocatedBytes_ (), SizeInBytes_ (useNewCapacity)));
352 fCapacityOfFreeStoreAllocation_ = useNewCapacity;
353 }
354 else if (oldInPlaceBuffer and newInPlaceBuffer) {
355 // pretty common case - just do nothing
356 }
357 else if (oldInPlaceBuffer and not newInPlaceBuffer) {
358 // malloc in this case
359 byte* newPtr = Allocate_ (SizeInBytes_ (useNewCapacity));
360 // Initialize new memory from old
361 Assert (this->begin () != reinterpret_cast<T*> (newPtr));
362 Assert (static_cast<size_t> (this->end () - this->begin ()) <= useNewCapacity); // so no possible overflow
363 uninitialized_copy (this->begin (), this->end (), reinterpret_cast<T*> (newPtr));
364 fLiveData_ = reinterpret_cast<T*> (newPtr);
365 if (not newInPlaceBuffer) {
366 fCapacityOfFreeStoreAllocation_ = useNewCapacity;
367 }
368 }
369 else if (not oldInPlaceBuffer and newInPlaceBuffer) {
370 // was malloced, but not needed anymore - so free
371 byte* newPtr = std::begin (fInlinePreallocatedBuffer_);
372 // Initialize new memory from old
373 Assert (this->begin () != reinterpret_cast<T*> (newPtr));
374 Assert (static_cast<size_t> (this->end () - this->begin ()) <= useNewCapacity); // so no possible overflow
375 uninitialized_copy (this->begin (), this->end (), reinterpret_cast<T*> (newPtr));
376 Deallocate_ (LiveDataAsAllocatedBytes_ ());
377 fLiveData_ = reinterpret_cast<T*> (newPtr);
378 }
379 }
380 else {
381 // Only if we changed if using inplace buffer, or if was and is using ramBuffer, and eltCount changed do we need to do anything
382 if (oldInPlaceBuffer != newInPlaceBuffer or (not newInPlaceBuffer)) {
383 byte* newPtr = newInPlaceBuffer ? std::begin (fInlinePreallocatedBuffer_) : Allocate_ (SizeInBytes_ (useNewCapacity));
384
385 // Initialize new memory from old
386 Assert (this->begin () != reinterpret_cast<T*> (newPtr));
387 Assert (static_cast<size_t> (this->end () - this->begin ()) <= useNewCapacity); // so no possible overflow
388 uninitialized_copy (this->begin (), this->end (), reinterpret_cast<T*> (newPtr));
389
390 // destroy objects in old memory
391 DestroyElts_ (this->begin (), this->end ());
392
393 // free old memory if needed
394 if (not oldInPlaceBuffer) {
395 Assert (not UsingInlinePreallocatedBuffer_ ());
396 Deallocate_ (LiveDataAsAllocatedBytes_ ());
397 }
398
399 fLiveData_ = reinterpret_cast<T*> (newPtr);
400 if (not newInPlaceBuffer) {
401 fCapacityOfFreeStoreAllocation_ = useNewCapacity;
402 }
403 }
404 }
405 }
406 Ensure ((useNewCapacity <= BUF_SIZE and capacity () == BUF_SIZE) or (useNewCapacity > BUF_SIZE and useNewCapacity == capacity ()));
407 Invariant ();
408 }
409 template <typename T, size_t BUF_SIZE>
410 inline size_t InlineBuffer<T, BUF_SIZE>::GetSize () const noexcept
411 {
412 Ensure (fSize_ <= capacity ());
413 return fSize_;
414 }
415 template <typename T, size_t BUF_SIZE>
416 inline size_t InlineBuffer<T, BUF_SIZE>::size () const noexcept
417 {
418 Ensure (fSize_ <= capacity ());
419 return fSize_;
420 }
421 template <typename T, size_t BUF_SIZE>
422 inline bool InlineBuffer<T, BUF_SIZE>::empty () const noexcept
423 {
424 return fSize_ == 0;
425 }
426 template <typename T, size_t BUF_SIZE>
427 inline typename InlineBuffer<T, BUF_SIZE>::reference InlineBuffer<T, BUF_SIZE>::at (size_t i) noexcept
428 {
429 Require (i < fSize_);
430 return *(fLiveData_ + i);
431 }
432 template <typename T, size_t BUF_SIZE>
433 inline typename InlineBuffer<T, BUF_SIZE>::const_reference InlineBuffer<T, BUF_SIZE>::at (size_t i) const noexcept
434 {
435 Require (i < fSize_);
436 return *(fLiveData_ + i);
437 }
438 template <typename T, size_t BUF_SIZE>
439 inline auto InlineBuffer<T, BUF_SIZE>::operator[] (size_t i) noexcept -> reference
440 {
441 return at (i);
442 }
443 template <typename T, size_t BUF_SIZE>
444 inline auto InlineBuffer<T, BUF_SIZE>::operator[] (size_t i) const noexcept -> const_reference
445 {
446 return at (i);
447 }
448 template <typename T, size_t BUF_SIZE>
449 template <ISpanOfT<T> SPAN_T>
450 inline void InlineBuffer<T, BUF_SIZE>::Insert (size_t at, const SPAN_T& copyFrom)
451 {
452 size_t s = size ();
453 size_t n2Add = copyFrom.size ();
454 size_t newS = s + n2Add;
455 if (not this->HasEnoughCapacity_ (newS)) [[unlikely]] {
456 reserve (newS);
457 }
458 Assert (this->HasEnoughCapacity_ (newS));
459 this->fSize_ = Memory::Insert (span{this->begin (), size ()}, span{this->begin (), capacity ()}, at, copyFrom).size ();
460 Assert (this->fSize_ == newS);
461 }
462 template <typename T, size_t BUF_SIZE>
463 inline void InlineBuffer<T, BUF_SIZE>::Insert (size_t at, const T& item)
464 {
465 Insert (at, span{&item, 1});
466 }
467 template <typename T, size_t BUF_SIZE>
468 inline void InlineBuffer<T, BUF_SIZE>::insert (iterator i, const_pointer from, const_pointer to)
469 {
470 Insert (i - begin (), span{from, to});
471 }
472 template <typename T, size_t BUF_SIZE>
474 {
475 size_t s = size ();
476 size_t newS = s + 1;
477 if (not this->HasEnoughCapacity_ (newS)) [[unlikely]] {
478 reserve (newS);
479 }
480 if constexpr (is_trivially_copyable_v<T>) {
481 fLiveData_[s] = e;
482 }
483 else {
484 uninitialized_copy (&e, &e + 1, this->begin () + s); // copy into uninitialized memory so careful
485 }
486 ++this->fSize_;
487 Invariant ();
488 }
489 template <typename T, size_t BUF_SIZE>
490 template <ISpanOfT<T> SPAN_T>
491 void InlineBuffer<T, BUF_SIZE>::push_back (const SPAN_T& copyFrom)
492 {
493 size_t s = size ();
494 size_t newS = s + copyFrom.size ();
495 if (not this->HasEnoughCapacity_ (newS)) [[unlikely]] {
496 reserve (newS);
497 }
498 Assert (this->HasEnoughCapacity_ (newS));
499 if constexpr (is_trivially_copyable_v<T>) {
500 CopySpanData (copyFrom, span{this->begin () + s, copyFrom.size ()});
501 }
502 else {
503 uninitialized_copy (copyFrom.begin (), copyFrom.end (), this->begin () + s); // copy into uninitialized memory so careful
504 }
505 this->fSize_ = newS;
506 }
507 template <typename T, size_t BUF_SIZE>
508 template <ISpan SPAN_T>
509 void InlineBuffer<T, BUF_SIZE>::push_back_coerced (const SPAN_T& copyFrom)
510 {
511 size_t s = size ();
512 size_t newS = s + copyFrom.size ();
513 if (not this->HasEnoughCapacity_ (newS)) [[unlikely]] {
514 reserve (newS);
515 }
516 Assert (this->HasEnoughCapacity_ (newS));
517 auto outPtr = this->begin () + s;
518 if constexpr (is_trivially_copyable_v<T>) {
519 CopySpanData (copyFrom, span{outPtr, copyFrom.size ()});
520 }
521 else {
522 uninitialized_copy (copyFrom.begin (), copyFrom.end (), outPtr); // copy into uninitialized memory so careful
523 }
524 this->fSize_ = newS;
525 }
526 template <typename T, size_t BUF_SIZE>
527 inline void InlineBuffer<T, BUF_SIZE>::Remove (size_t at)
528 {
529 Remove (at, at + 1);
530 }
531 template <typename T, size_t BUF_SIZE>
532 void InlineBuffer<T, BUF_SIZE>::Remove (size_t from, size_t to)
533 {
534 Require (from <= to);
535 Require (to <= size ());
536#if qStroika_Foundation_Debug_AssertionsChecked
537 size_t newSz = size () - (to - from);
538#endif
539 this->fSize_ = Memory::Remove (span{this->data (), size ()}, span{this->data (), capacity ()}, from, to).size ();
540#if qStroika_Foundation_Debug_AssertionsChecked
541 Assert (this->fSize_ == newSz);
542#endif
543 }
544 template <typename T, size_t BUF_SIZE>
545 inline void InlineBuffer<T, BUF_SIZE>::clear () noexcept
546 {
547 resize (0);
548 }
549 template <typename T, size_t BUF_SIZE>
550 inline InlineBuffer<T, BUF_SIZE>::operator T* () noexcept
551 {
552 AssertNotNull (fLiveData_);
553 return fLiveData_;
554 }
555 template <typename T, size_t BUF_SIZE>
556 inline InlineBuffer<T, BUF_SIZE>::operator const T* () const noexcept
557 {
558 AssertNotNull (fLiveData_);
559 return fLiveData_;
560 }
561 template <typename T, size_t BUF_SIZE>
562 inline void InlineBuffer<T, BUF_SIZE>::Invariant () const noexcept
563 {
564#if qStroika_Foundation_Debug_AssertionsChecked
565 Invariant_ ();
566#endif
567 }
568#if qStroika_Foundation_Debug_AssertionsChecked
569 template <typename T, size_t BUF_SIZE>
570 void InlineBuffer<T, BUF_SIZE>::Invariant_ () const noexcept
571 {
572 Assert (capacity () >= size ());
573 ValidateGuards_ ();
574 }
575 template <typename T, size_t BUF_SIZE>
576 void InlineBuffer<T, BUF_SIZE>::ValidateGuards_ () const noexcept
577 {
578 Assert (::memcmp (kGuard1_, fGuard1_, sizeof (kGuard1_)) == 0);
579 Assert (::memcmp (kGuard2_, fGuard2_, sizeof (kGuard2_)) == 0);
580 }
581#endif
582 template <typename T, size_t BUF_SIZE>
583 constexpr T* InlineBuffer<T, BUF_SIZE>::BufferAsT_ () noexcept
584 {
585 return reinterpret_cast<T*> (&fInlinePreallocatedBuffer_[0]);
586 }
587 template <typename T, size_t BUF_SIZE>
588 constexpr const T* InlineBuffer<T, BUF_SIZE>::BufferAsT_ () const noexcept
589 {
590 return reinterpret_cast<const T*> (&fInlinePreallocatedBuffer_[0]);
591 }
592 template <typename T, size_t BUF_SIZE>
593 inline void InlineBuffer<T, BUF_SIZE>::DestroyElts_ (T* start, T* end) noexcept
594 {
595 if constexpr (not is_trivially_destructible_v<T>) {
596 for (auto i = start; i != end; ++i) {
597 destroy_at (i);
598 }
599 }
600 }
601 template <typename T, size_t BUF_SIZE>
602 inline byte* InlineBuffer<T, BUF_SIZE>::LiveDataAsAllocatedBytes_ () noexcept
603 {
604 Require (not UsingInlinePreallocatedBuffer_ ());
605 return reinterpret_cast<byte*> (fLiveData_);
606 }
607 template <typename T, size_t BUF_SIZE>
608 inline byte* InlineBuffer<T, BUF_SIZE>::Allocate_ (size_t bytes)
609 {
610 DISABLE_COMPILER_GCC_WARNING_START ("GCC diagnostic ignored \"-Wmaybe-uninitialized\"") // crazy warning from g++-11
611 void* p = ::malloc (bytes);
612#if qCompilerAndStdLib_release_bld_error_bad_obj_offset_Buggy
613 if (p == nullptr) {
614 throw bad_alloc{};
615 }
616#else
618#endif
619 DISABLE_COMPILER_GCC_WARNING_END ("GCC diagnostic ignored \"-Wmaybe-uninitialized\"") // crazy warning from g++-11
620 return reinterpret_cast<byte*> (p);
621 }
622 template <typename T, size_t BUF_SIZE>
623 inline void InlineBuffer<T, BUF_SIZE>::Deallocate_ (byte* bytes) noexcept
624 {
625 if (bytes != nullptr) [[likely]] {
626 ::free (bytes);
627 }
628 }
629 template <typename T, size_t BUF_SIZE>
630 inline byte* InlineBuffer<T, BUF_SIZE>::Reallocate_ (byte* bytes, size_t n)
631 requires (is_trivially_copyable_v<T>)
632 {
633 if (n == 0) {
634 Deallocate_ (bytes);
635 return nullptr;
636 }
637 else {
638 if (bytes == nullptr) {
639 return Allocate_ (n);
640 }
641 else {
642 byte* p = reinterpret_cast<byte*> (::realloc (bytes, n));
643#if qCompilerAndStdLib_release_bld_error_bad_obj_offset_Buggy
644 if (p == nullptr) {
645 throw bad_alloc{};
646 }
647#else
649#endif
650 return p;
651 }
652 }
653 }
654 template <typename T, size_t BUF_SIZE>
655 constexpr bool InlineBuffer<T, BUF_SIZE>::UsingInlinePreallocatedBuffer_ () const noexcept
656 {
657 return fLiveData_ == BufferAsT_ ();
658 }
659
660}
#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...