NDEVR
API Documentation
MemoryManager.h
1/*--------------------------------------------------------------------------------------------
2Copyright (c) 2019, NDEVR LLC
3tyler.parke@ndevr.org
4 __ __ ____ _____ __ __ _______
5 | \ | | | __ \ | ___|\ \ / / | __ \
6 | \ | | | | \ \ | |___ \ \ / / | |__) |
7 | . \| | | |__/ / | |___ \ V / | _ /
8 | |\ |_|_____/__|_____|___\_/____| | \ \
9 |__| \__________________________________| \__\
10
11Subject to the terms of the Enterprise+ Agreement, NDEVR hereby grants
12Licensee a limited, non-exclusive, non-transferable, royalty-free license
13(without the right to sublicense) to use the API solely for the purpose of
14Licensee's internal development efforts to develop applications for which
15the API was provided.
16
17The above copyright notice and this permission notice shall be included in all
18copies or substantial portions of the Software.
19
20THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
21INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
22PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
23FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
24OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
25DEALINGS IN THE SOFTWARE.
26
27Library: Base
28File: MemoryManager
29Included in API: True
30Author(s): Tyler Parke
31 *-----------------------------------------------------------------------------------------**/
32#pragma once
33#include <NDEVR/BaseValues.h>
34#include <NDEVR/LibAssert.h>
35#include <algorithm>
36#include <cstring>
37#ifdef __clang__
38 #pragma clang diagnostic push
39 #pragma clang diagnostic ignored "-Wdynamic-class-memaccess"
40#endif
41#ifdef _WIN32
42 #define NDEVR_HAS_ALIGNED_ALLOC 1
43 #include <memory.h>
44 #include <cstddef>
53 inline void* ndevr_aligned_realloc(void* old_ptr,
54 size_t new_size,
55 size_t old_size,
56 size_t alignment)
57 {
58 UNUSED(old_size);
59 return _aligned_realloc(old_ptr, new_size, alignment);
60 }
67 inline auto ndevr_aligned_malloc(
68 size_t new_size,
69 size_t alignment)
70 {
71 return _aligned_malloc(new_size, alignment);
72 }
77 inline auto ndevr_aligned_free(void* ptr)
78 {
79 return _aligned_free(ptr);
80 }
81#elif defined(ANDROID)
82 #define NDEVR_HAS_ALIGNED_ALLOC 0
83#elif defined(__GNUC__) || defined(__clang__)
84 #define NDEVR_HAS_ALIGNED_ALLOC 1
85 #include <cstdlib>
86 #include <stdlib.h>
87 #define ndevr_aligned_free ::free
94 inline void* ndevr_aligned_malloc(
95 size_t new_size,
96 size_t alignment)
97 {
98 // aligned_alloc requires size to be a multiple of alignment (C11 spec)
99 size_t fixed_size = (new_size + alignment - 1) / alignment * alignment;
100 return ::aligned_alloc(alignment, fixed_size);
101 }
110 inline void* ndevr_aligned_realloc(void* old_ptr,
111 size_t new_size,
112 size_t old_size,
113 size_t alignment)
114 {
115 // std::aligned_alloc requires size to be a multiple of alignment
116 size_t fixed_new_size = (new_size + alignment - 1) / alignment * alignment;
117
118 void* new_ptr = ::aligned_alloc(alignment, fixed_new_size);
119 if (old_ptr)
120 {
121 memcpy(new_ptr, old_ptr, getMin(old_size, new_size));
122 free(old_ptr);
123 }
124
125 return new_ptr;
126 }
127#else
128 #define NDEVR_HAS_ALIGNED_ALLOC 0
129#endif
130namespace NDEVR
131{
132#if NDEVR_HAS_ALIGNED_ALLOC
133#if defined(__cpp_lib_assume_aligned) && __cpp_lib_assume_aligned
134 #define NDEVR_ASSUME_ALIGNED(ALIGN, PTR) \
135 (__builtin_assume_aligned(PTR, ALIGN))
136 #elif defined(_MSC_VER)
137 #define NDEVR_ASSUME_ALIGNED(ALIGN, PTR) \
138 ((__assume((reinterpret_cast<std::uintptr_t>(PTR) & ((ALIGN) - 1)) == 0), (PTR)))
139 #elif defined(__GNUC__) || defined(__clang__)
140 #define NDEVR_ASSUME_ALIGNED(ALIGN, PTR) \
141 (__builtin_assume_aligned(PTR, ALIGN))
142 #else
143 #define NDEVR_ASSUME_ALIGNED(ALIGN, PTR) \
144 (PTR)
145 #endif
146#else
147 #define NDEVR_ASSUME_ALIGNED(ALIGN, PTR) \
148 (PTR)
149#endif
154 template<size_t t_size>
155 static constexpr size_t DetermineAlignment() noexcept
156 {
157 #if NDEVR_HAS_ALIGNED_ALLOC
158 if constexpr (t_size >= 64)
159 return 64;
160 else if constexpr (t_size >= 32)
161 return 32;
162 else if constexpr (t_size >= 16)
163 return 16;
164 else if constexpr (t_size >= 8)
165 return 8;
166 else
167 #endif
168 return 0;
169 }
170
173 template<class t_type>
175 {
180 static constexpr size_t Size() noexcept
181 {
183 }
184 };
185
189 template<class t_index_type, bool is_primitive>
195 template<class t_index_type>
196 class ObjectAllocatorT<t_index_type, true>
197 {
198 public:
205 template<class t_buffer_type, class t_type>
206 static void Allocate(t_buffer_type&, t_index_type, t_index_type) noexcept
207 {}
208
214 template<class t_buffer_type, class t_type>
215 static constexpr void Deallocate(t_buffer_type&, t_index_type, t_index_type) noexcept
216 {}
217
224 template<class t_buffer_type, class t_type>
225 static void AllocateElement(t_buffer_type& buff, t_index_type start, t_index_type size, const t_type& object) noexcept
226 {
227 buff.setAll(object, start, size);
228 }
229
235 template<class t_buffer_type, class t_type>
236 static void AllocateElement(t_buffer_type& buff, t_index_type index, const t_type& object) noexcept
237 {
238 buff.get(index) = object;
239 }
240
246 template<class t_buffer_type, class t_type>
247 static void AllocateElement(t_buffer_type& buff, t_index_type index, t_type&& object) noexcept
248 {
249 buff.get(index) = std::forward<t_type>(object);
250 }
251 };
252
257 template<class t_index_type>
258 class ObjectAllocatorT<t_index_type, false>
259 {
260 public:
267 template<class t_buffer_type, class t_type>
268 static constexpr void Allocate(t_buffer_type& buff, t_index_type start, t_index_type size) noexcept
269 {
270 if constexpr (!std::is_trivially_default_constructible_v<t_type>)
271 {
272 for (t_index_type i = 0; i < size; i++)
273 new (&buff.get(i + start)) t_type();
274 }
275 }
276
282 template<class t_buffer_type, class t_type>
283 static constexpr void Deallocate(t_buffer_type& buff, t_index_type start, t_index_type size) noexcept
284 {
285 if constexpr (!std::is_trivially_destructible<t_type>::value)
286 {
287 t_index_type end = start + size;
288 for (t_index_type i = start; i < end; i++)
289 (&buff.get(i))->~t_type();
290 }
291 }
292
299 template<class t_buffer_type, class t_type>
300 static constexpr void AllocateElement(t_buffer_type& buff, t_index_type start, t_index_type size, const t_type& object) noexcept
301 {
302 t_index_type end = start + size;
303 for (t_index_type i = start; i < end; i++)
304 new (&buff.get(i)) t_type(object);
305 }
306
312 template<class t_buffer_type, class t_type>
313 static constexpr void AllocateElement(t_buffer_type& buff, t_index_type index, const t_type& object) noexcept
314 {
315 new (&buff.get(index)) t_type(object);
316 }
317
323 template<class t_buffer_type, class t_type>
324 static constexpr void AllocateElement(t_buffer_type& buff, t_index_type index, t_type&& object) noexcept
325 {
326 new (&buff.get(index)) t_type(std::move(object));
327 }
328 };
329
333 template<class t_type, bool t_is_primitive, class t_index_type>
335 {
336 public:
343 template<class t_buffer_type>
344 static constexpr void Allocate(t_buffer_type& buffer, t_index_type start, t_index_type size) noexcept
345 {
347 }
348
354 template<class t_buffer_type>
355 static constexpr void Deallocate(t_buffer_type& buffer, t_index_type start, t_index_type size) noexcept
356 {
358 }
359
366 template<class t_buffer_type>
367 static constexpr void AllocateElement(t_buffer_type& buffer, t_index_type start, t_index_type size, const t_type& object) noexcept
368 {
370 }
371
378 template<class t_buffer_type>
379 static constexpr void AllocateElement(t_buffer_type& buffer, t_index_type start, t_index_type size, t_type& object) noexcept
380 {
382 }
383
389 template<class t_buffer_type>
390 static constexpr void AllocateElement(t_buffer_type& buffer, t_index_type index, const t_type& object) noexcept
391 {
393 }
394
400 template<class t_buffer_type>
401 static constexpr void AllocateElement(t_buffer_type& buffer, t_index_type index, t_type&& object) noexcept
402 {
404 }
405
409 static constexpr bool isPrimitive() noexcept
410 {
411 return t_is_primitive;
412 }
413 };
414 // Memory Allocation tuned per platform
415 #if defined(_MSC_VER) && !defined(__clang__)
416 #define NDEVR_HAS_UMUL128 1
417 #else
418 #define NDEVR_HAS_UMUL128 0
419 #endif
420 #if defined(__EMSCRIPTEN__)
421 #define NDEVR_ALIGN_TINY 64ULL//64
422 #define NDEVR_ALIGN_MED 65536ULL//64KB
423 #define NDEVR_ALIGN_LARGE 1048576ULL//1MB
424 #elif defined(_WIN32)
425 #define NDEVR_ALIGN_TINY 64ULL//64
426 #define NDEVR_ALIGN_MED 4096ULL//4KB
427 #define NDEVR_ALIGN_LARGE 65536ull//64KB
428 #define NDEVR_ALIGN_HUGE 2097152ull//2MB
429 #elif defined(__ANDROID__)
430 #define NDEVR_ALIGN_TINY 64ULL//64
431 #define NDEVR_ALIGN_MED 4096ULL//4KB
432 #define NDEVR_ALIGN_LARGE 1048576ULL//1MB
433 #else
434 #define NDEVR_ALIGN_TINY 64ULL//64
435 #define NDEVR_ALIGN_MED 4096ULL//4KB
436 #define NDEVR_ALIGN_LARGE 1048576ULL//1MB
437 #endif
438#if __cpp_lib_bitops <= 201907L
443 template<size_t t_value>
444 struct ShiftCount { static constexpr int value = ((t_value & 1) != 0) ? 0 : (1 + ShiftCount<(t_value >> 1)>::value); };
446 template<> struct ShiftCount<0> { static constexpr int value = 0; };
447#endif
452 template<uint08 D>
453 struct MagicDiv
454 {
455 #if NDEVR_HAS_UMUL128
456 static constexpr uint08 m = (uint08(~uint08(0)) / D) + 1;
457 #else
458 static constexpr unsigned __int128 high_bit = (unsigned __int128)1 << 64;
459 static constexpr uint08 m = uint08((high_bit + (D - 1)) / D);
460 #endif
461 };
462
470 template<class t_index_type, size_t t_elem_size>
471 static inline t_index_type NextCapacityBytes(t_index_type cap_elems, t_index_type need_elems) noexcept
472 {
473 constexpr size_t min_step = size_t(8) * t_elem_size;
474
475 size_t old_b = size_t(cap_elems) * t_elem_size;
476 size_t need_b = size_t(need_elems) * t_elem_size;
477
478 size_t base = (old_b < size_t(256)) ? (old_b << 1) : (old_b + (old_b >> 1));
479 size_t grow_b = (base > old_b + min_step) ? base : (old_b + min_step);
480 if (grow_b < need_b)
481 grow_b = need_b;
482
483 size_t align_b;
484 #ifdef NDEVR_ALIGN_HUGE
485 if (grow_b >= NDEVR_ALIGN_HUGE || t_elem_size >= NDEVR_ALIGN_HUGE)
486 align_b = NDEVR_ALIGN_HUGE;
487 else
488 #endif
489 if (grow_b >= NDEVR_ALIGN_LARGE || t_elem_size >= NDEVR_ALIGN_LARGE)
490 align_b = NDEVR_ALIGN_LARGE;
491 else if (grow_b >= NDEVR_ALIGN_MED || t_elem_size >= NDEVR_ALIGN_MED)
492 align_b = NDEVR_ALIGN_MED;
493 else
494 align_b = NDEVR_ALIGN_TINY;
495 if constexpr ((t_elem_size & (t_elem_size - size_t(1))) == 0)
496 {
497 #if __cpp_lib_bitops > 201907L
498 constexpr int shift = std::countr_zero((size_t)t_elem_size);
499 #else
500 constexpr int shift = ShiftCount<t_elem_size>::value;
501 #endif
502 size_t rounded = (grow_b + (align_b - size_t(1))) & ~(align_b - size_t(1));
503 size_t elems = rounded >> shift;
504 return cast<t_index_type>(elems);
505 }
506 else
507 {
508 size_t rounded = (grow_b + (align_b - size_t(1))) & ~(align_b - size_t(1));
509 #if NDEVR_HAS_UMUL128
510 size_t hi;
511 (void)_umul128(rounded, MagicDiv<t_elem_size>::m, &hi);
512 return cast<t_index_type>(hi);
513 #else
514 unsigned __int128 prod = (unsigned __int128)(rounded) * (unsigned __int128)MagicDiv<t_elem_size>::m;
515 size_t elems = size_t(prod >> 64);
516 return cast<t_index_type>(elems);
517 #endif
518 }
519 }
520
526 template<class t_type, size_t t_aligned_size, bool t_is_primitive, class t_index_type = uint04, bool t_null_term = false, bool t_allow_reference = false>
528 {
529 public:
530 using index_type = t_index_type;
532
537 [[nodiscard]] static constexpr bool IsNullTerm() noexcept { return t_null_term; }
538
542 constexpr explicit BufferAllocator() noexcept
544 , m_filled_size(0)
546 {}
547
553 , m_filled_size(0)
555 {
556 std::swap(allocator.m_buffer, m_buffer);
557 std::swap(allocator.m_filled_size, m_filled_size);
558 std::swap(allocator.m_allocated_size, m_allocated_size);
559 }
560
564 {
565 if (capacity() != 0)
566 {
567 #if NDEVR_HAS_ALIGNED_ALLOC
568 if constexpr (t_aligned_size > 0)
569 ndevr_aligned_free(ptr());
570 else
571 #endif
572 free(ptr());
573 }
574 }
575
579 template<bool t_managed>
580 constexpr void createSpace(t_index_type size) noexcept
581 {
582 t_index_type new_size = m_filled_size + size;
584 m_filled_size = new_size;
586 }
587
592 template<bool t_managed>
593 constexpr void createSpace(t_index_type location, t_index_type size) noexcept
594 {
596 memmove(begin() + location + size, begin() + location, sizeof(t_type) * (m_filled_size - (size + location)));
597 }
598
601 void addLast() noexcept
602 {
604 }
605
608 void removeLast() noexcept
609 {
612 }
613
617 void removeLast(t_index_type count) noexcept
618 {
621 }
622
627 [[nodiscard]] constexpr t_index_type count(const t_type& value) const noexcept
628 {
629 t_index_type count = 0;
630 for (t_index_type i = 0; i < m_filled_size; i++)
631 {
632 if (ptr()[i] == value)
633 ++count;
634 }
635 return count;
636 }
637
644 template<class t_comparable_type>
645 [[nodiscard]] constexpr bool contains(const t_comparable_type& value, t_index_type start, t_index_type size) const noexcept
646 {
647 for (t_index_type i = start; i < start + size; ++i)
648 {
649 if (ptr()[i] == value)
650 return true;
651 }
652 return false;
653 }
654
657 constexpr void addIndex() noexcept
658 {
660 }
661
665 constexpr void addIndex(t_index_type location) noexcept
666 {
667 lib_assert(location <= m_filled_size, "Out of bounds add");
669 memmove(ptr() + location + 1, ptr() + location, sizeof(t_type) * (m_filled_size - location - 1));
670
671 }
672
677 void removeIndex(t_index_type location) noexcept
678 {
679 lib_assert(location <= m_filled_size, "Out of bounds remove");
680 memmove(ptr() + location, ptr() + location + 1, (m_filled_size - location - 1) * sizeof(t_type));
683 }
684
689 [[nodiscard]] sint04 compare(const BufferAllocator& allocator) const noexcept
690 {
691 return cast<sint04>(memcmp(ptr(), allocator.ptr(), sizeof(t_type) * getMin(allocator.filledSize(), m_filled_size)));
692 }
693
700 [[nodiscard]] sint04 compare(const BufferAllocator& allocator, t_index_type start, t_index_type end) const noexcept
701 {
702 return cast<sint04>(memcmp(&(ptr()[start]), &(allocator.ptr()[start]), sizeof(t_type) * getMin(allocator.filledSize(), m_filled_size, end)));
703 }
704
709 void swap(t_index_type index_a, t_index_type index_b) noexcept
710 {
711 std::swap(ptr()[index_a], ptr()[index_b]);
712 }
713
716 void clear() noexcept
717 {
718 m_filled_size = 0;
720 }
721
725 void setSize(t_index_type size) noexcept
726 {
728 m_filled_size = size;
730 }
731
736 [[nodiscard]] t_type& get(t_index_type index) noexcept
737 {
738 return ptr()[index];
739 }
740
745 [[nodiscard]] const t_type& get(t_index_type index) const noexcept
746 {
747 return ptr()[index];
748 }
749
754 [[nodiscard]] constexpr decltype(auto) ptr() noexcept
755 {
756 if constexpr (t_aligned_size > 0)
757 return (t_type*)NDEVR_ASSUME_ALIGNED(t_aligned_size, m_buffer);
758 else
759 return m_buffer;
760 }
761
766 [[nodiscard]] constexpr decltype(auto) ptr() const noexcept
767 {
768 if constexpr (t_aligned_size > 0)
769 return (const t_type*)NDEVR_ASSUME_ALIGNED(t_aligned_size, m_buffer);
770 else
771 return m_buffer;
772 }
773
778 [[nodiscard]] constexpr decltype(auto) begin() noexcept
779 {
780 return ptr();
781 }
782
787 [[nodiscard]] constexpr decltype(auto) begin() const noexcept
788 {
789 return ptr();
790 }
791
797 [[nodiscard]] constexpr decltype(auto) begin(t_index_type index) const noexcept
798 {
799 return &(ptr()[index]);
800 }
801
806 [[nodiscard]] constexpr decltype(auto) begin(t_index_type index) noexcept
807 {
808 return &(ptr()[index]);
809 }
810
815 [[nodiscard]] constexpr decltype(auto) end() noexcept
816 {
817 return ptr() ? &(ptr()[m_filled_size]) : ptr();
818 }
819
823 [[nodiscard]] constexpr decltype(auto) end() const noexcept
824 {
825 return ptr() ? &(ptr()[m_filled_size]) : ptr();
826 }
827
832 [[nodiscard]] constexpr decltype(auto) end(t_index_type index) noexcept
833 {
834 return &(get(m_filled_size - index));
835 }
836
840 [[nodiscard]] constexpr t_index_type capacity() const noexcept
841 {
842 return m_allocated_size;
843 }
844
848 [[nodiscard]] constexpr t_index_type filledSize() const noexcept
849 {
850 return m_filled_size;
851 }
852
856 [[nodiscard]] constexpr bool ownsMemory() const
857 {
858 return m_allocated_size != 0;
859 }
860
864 [[nodiscard]] constexpr size_t memSize() const noexcept
865 {
866 return m_filled_size * sizeof(t_type);
867 }
868
873 inline void resizeSpace(t_index_type new_size) noexcept
874 {
875 if (new_size == 0)
876 {
877 if (capacity() != 0)
878 {
879 #if NDEVR_HAS_ALIGNED_ALLOC
880 if constexpr(t_aligned_size > 0)
881 ndevr_aligned_free(ptr());
882 else
883 #endif
884 free(ptr());
885 }
886 m_buffer = emptyPtr<t_null_term>();//this will provide the needed null termination without a malloc
887 }
888 else
889 {
890 if (capacity() == 0)
891 {
892 if constexpr (t_allow_reference)
893 {
894 if (m_filled_size > 0)//we are referencing another memory location, so be sure to move value
895 {
896 auto referenced_data = ptr();
897 #if NDEVR_HAS_ALIGNED_ALLOC
898 if constexpr (t_aligned_size > 0)
899 m_buffer = (t_type*)ndevr_aligned_malloc(sizeof(t_type) * (t_null_term ? new_size + 1 : new_size), t_aligned_size);
900 else
901 #endif
902 m_buffer = (t_type*)malloc(sizeof(t_type) * (t_null_term ? new_size + 1 : new_size));
903 setAll<t_is_primitive>(referenced_data, 0, m_filled_size);
904 }
905 else
906 {
907 #if NDEVR_HAS_ALIGNED_ALLOC
908 if constexpr (t_aligned_size > 0)
909 m_buffer = (t_type*)ndevr_aligned_malloc(sizeof(t_type) * (t_null_term ? new_size + 1 : new_size), t_aligned_size);
910 else
911 #endif
912 m_buffer = (t_type*)malloc(sizeof(t_type) * (t_null_term ? new_size + 1 : new_size));
913 }
914 }
915 else
916 {
917 #if NDEVR_HAS_ALIGNED_ALLOC
918 if constexpr (t_aligned_size > 0)
919 m_buffer = (t_type*)ndevr_aligned_malloc(sizeof(t_type) * (t_null_term ? new_size + 1 : new_size), t_aligned_size);
920 else
921 #endif
922 m_buffer = (t_type*)malloc(sizeof(t_type) * (t_null_term ? new_size + 1 : new_size));
923 }
924 }
925 else
926 {
927 #if NDEVR_HAS_ALIGNED_ALLOC
928 if constexpr (t_aligned_size > 0)
929 m_buffer = (t_type*)ndevr_aligned_realloc(ptr()
930 , sizeof(t_type) * (t_null_term ? new_size + 1 : new_size)
931 , sizeof(t_type) * (t_null_term ? m_filled_size + 1 : m_filled_size), t_aligned_size);
932 else
933 #endif
934 m_buffer = (t_type*)realloc(ptr(), sizeof(t_type) * (t_null_term ? new_size + 1 : new_size));
935 }
936 if (ptr() == nullptr)
937 {
939 lib_assert(false, "Unable to allocate buffer: Perhaps out of system memory!");
940 }
941 }
942 m_allocated_size = new_size;
943 }
944
950 void setAll(const t_type& object, t_index_type offset, t_index_type size) noexcept
951 {
952 for (t_index_type i = offset; i < offset + size; i++)
953 ptr()[i] = object;
954 }
955
962 template<bool is_primitive>
963 void setAll(const t_type* src, t_index_type offset, t_index_type size) noexcept
964 {
965 if constexpr (is_primitive)
966 {
967 memmove(&ptr()[offset], src, size * sizeof(t_type));
968 }
969 else
970 {
971 for (t_index_type i = 0; i < size; i++)
972 {
973 ptr()[i + offset] = src[i];
974 }
975 }
976 }
977
984 template<bool t_is_other_primitive, class t_other_index_type, bool t_other_null_term>
985 void setAll(const BufferAllocator<t_type, t_aligned_size, t_is_other_primitive, t_other_index_type, t_other_null_term>& allocator, t_index_type offset, t_index_type other_offset, t_index_type size) noexcept
986 {
987 if constexpr (t_is_other_primitive)
988 {
989 memmove(&ptr()[offset], &(allocator.ptr()[other_offset]), size * sizeof(t_type));
990 }
991 else
992 {
993 for (t_index_type i = 0; i < size; i++)
994 ptr()[i + offset] = allocator.get(i + other_offset);
995 }
996 }
997
1004 template<class t_other_allocator>
1005 void setAllFromSource(const t_other_allocator& allocator, t_index_type offset, t_index_type other_offset, t_index_type size) noexcept
1006 {
1007 for (t_index_type i = 0; i < size; i++)
1008 {
1009 ptr()[i + offset] = allocator.get(i + other_offset);
1010 }
1011 }
1012
1016 inline void autoSetCapacity(t_index_type size) noexcept
1017 {
1019 lib_assert(nc >= size, "Bad allocation prediciton");
1020 lib_assert(nc < Constant<uint04>::Max, "Bad allocation prediciton");
1021 resizeSpace(nc);
1022 }
1023
1029 constexpr void reference(t_type* reference, t_index_type new_size)
1030 {
1031 resizeSpace(0);
1033 m_filled_size = new_size;
1034 }
1035
1041 template<bool t_auto_set_capacity>
1042 constexpr inline void allocationSizeCheck(t_index_type new_size) noexcept
1043 {
1044 if constexpr (t_auto_set_capacity)//We only 1, assume more to come shortly and allocate more space than required
1045 {
1046 if(new_size >= m_allocated_size)
1047 {
1048 autoSetCapacity(new_size);
1049 }
1050 }
1051 else //We added more than 1, increase buffer only once for compaction
1052 {
1053 if (new_size == m_allocated_size + 1)
1054 {
1055 autoSetCapacity(new_size);
1056 }
1057 else if (new_size > m_allocated_size)
1058 {
1059 resizeSpace(new_size);
1060 }
1061 }
1062 }
1063
1068 void removeAllIndex(t_index_type start, t_index_type end) noexcept
1069 {
1070 lib_assert(end <= m_filled_size, "Bad remove index");
1071 if (end == m_filled_size)
1072 {
1073 m_filled_size = start;
1074 }
1075 else
1076 {
1077 memmove(&ptr()[start], &ptr()[end], (m_filled_size - end) * sizeof(t_type));
1078 m_filled_size -= (end - start);
1079 }
1081 }
1082
1086 template<class t_range_buffer>
1087 void removeAllIndices(const t_range_buffer& ranges) noexcept
1088 {
1089 t_index_type offset = 0;
1090 for (t_index_type i = 0; i < ranges.size(); i++)
1091 {
1092 auto range = ranges[i];
1093 t_index_type start = range.first;
1094 t_index_type end = range.second;
1095 t_index_type next = i == ranges.size() - 1 ? m_filled_size : ranges[i + 1].start;
1096 memmove(&ptr()[start], &ptr()[end], (next - (end + offset)) * sizeof(t_type));
1097 offset += (end - start);
1098 }
1099 m_filled_size -= offset;
1101 }
1102
1105 template<bool t_is_null>
1106 inline void nullTerminatorCheck() noexcept
1107 {
1108 if constexpr (t_is_null)
1109 {
1110 if (capacity() != 0)
1111 memset(&(ptr()[m_filled_size]), 0, sizeof(t_type));//Ensure null termination
1112 }
1113 }
1114
1119 template<bool t_is_null>
1120 [[nodiscard]] decltype(auto) emptyPtr() const noexcept
1121 {
1122 if constexpr(t_is_null)
1123 {
1124 #if NDEVR_HAS_ALIGNED_ALLOC
1125 if constexpr (t_aligned_size > 0)
1126 {
1127 alignas(t_aligned_size) static constexpr t_type null_object = {};
1128 return const_cast<t_type*>(&null_object);
1129 }
1130 else
1131 #endif
1132 {
1133 static constexpr t_type null_object = {};
1134 return const_cast<t_type*>(&null_object);
1135 }
1136 }
1137 else
1138 {
1139 return nullptr;
1140 }
1141 }
1142 public:
1149 {
1150 std::swap(value.m_buffer, m_buffer);
1151 std::swap(value.m_filled_size, m_filled_size);
1152 std::swap(value.m_allocated_size, m_allocated_size);
1153 return *this;
1154 }
1155 public:
1157 t_type* m_buffer;
1158 t_index_type m_filled_size;
1159 t_index_type m_allocated_size;
1160 };
1161}
1162#ifdef __clang__
1163 #pragma clang diagnostic pop
1164#endif
constexpr BufferAllocator() noexcept
Default constructor.
void nullTerminatorCheck() noexcept
Writes a null terminator after the last filled element if null termination is enabled.
constexpr decltype(auto) ptr() noexcept
Returns a pointer to the underlying buffer, with optional alignment hint.
constexpr decltype(auto) begin() const noexcept
Returns a const pointer to the beginning of the buffer.
constexpr decltype(auto) end() const noexcept
Returns a const pointer to one past the last filled element.
void setAllFromSource(const t_other_allocator &allocator, t_index_type offset, t_index_type other_offset, t_index_type size) noexcept
Copies elements from a generic source allocator into this buffer using element-wise assignment.
static constexpr bool IsNullTerm() noexcept
Checks whether this allocator uses null termination.
constexpr t_index_type count(const t_type &value) const noexcept
Counts the number of occurrences of a value in the buffer.
void resizeSpace(t_index_type new_size) noexcept
Resizes the allocated memory to the specified number of elements.
sint04 compare(const BufferAllocator &allocator) const noexcept
Performs a byte-level comparison of this buffer with another.
void removeIndex(t_index_type location) noexcept
Removes an element at the specified location by shifting subsequent elements backward.
constexpr decltype(auto) end() noexcept
Returns a pointer to one past the last filled element.
constexpr decltype(auto) begin(t_index_type index) const noexcept
Returns a const pointer to the element at the given index.
void removeLast() noexcept
Removes the last element from the buffer by decrementing the filled size.
constexpr decltype(auto) begin(t_index_type index) noexcept
Returns a pointer to the element at the given index.
BufferAllocator & operator=(BufferAllocator &&value) noexcept
Move assignment operator.
constexpr t_index_type capacity() const noexcept
Returns the total allocated capacity of the buffer in elements.
t_index_type m_filled_size
The number of elements currently stored in the buffer.
t_type & get(t_index_type index) noexcept
Gets a mutable reference to the element at the given index.
constexpr void addIndex(t_index_type location) noexcept
Inserts one uninitialized element at the specified location, shifting subsequent elements.
void autoSetCapacity(t_index_type size) noexcept
Automatically sets the buffer capacity using the growth strategy defined by NextCapacityBytes.
void swap(t_index_type index_a, t_index_type index_b) noexcept
Swaps two elements in the buffer by index.
void setSize(t_index_type size) noexcept
Sets the filled size of the buffer, allocating more space if necessary.
t_type * m_buffer
The buffer that stores the actual data.
constexpr t_index_type filledSize() const noexcept
Returns the number of elements currently stored in the buffer.
t_index_type index_type
The index type used for element addressing.
~BufferAllocator() noexcept
Destructor.
void setAll(const BufferAllocator< t_type, t_aligned_size, t_is_other_primitive, t_other_index_type, t_other_null_term > &allocator, t_index_type offset, t_index_type other_offset, t_index_type size) noexcept
Copies elements from another BufferAllocator into this buffer.
decltype(auto) emptyPtr() const noexcept
Returns a pointer to a static empty object for use when the buffer has no allocation.
constexpr decltype(auto) begin() noexcept
Returns a pointer to the beginning of the buffer.
void removeAllIndex(t_index_type start, t_index_type end) noexcept
Removes a contiguous range of elements from the buffer.
void setAll(const t_type &object, t_index_type offset, t_index_type size) noexcept
Fills a range of elements in the buffer with a single value.
constexpr BufferAllocator(BufferAllocator &&allocator) noexcept
Move constructor.
constexpr decltype(auto) ptr() const noexcept
Returns a const pointer to the underlying buffer, with optional alignment hint.
void setAll(const t_type *src, t_index_type offset, t_index_type size) noexcept
Copies elements from a source array into this buffer.
constexpr void createSpace(t_index_type size) noexcept
Creates additional space at the end of the buffer, optionally auto-managing capacity.
constexpr void addIndex() noexcept
Appends one uninitialized element at the end of the buffer without managed growth.
ObjectAllocator< t_type, t_is_primitive, t_index_type > allocator
The object allocator type for constructing and destructing elements.
const t_type & get(t_index_type index) const noexcept
Gets a const reference to the element at the given index.
constexpr size_t memSize() const noexcept
Returns the total memory size of the filled portion of the buffer in bytes.
constexpr void allocationSizeCheck(t_index_type new_size) noexcept
Checks if the buffer needs to grow and allocates accordingly.
constexpr void reference(t_type *reference, t_index_type new_size)
Sets this allocator to reference external memory without owning it.
void clear() noexcept
Clears the buffer by resetting the filled size to zero without freeing memory.
void removeAllIndices(const t_range_buffer &ranges) noexcept
Removes multiple contiguous ranges of elements from the buffer in a single pass.
sint04 compare(const BufferAllocator &allocator, t_index_type start, t_index_type end) const noexcept
Performs a byte-level comparison of a sub-range of this buffer with another.
void removeLast(t_index_type count) noexcept
Removes a specified number of elements from the end of the buffer.
constexpr bool ownsMemory() const
Checks whether this allocator owns its memory (i.e., has allocated capacity).
t_index_type m_allocated_size
The total allocated capacity in elements.
void addLast() noexcept
Appends one uninitialized element to the end of the buffer without managed growth.
constexpr bool contains(const t_comparable_type &value, t_index_type start, t_index_type size) const noexcept
Checks whether the buffer contains a given value within the specified range.
constexpr void createSpace(t_index_type location, t_index_type size) noexcept
Creates additional space at a specific location, shifting existing elements forward.
constexpr decltype(auto) end(t_index_type index) noexcept
Returns a pointer to the element at the given offset from the end.
static constexpr void AllocateElement(t_buffer_type &buff, t_index_type index, const t_type &object) noexcept
Allocates a single non-primitive element using placement new copy construction.
static constexpr void AllocateElement(t_buffer_type &buff, t_index_type start, t_index_type size, const t_type &object) noexcept
Allocates a range of non-primitive elements using placement new copy construction.
static constexpr void AllocateElement(t_buffer_type &buff, t_index_type index, t_type &&object) noexcept
Allocates a single non-primitive element using placement new move construction.
static constexpr void Allocate(t_buffer_type &buff, t_index_type start, t_index_type size) noexcept
Allocates a range of non-primitive elements using placement new default construction.
static constexpr void Deallocate(t_buffer_type &buff, t_index_type start, t_index_type size) noexcept
Deallocates a range of non-primitive elements by explicitly calling their destructors.
static void AllocateElement(t_buffer_type &buff, t_index_type index, t_type &&object) noexcept
Allocates a single primitive element by move-assigning the given object.
static void AllocateElement(t_buffer_type &buff, t_index_type index, const t_type &object) noexcept
Allocates a single primitive element by copy-assigning the given object.
static constexpr void Deallocate(t_buffer_type &, t_index_type, t_index_type) noexcept
Deallocates space for primitive types.
static void AllocateElement(t_buffer_type &buff, t_index_type start, t_index_type size, const t_type &object) noexcept
Allocates a range of primitive elements by assigning a copy of the given object.
static void Allocate(t_buffer_type &, t_index_type, t_index_type) noexcept
Allocates space for primitive types.
Allows for specific, custom logic for allocating an object.
Allows for specific, custom logic for allocating an object.
static constexpr void Deallocate(t_buffer_type &buffer, t_index_type start, t_index_type size) noexcept
Deallocates a range of elements in the buffer, delegating to the appropriate primitive or non-primiti...
static constexpr void AllocateElement(t_buffer_type &buffer, t_index_type index, t_type &&object) noexcept
Allocates a single element by move-constructing from a given object.
static constexpr bool isPrimitive() noexcept
Checks whether this allocator handles primitive types.
static constexpr void AllocateElement(t_buffer_type &buffer, t_index_type start, t_index_type size, t_type &object) noexcept
Allocates a range of elements by constructing from a given mutable object reference.
static constexpr void AllocateElement(t_buffer_type &buffer, t_index_type start, t_index_type size, const t_type &object) noexcept
Allocates a range of elements by copy-constructing from a given object.
static constexpr void AllocateElement(t_buffer_type &buffer, t_index_type index, const t_type &object) noexcept
Allocates a single element by copy-constructing from a given object.
static constexpr void Allocate(t_buffer_type &buffer, t_index_type start, t_index_type size) noexcept
Allocates a range of elements in the buffer, delegating to the appropriate primitive or non-primitive...
The primary namespace for the NDEVR SDK.
constexpr t_type getMin(const t_type &left, const t_type &right)
Finds the minimum of the given arguments based on the < operator Author: Tyler Parke Date: 2017-11-05...
uint64_t uint08
-Defines an alias representing an 8 byte, unsigned integer
int32_t sint04
-Defines an alias representing a 4 byte, signed integer.
static constexpr size_t DetermineAlignment() noexcept
Determines the optimal memory alignment for an allocation of the given byte size.
constexpr t_to cast(const Angle< t_from > &value)
Casts an Angle from one backing type to another.
Definition Angle.h:408
static t_index_type NextCapacityBytes(t_index_type cap_elems, t_index_type need_elems) noexcept
Computes the next buffer capacity in elements, using a growth strategy tuned per platform alignment b...
Determines the optimal memory alignment for a given type based on its size.
static constexpr size_t Size() noexcept
Returns the optimal alignment size in bytes for the template type.
Precomputes a magic multiplier for fast compile-time division by the constant D, avoiding expensive r...
static constexpr unsigned __int128 high_bit
High bit used for computing the magic constant.
static constexpr uint08 m
Magic multiplier for GCC/Clang 128-bit integer path.
Compile-time helper that counts the number of trailing zero bits in a value, used for computing shift...