10#include "Stroika/Foundation/Containers/Support/ReserveTweaks.h"
15 template <equality_comparable_with<
nullptr_t> T>
19namespace Stroika::Foundation::Memory {
26 template <
typename T,
size_t BUF_SIZE>
28 : fLiveData_{BufferAsT_ ()}
30#if qStroika_Foundation_Debug_AssertionsChecked
31 Assert (UsingInlinePreallocatedBuffer_ ());
32 (void)::memcpy (fGuard1_, kGuard1_,
sizeof (kGuard1_));
33 (void)::memcpy (fGuard2_, kGuard2_,
sizeof (kGuard2_));
37 template <
typename T,
size_t BUF_SIZE>
44 template <
typename T,
size_t BUF_SIZE>
48 if constexpr (is_trivially_copyable_v<T>) {
52 Require (flag == UninitializedConstructorFlag::eUninitializedIfTrivial);
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>
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}));
66 auto sz =
static_cast<size_t> (ranges::distance (start, forward<ITERATOR_OF_T2> (end)));
68 if (not this->HasEnoughCapacity_ (sz)) [[unlikely]] {
71#if qCompilerAndStdLib_stdlib_ranges_pretty_broken_Buggy || qCompilerAndStdLib_stdlib_ranges_ComputeDiffSignularToADeref_Buggy
72 uninitialized_copy (start, ITERATOR_OF_T (end), this->begin ());
74 ranges::uninitialized_copy (start, forward<ITERATOR_OF_T2> (end), this->begin (), this->begin () + sz);
79 template <
typename T,
size_t BUF_SIZE>
80 template <ISpanOfT<T> SPAN_T>
84 if (not this->HasEnoughCapacity_ (copyFrom.size ())) [[unlikely]] {
85 reserve (copyFrom.size (),
true);
87 uninitialized_copy (copyFrom.begin (), copyFrom.end (), this->begin ());
88 fSize_ = copyFrom.size ();
91 template <
typename T,
size_t BUF_SIZE>
92 template <
size_t FROM_BUF_SIZE>
94 : InlineBuffer{src.begin (), src.end ()}
97 template <
typename T,
size_t BUF_SIZE>
99 : InlineBuffer{src.begin (), src.end ()}
102 template <
typename T,
size_t BUF_SIZE>
106#if qStroika_Foundation_Debug_AssertionsChecked
107 size_t origSize = src.size ();
108 size_t origCapacity = src.capacity ();
110 if (src.UsingInlinePreallocatedBuffer_ ()) [[likely]] {
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 ());
120 this->fLiveData_ = src.fLiveData_;
121 this->fCapacityOfFreeStoreAllocation_ = src.fCapacityOfFreeStoreAllocation_;
122 src.fLiveData_ = src.BufferAsT_ ();
123 this->fSize_ = src.fSize_;
125 Ensure (src.fSize_ == 0);
126 Ensure (src.capacity () == BUF_SIZE);
128#if qStroika_Foundation_Debug_AssertionsChecked
129 Ensure (this->
size () == origSize);
130 Ensure (this->
capacity () == origCapacity);
133 template <
typename T,
size_t BUF_SIZE>
134 inline InlineBuffer<T, BUF_SIZE>::~InlineBuffer ()
137 DestroyElts_ (this->begin (), this->end ());
138 if (not UsingInlinePreallocatedBuffer_ ()) [[unlikely]] {
140 Deallocate_ (LiveDataAsAllocatedBytes_ ());
143 template <
typename T,
size_t BUF_SIZE>
144 InlineBuffer<T, BUF_SIZE>& InlineBuffer<T, BUF_SIZE>::operator= (
const InlineBuffer& rhs)
147 if (
this != &rhs) [[likely]] {
149 DestroyElts_ (this->begin (), this->end ());
151 if (not this->HasEnoughCapacity_ (rhs.size ())) [[unlikely]] {
152 reserve (rhs.size ());
154 uninitialized_copy (rhs.begin (), rhs.end (), this->begin ());
155 fSize_ = rhs.size ();
160 template <
typename T,
size_t BUF_SIZE>
164#if qStroika_Foundation_Debug_AssertionsChecked
165 size_t origFromSize = rhs.size ();
166 size_t origFromCapacity = rhs.capacity ();
169 DestroyElts_ (this->begin (), this->end ());
170 if (not this->UsingInlinePreallocatedBuffer_ ()) [[unlikely]] {
171 Deallocate_ (LiveDataAsAllocatedBytes_ ());
172 fLiveData_ = BufferAsT_ ();
175 if (rhs.UsingInlinePreallocatedBuffer_ ()) [[likely]] {
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 ());
185 this->fLiveData_ = rhs.fLiveData_;
186 rhs.fLiveData_ = rhs.BufferAsT_ ();
187 this->fSize_ = rhs.fSize_;
189 Ensure (rhs.fSize_ == 0);
190 Ensure (rhs.capacity () == BUF_SIZE);
192#if qStroika_Foundation_Debug_AssertionsChecked
193 Ensure (this->size () == origFromSize);
194 Ensure (this->capacity () == origFromCapacity);
198 template <
typename T,
size_t BUF_SIZE>
199 template <ISpanOfT<T> SPAN_T>
203 DestroyElts_ (this->begin (), this->end ());
205 if (not this->HasEnoughCapacity_ (copyFrom.size ())) [[unlikely]] {
206 reserve (copyFrom.size ());
208 uninitialized_copy (copyFrom.begin (), copyFrom.end (), this->begin ());
212 template <
typename T,
size_t BUF_SIZE>
215 if (nElements > size ()) {
218 Ensure (size () >= nElements);
220 template <
typename T,
size_t BUF_SIZE>
222 requires (is_trivially_copyable_v<T>)
224 static_assert (is_trivially_copyable_v<T>);
225 if (nElements > size ()) {
226 resize_uninitialized (nElements);
228 Ensure (size () >= nElements);
230 template <
typename T,
size_t BUF_SIZE>
233 if (nElements > fSize_) {
235 if (not this->HasEnoughCapacity_ (nElements)) [[unlikely]] {
236 reserve (nElements,
true);
238 Assert (nElements <= capacity ());
239 Assert (this->begin () + fSize_ == this->end ());
240 auto newEnd = this->begin () + nElements;
241 Assert (this->end () < newEnd);
243 uninitialized_fill (this->end (), newEnd, T{});
245 Assert (this->end () == newEnd);
247 else if (nElements < fSize_) {
249 DestroyElts_ (this->begin () + nElements, this->end ());
252 Assert (fSize_ == nElements);
253 Ensure (size () <= capacity ());
255 template <
typename T,
size_t BUF_SIZE>
257 requires (is_trivially_copyable_v<T> and is_trivially_destructible_v<T>)
259 if (not this->HasEnoughCapacity_ (nElements)) [[unlikely]] {
263 Assert (fSize_ == nElements);
264 Ensure (size () <= capacity ());
266 template <
typename T,
size_t BUF_SIZE>
269 Require (nElements <= fSize_);
270 if (nElements != fSize_) {
271 DestroyElts_ (this->begin () + nElements, this->end ());
274 Assert (fSize_ == nElements);
275 Ensure (size () <= capacity ());
277 template <
typename T,
size_t BUF_SIZE>
278 inline typename InlineBuffer<T, BUF_SIZE>::iterator InlineBuffer<T, BUF_SIZE>::begin () noexcept
282 template <
typename T,
size_t BUF_SIZE>
285 return fLiveData_ + fSize_;
287 template <
typename T,
size_t BUF_SIZE>
292 template <
typename T,
size_t BUF_SIZE>
295 return fLiveData_ + fSize_;
297 template <
typename T,
size_t BUF_SIZE>
302 template <
typename T,
size_t BUF_SIZE>
307 template <
typename T,
size_t BUF_SIZE>
311 if (UsingInlinePreallocatedBuffer_ ()) [[likely]] {
316 DISABLE_COMPILER_GCC_WARNING_START (
"GCC diagnostic ignored \"-Wmaybe-uninitialized\"");
317 return fCapacityOfFreeStoreAllocation_;
318 DISABLE_COMPILER_GCC_WARNING_END (
"GCC diagnostic ignored \"-Wmaybe-uninitialized\"");
321 template <
typename T,
size_t BUF_SIZE>
324 Require (newCapacity >= size ());
325 size_t useNewCapacity = newCapacity;
326 size_t oldCapacity = capacity ();
327 if (atLeast) [[likely]] {
328 if (useNewCapacity <= oldCapacity) [[likely]] {
332 if (useNewCapacity < BUF_SIZE) [[likely]] {
333 useNewCapacity = BUF_SIZE;
336 useNewCapacity = Foundation::Containers::Support::ReserveTweaks::GetScaledUpCapacity (useNewCapacity,
sizeof (T),
337 Math::AtLeast<size_t> (BUF_SIZE, 1));
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>) {
348 if (not oldInPlaceBuffer and not newInPlaceBuffer) {
350 fLiveData_ =
reinterpret_cast<T*
> (Reallocate_ (LiveDataAsAllocatedBytes_ (), SizeInBytes_ (useNewCapacity)));
351 fCapacityOfFreeStoreAllocation_ = useNewCapacity;
353 else if (oldInPlaceBuffer and newInPlaceBuffer) {
356 else if (oldInPlaceBuffer and not newInPlaceBuffer) {
358 byte* newPtr = Allocate_ (SizeInBytes_ (useNewCapacity));
360 Assert (this->begin () !=
reinterpret_cast<T*
> (newPtr));
361 Assert (
static_cast<size_t> (this->end () - this->begin ()) <= useNewCapacity);
362 uninitialized_copy (this->begin (), this->end (),
reinterpret_cast<T*
> (newPtr));
363 fLiveData_ =
reinterpret_cast<T*
> (newPtr);
364 if (not newInPlaceBuffer) {
365 fCapacityOfFreeStoreAllocation_ = useNewCapacity;
368 else if (not oldInPlaceBuffer and newInPlaceBuffer) {
370 byte* newPtr = std::begin (fInlinePreallocatedBuffer_);
372 Assert (this->begin () !=
reinterpret_cast<T*
> (newPtr));
373 Assert (
static_cast<size_t> (this->end () - this->begin ()) <= useNewCapacity);
374 uninitialized_copy (this->begin (), this->end (),
reinterpret_cast<T*
> (newPtr));
375 Deallocate_ (LiveDataAsAllocatedBytes_ ());
376 fLiveData_ =
reinterpret_cast<T*
> (newPtr);
381 if (oldInPlaceBuffer != newInPlaceBuffer or (not newInPlaceBuffer)) {
382 byte* newPtr = newInPlaceBuffer ? std::begin (fInlinePreallocatedBuffer_) : Allocate_ (SizeInBytes_ (useNewCapacity));
385 Assert (this->begin () !=
reinterpret_cast<T*
> (newPtr));
386 Assert (
static_cast<size_t> (this->end () - this->begin ()) <= useNewCapacity);
387 uninitialized_copy (this->begin (), this->end (),
reinterpret_cast<T*
> (newPtr));
390 DestroyElts_ (this->begin (), this->end ());
393 if (not oldInPlaceBuffer) {
394 Assert (not UsingInlinePreallocatedBuffer_ ());
395 Deallocate_ (LiveDataAsAllocatedBytes_ ());
398 fLiveData_ =
reinterpret_cast<T*
> (newPtr);
399 if (not newInPlaceBuffer) {
400 fCapacityOfFreeStoreAllocation_ = useNewCapacity;
405 Ensure ((useNewCapacity <= BUF_SIZE and capacity () == BUF_SIZE) or (useNewCapacity > BUF_SIZE and useNewCapacity == capacity ()));
408 template <
typename T,
size_t BUF_SIZE>
411 Ensure (fSize_ <= capacity ());
414 template <
typename T,
size_t BUF_SIZE>
417 Ensure (fSize_ <= capacity ());
420 template <
typename T,
size_t BUF_SIZE>
425 template <
typename T,
size_t BUF_SIZE>
428 Require (i < fSize_);
429 return *(fLiveData_ + i);
431 template <
typename T,
size_t BUF_SIZE>
434 Require (i < fSize_);
435 return *(fLiveData_ + i);
437 template <
typename T,
size_t BUF_SIZE>
442 template <
typename T,
size_t BUF_SIZE>
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)
452 size_t n2Add = copyFrom.size ();
453 size_t newS = s + n2Add;
454 if (not this->HasEnoughCapacity_ (newS)) [[unlikely]] {
457 Assert (this->HasEnoughCapacity_ (newS));
458 auto b = this->begin ();
463 if constexpr (is_trivially_copyable_v<T>) {
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);
470 ranges::uninitialized_copy (copyFrom, span{atPtr, n2Add});
478#if qCompilerAndStdLib_stdlib_ranges_pretty_broken_Buggy
479 uninitialized_copy (copyFrom.begin (), copyFrom.end (), b + s);
481 ranges::uninitialized_copy (copyFrom, span{b + s, n2Add});
483 rotate (atPtr, b + s, b + newS);
487 template <
typename T,
size_t BUF_SIZE>
490 Insert (i - begin (), span{from, to});
492 template <
typename T,
size_t BUF_SIZE>
497 if (not this->HasEnoughCapacity_ (newS)) [[unlikely]] {
500 if constexpr (is_trivially_copyable_v<T>) {
504 uninitialized_copy (&e, &e + 1, this->begin () + s);
509 template <
typename T,
size_t BUF_SIZE>
510 template <ISpanOfT<T> SPAN_T>
514 size_t newS = s + copyFrom.size ();
515 if (not this->HasEnoughCapacity_ (newS)) [[unlikely]] {
518 Assert (this->HasEnoughCapacity_ (newS));
519 if constexpr (is_trivially_copyable_v<T>) {
520 CopySpanData (copyFrom, span{this->begin () + s, copyFrom.size ()});
523 uninitialized_copy (copyFrom.begin (), copyFrom.end (), this->begin () + s);
527 template <
typename T,
size_t BUF_SIZE>
528 template <ISpan SPAN_T>
532 size_t newS = s + copyFrom.size ();
533 if (not this->HasEnoughCapacity_ (newS)) [[unlikely]] {
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 ()});
542 uninitialized_copy (copyFrom.begin (), copyFrom.end (), outPtr);
546 template <
typename T,
size_t BUF_SIZE>
547 inline void InlineBuffer<T, BUF_SIZE>::clear () noexcept
551 template <
typename T,
size_t BUF_SIZE>
552 inline InlineBuffer<T, BUF_SIZE>::operator T* ()
noexcept
557 template <
typename T,
size_t BUF_SIZE>
563 template <
typename T,
size_t BUF_SIZE>
566#if qStroika_Foundation_Debug_AssertionsChecked
570#if qStroika_Foundation_Debug_AssertionsChecked
571 template <
typename T,
size_t BUF_SIZE>
572 void InlineBuffer<T, BUF_SIZE>::Invariant_ () const noexcept
574 Assert (capacity () >= size ());
577 template <
typename T,
size_t BUF_SIZE>
578 void InlineBuffer<T, BUF_SIZE>::ValidateGuards_ () const noexcept
580 Assert (::memcmp (kGuard1_, fGuard1_,
sizeof (kGuard1_)) == 0);
581 Assert (::memcmp (kGuard2_, fGuard2_,
sizeof (kGuard2_)) == 0);
584 template <
typename T,
size_t BUF_SIZE>
585 constexpr T* InlineBuffer<T, BUF_SIZE>::BufferAsT_ () noexcept
587 return reinterpret_cast<T*
> (&fInlinePreallocatedBuffer_[0]);
589 template <
typename T,
size_t BUF_SIZE>
590 constexpr const T* InlineBuffer<T, BUF_SIZE>::BufferAsT_ () const noexcept
592 return reinterpret_cast<const T*
> (&fInlinePreallocatedBuffer_[0]);
594 template <
typename T,
size_t BUF_SIZE>
595 inline void InlineBuffer<T, BUF_SIZE>::DestroyElts_ (T* start, T* end)
noexcept
597 if constexpr (not is_trivially_destructible_v<T>) {
598 for (
auto i = start; i != end; ++i) {
603 template <
typename T,
size_t BUF_SIZE>
604 inline byte* InlineBuffer<T, BUF_SIZE>::LiveDataAsAllocatedBytes_ () noexcept
606 Require (not UsingInlinePreallocatedBuffer_ ());
607 return reinterpret_cast<byte*
> (fLiveData_);
609 template <
typename T,
size_t BUF_SIZE>
610 inline byte* InlineBuffer<T, BUF_SIZE>::Allocate_ (
size_t bytes)
612 DISABLE_COMPILER_GCC_WARNING_START (
"GCC diagnostic ignored \"-Wmaybe-uninitialized\"")
613 void* p = ::malloc (bytes);
614#if qCompilerAndStdLib_release_bld_error_bad_obj_offset_Buggy
621 DISABLE_COMPILER_GCC_WARNING_END (
"GCC diagnostic ignored \"-Wmaybe-uninitialized\"")
622 return reinterpret_cast<
byte*> (p);
624 template <typename T,
size_t BUF_SIZE>
625 inline
void InlineBuffer<T, BUF_SIZE>::Deallocate_ (
byte* bytes) noexcept
627 if (bytes !=
nullptr) [[likely]] {
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>)
640 if (bytes ==
nullptr) {
641 return Allocate_ (n);
644 byte* p =
reinterpret_cast<byte*
> (::realloc (bytes, n));
645#if qCompilerAndStdLib_release_bld_error_bad_obj_offset_Buggy
656 template <
typename T,
size_t BUF_SIZE>
657 constexpr bool InlineBuffer<T, BUF_SIZE>::UsingInlinePreallocatedBuffer_ () const noexcept
659 return fLiveData_ == BufferAsT_ ();
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 ©From)
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.
void ThrowIfNull(const Private_::ConstVoidStar &p, const HRESULT &hr)
Template specialization for ThrowIfNull (), for thing being thrown HRESULT - really throw HRESULTErro...