23#if MOCHI_LANGUAGE_CPP20
37#ifndef MOCHI_DARRAY_DEBUG
38#define MOCHI_DARRAY_DEBUG MOCHI_DEBUG
41#if MOCHI_DARRAY_DEBUG && MOCHI_ASSERT_ENABLED
42#define MOCHI_DARRAY_ASSERT(cond, ...) MOCHI_ASSERT(cond, __VA_ARGS__)
44#define MOCHI_DARRAY_ASSERT(cond, ...)
47#if MOCHI_LANGUAGE_CPP20
50template <
class From,
class To>
52 std::is_convertible_v<From, To> &&
requires {
static_cast<To
>(std::declval<From>()); };
57#define MOCHI_DARRAY_ASSERT_FORWARD_ITERATOR(IteratorType) \
59 HasDistance<IteratorType>, \
60 "This type of input iterator is not supported by DynamicArray because there is no way to " \
61 "reserve sufficient memory up front. We would have to loop calling emplace_back, which " \
62 "would be inefficient. Consider initializing this DynamicArray in a different way.");
64#define MOCHI_DARRAY_ASSERT_FORWARD_ITERATOR(IteratorType) \
67 std::forward_iterator_tag, \
68 typename std::iterator_traits<IteratorType>::iterator_category>, \
69 "This type of input iterator is not supported by DynamicArray because there is no way to " \
70 "reserve sufficient memory up front. We would have to loop calling emplace_back, which " \
71 "would be inefficient. Consider initializing this DynamicArray in a different way.");
108 !std::is_same_v<T, Allocator> && !std::is_same_v<T, Allocator*>,
109 "DynamicArray of allocators is not allowed because of overload ambiguity.");
136 : _begin(static_cast<T*>(allocator->allocate(
size * sizeof(T), alignof(T)))),
139 _allocator(allocator) {
140 DefaultConstructRange(_begin, _end);
151 template <
typename FromT, MOCHI_CONCEPT((std::is_convertible_v<FromT, T>))>
154 FromT
const& defaultValue,
156 : _begin(static_cast<T*>(allocator->allocate(
size * sizeof(T), alignof(T)))),
159 _allocator(allocator) {
160 CopyConstructRangeFromValue(_begin, _end, defaultValue);
175 std::input_iterator_tag,
176 typename std::iterator_traits<InputIt>::iterator_category>))>
178 : _allocator(allocator) {
180 auto size =
static_cast<size_type>(std::distance(rangeBegin, rangeEnd));
181 _begin =
static_cast<T*
>(allocator->allocate(
size *
sizeof(T),
alignof(T)));
182 _end = _endCapacity = _begin +
size;
183 CopyConstructRange(_begin, rangeBegin, rangeEnd);
204 typename InputContainerT,
206 (!std::is_same_v<std::decay_t<InputContainerT>,
DynamicArray> &&
207 sizeof(
decltype(std::begin(std::declval<InputContainerT const&>()))) &&
208 sizeof(
decltype(std::end(std::declval<InputContainerT const&>())))))>
241 if (other._allocator->is_equal(*_allocator))
243 _begin = other._begin;
245 _endCapacity = other._endCapacity;
246 other._begin = other._end = other._endCapacity = nullptr;
249 auto size = other.size();
250 _begin = static_cast<T*>(_allocator->allocate(size * sizeof(T), alignof(T)));
252 _endCapacity = _begin + size;
255 for (auto src = other._begin; src < other._end; ++src, ++_end) {
256 new (_end) T(std::move(*src));
270 : _begin(other._begin),
272 _endCapacity(other._endCapacity),
273 _allocator(other._allocator) {
274 other._begin = other._end = other._endCapacity =
nullptr;
282 if (_begin !=
nullptr) {
283 DestroyRange(_begin, _end);
284 _allocator->deallocate(_begin,
capacity() *
sizeof(T),
alignof(T));
285#if MOCHI_DARRAY_DEBUG
286 _begin = _end = _endCapacity =
nullptr;
287 _allocator =
nullptr;
384 return _end - _begin;
391 return _endCapacity - _begin;
398 return _end == _begin;
412 if (newSize >
size()) {
414 GrowCapacity(_begin ? GetNextCapacity(newSize) : newSize);
416 DefaultConstructRange(_end, _begin + newSize);
418 DestroyRange(_begin + newSize, _end);
420 _end = _begin + newSize;
438 if (newSize >
size()) {
440 GrowCapacity(_begin ? GetNextCapacity(newSize) : newSize);
442 CopyConstructRangeFromValue(_end, _begin + newSize, value);
444 DestroyRange(_begin + newSize, _end);
446 _end = _begin + newSize;
466 if (newSize > prevSize) {
468 GrowCapacity(_begin ? GetNextCapacity(newSize) : newSize);
470 _end = _begin + newSize;
471 DebugFillWithNaN(_begin + prevSize, _end);
473 DestroyRange(_begin + newSize, _end);
474 _end = _begin + newSize;
488 template <
typename... Args>
504 GrowCapacity(newCapacity);
513 DestroyRange(_begin, _end);
521 if (_endCapacity > _end) {
523 _allocator->deallocate(_begin,
capacity() *
sizeof(T),
alignof(T));
524 _begin = _end = _endCapacity =
nullptr;
526 auto const n =
size();
527 auto* newBegin =
static_cast<T*
>(_allocator->allocate(n *
sizeof(T),
alignof(T)));
528 MoveConstructRange(newBegin, _begin, n);
529 DestroyRange(_begin, _end);
530 _allocator->deallocate(_begin,
capacity() *
sizeof(T),
alignof(T));
532 _end = _endCapacity = newBegin + n;
553 template <
class... Args>
555 if constexpr (
sizeof...(Args) == 1) {
556 if constexpr (std::is_same_v<
558 std::remove_cv_t<std::remove_reference_t<decltype(std::get<0>(
559 std::forward_as_tuple(args...)))>>>) {
561 !IsInThisArray(&std::get<0>(std::forward_as_tuple(args...))),
562 "You are not allowed to push an element of a DynamicArray onto the end of the same DynamicArray. "
563 "Consider pushing a copy of the value instead.");
566 if (_endCapacity == _end)
568 GrowCapacity(GetNextCapacity(
size() + 1));
570 new (_end) T(std::forward<Args>(args)...);
626 if constexpr (!std::is_trivially_destructible_v<T>) {
643 auto rangeBegin =
const_cast<iterator>(rangeBegin_);
644 auto rangeEnd =
const_cast<iterator>(rangeEnd_);
645 if (rangeEnd == _end) {
648 DestroyRange(rangeBegin, rangeEnd);
650 }
else if (rangeEnd != rangeBegin) {
654 if constexpr (std::is_trivially_move_assignable_v<T>) {
655 memmove(rangeBegin, rangeEnd, (_end - rangeEnd) *
sizeof(T));
658 T* src =
const_cast<T*
>(rangeEnd);
659 for (; src != _end; ++dst, ++src) {
660 *dst = std::move(*src);
663 auto numRemoved = (rangeEnd - rangeBegin);
664 auto newSize =
size() - numRemoved;
665 DestroyRange(_begin + newSize, _end);
666 _end = _begin + newSize;
678 auto it =
const_cast<iterator>(it_);
680 if (it + 1 == _end) {
697 auto* last = _end - 1;
699 *it = std::move(*last);
718 std::input_iterator_tag,
719 typename std::iterator_traits<InputIt>::iterator_category>))>
720 void assign(InputIt rangeBegin, InputIt rangeEnd) {
722 if constexpr (std::is_pointer_v<InputIt>) {
725 !IsInThisArray(rangeBegin, rangeEnd),
726 "The input range is not allowed to point to memory within the DynamicArray that is being modified.");
728 auto const oldSize =
size();
729 auto const newSize =
static_cast<size_type>(std::distance(rangeBegin, rangeEnd));
731 _allocator || (newSize == oldSize),
732 "It is illegal to change the size of a DynamicArray that does not own the memory.");
733 if (newSize <= oldSize) {
735 _end = _begin + newSize;
736 DestroyRange(_begin + newSize, _begin + oldSize);
737 std::copy(rangeBegin, rangeEnd, _begin);
740 _end += (newSize - oldSize);
741 if constexpr (std::is_trivially_copyable_v<T>) {
744 std::copy(rangeBegin, rangeEnd, _begin);
747 auto src = rangeBegin;
748 std::copy_n(src, oldSize, _begin);
750 std::advance(src, oldSize);
751 for (
size_type i = oldSize; i < newSize; ++i, ++src) {
752 new (_begin + i) T(*src);
758 std::is_trivially_copyable_v<T> && std::is_trivially_move_constructible_v<T> &&
759 std::is_trivially_move_assignable_v<T> && std::is_trivially_destructible_v<T>) {
764 _allocator->deallocate(_begin,
capacity() *
sizeof(T),
alignof(T));
766 _begin =
static_cast<T*
>(_allocator->allocate(newSize *
sizeof(T),
alignof(T)));
767 _end = _endCapacity = _begin + newSize;
768 std::copy(rangeBegin, rangeEnd, _begin);
771 GrowCapacity(newSize);
772 _end = _begin + newSize;
774 auto src = rangeBegin;
775 std::copy_n(src, oldSize, _begin);
777 std::advance(src, oldSize);
778 CopyConstructRange(_begin + oldSize, src, rangeEnd);
794 template <
class InputIt>
795 void append(InputIt rangeBegin, InputIt rangeEnd) {
798 !IsInThisArray(rangeBegin, rangeEnd),
799 "The input range is not allowed to point to memory within the DynamicArray that is being modified.");
800 auto count = std::distance(rangeBegin, rangeEnd);
802 auto prevSize =
size();
803 auto newSize = prevSize + count;
806 GrowCapacity(GetNextCapacity(newSize));
808 _end = _begin + newSize;
809 CopyConstructRange(_begin + prevSize, rangeBegin, rangeEnd);
822 template <
class InputContainerT>
824 append(std::begin(container), std::end(container));
881 if (_allocator->is_equal(*other._allocator))
884 if (_begin !=
nullptr) {
885 _allocator->deallocate(_begin,
capacity() *
sizeof(T),
alignof(T));
888 _begin = other._begin;
890 _endCapacity = other._endCapacity;
891 other._begin = other._end = other._endCapacity =
nullptr;
893 else if (!other.empty()) {
895 auto const newSize = other.size();
897 GrowCapacity(newSize);
902 for (
auto src = other._begin; src < other._end; ++src, ++_end) {
903 new (_end) T(std::move(*src));
921 assign(std::begin(list), std::end(list));
934 typename InputContainerT,
936 (!std::is_same_v<std::decay_t<InputContainerT>,
DynamicArray> &&
937 sizeof(
decltype(std::begin(std::declval<InputContainerT const&>()))) &&
938 sizeof(
decltype(std::end(std::declval<InputContainerT const&>())))))>
940 assign(std::begin(other), std::end(other));
949 if (other.
size() == s) {
951 if (other._begin[i] != _begin[i]) {
966 return !(*
this == other);
970 void GrowCapacity(size_type newCapacity) {
975 _allocator->deallocate(_begin, capacity() *
sizeof(T),
alignof(T));
977 _begin = _end =
static_cast<T*
>(_allocator->allocate(newCapacity *
sizeof(T),
alignof(T)));
978 _endCapacity = _begin + newCapacity;
980 auto oldSize = size();
981 auto* newBegin =
static_cast<T*
>(_allocator->allocate(newCapacity *
sizeof(T),
alignof(T)));
982 for (
auto i =
static_cast<ptrdiff_t
>(oldSize) - 1; i >= 0; --i) {
983 new (&newBegin[i]) T(std::move(_begin[i]));
984 if constexpr (!std::is_trivially_destructible_v<T>) {
988 _allocator->deallocate(_begin, capacity() *
sizeof(T),
alignof(T));
990 _end = newBegin + oldSize;
991 _endCapacity = newBegin + newCapacity;
995 size_type GetNextCapacity(size_type minCapacity)
const {
997 return std::max(std::max(minCapacity, kGrowthPatternStartSize), capacity() * 3 / 2);
1000 static void DebugFillWithNaN(
1001 [[maybe_unused]]
void* rangeBegin,
1002 [[maybe_unused]]
void const* rangeEnd) {
1003#if MOCHI_DARRAY_DEBUG
1006 auto fillBeginAddr =
1007 Min(
reinterpret_cast<size_type
>(rangeEnd),
1008 RoundUp(
reinterpret_cast<size_type
>(rangeBegin),
alignof(
real)));
1010 Max(
reinterpret_cast<size_type
>(rangeBegin),
1011 RoundDown(
reinterpret_cast<size_type
>(rangeEnd),
alignof(
real)));
1012 auto* fillBegin =
reinterpret_cast<std::byte*
>(fillBeginAddr);
1013 auto* fillEnd =
reinterpret_cast<std::byte*
>(fillEndAddr);
1014 auto const nan = std::numeric_limits<real>::signaling_NaN();
1015 for (
auto it = fillBegin; it < fillEnd; it +=
sizeof(nan)) {
1017 memcpy(it, &nan,
sizeof(nan));
1021 auto paddingSizeFront = fillBegin -
reinterpret_cast<std::byte*
>(rangeBegin);
1022 if (paddingSizeFront) {
1023 memset(rangeBegin, 0, paddingSizeFront);
1025 auto paddingSizeBack =
reinterpret_cast<std::byte const*
>(rangeEnd) - fillEnd;
1026 if (paddingSizeBack) {
1027 memset(fillEnd, 0, paddingSizeBack);
1032 static void DefaultConstructRange(T* rangeBegin, T
const* rangeEnd) {
1033 if constexpr (std::is_trivially_default_constructible_v<T>) {
1035 reinterpret_cast<std::intptr_t
>(rangeEnd) -
reinterpret_cast<std::intptr_t
>(rangeBegin);
1036 memset(rangeBegin, 0, numBytes);
1038 for (
auto* it = rangeBegin; it < rangeEnd; ++it) {
1044 static void CopyConstructRangeFromValue(T* rangeBegin, T
const* rangeEnd, T
const& value) {
1046 for (
auto* it = rangeBegin; it < rangeEnd; ++it) {
1051 template <
class InputIt>
1052 static void CopyConstructRange(T* dstBegin, InputIt srcBegin, InputIt srcEnd) {
1054 using SrcT = std::remove_cv_t<typename std::iterator_traits<InputIt>::value_type>;
1056 std::is_trivially_copyable_v<T> && std::is_copy_assignable_v<T> &&
1057 std::is_same_v<SrcT, T>) {
1058 std::copy(srcBegin, srcEnd, dstBegin);
1060 auto src = srcBegin;
1061 auto dst = dstBegin;
1062 for (; src != srcEnd; ++src, ++dst) {
1068 static void MoveConstructRange(T* dstBegin, T* srcBegin, size_type count) {
1069 if constexpr (std::is_trivially_move_constructible_v<T>) {
1072 memcpy(dstBegin, srcBegin, count *
sizeof(T));
1075 for (
auto i = 0; i < count; ++i) {
1076 new (&dstBegin[i]) T(std::move(srcBegin[i]));
1081 static void Destroy(T* it) {
1082 if constexpr (!std::is_trivially_destructible_v<T>) {
1085 DebugFillWithNaN(it, it + 1);
1088 static void DestroyRange(T* rangeBegin, T
const* rangeEnd) {
1089 if constexpr (!std::is_trivially_destructible_v<T>) {
1090 if (rangeEnd > rangeBegin)
1093 auto* it =
const_cast<T*
>(rangeEnd);
1094 while (it != rangeBegin) {
1100 DebugFillWithNaN(rangeBegin, rangeEnd);
1103#if MOCHI_DARRAY_DEBUG
1104 template <
class InputIt>
1105 bool IsInThisArray(InputIt rangeBegin, InputIt rangeEnd)
const {
1106 if constexpr (std::is_pointer_v<std::remove_reference_t<InputIt>>) {
1109 (
reinterpret_cast<T const*
>(rangeBegin)) >= _endCapacity ||
1110 (
reinterpret_cast<T const*
>(rangeEnd) <= _begin));
1118 bool IsInThisArray(T
const* ptr)
const {
1119 return (ptr >= _begin) && (ptr < _end);
1124 static constexpr size_type kGrowthPatternStartSize = 8;
1126 T* _begin =
nullptr;
1128 T* _endCapacity =
nullptr;
1137template <
typename T,
typename InBeginIterator,
typename InEndIterator>
1140 out.
append(inBegin, inEnd);
1144template <
typename T,
typename InContainer>
1146 out.
append(std::begin(inContainer), std::end(inContainer));
1151template <
typename T,
typename ContainerIn>
1153 if (valueToAdd == T(0)) {
1156 auto outIndex = out.
size();
1158 for (
auto const& x : in) {
1159 out[outIndex++] = x + valueToAdd;
1169template <
class ContainerT>
1170struct IsDynamicArrayDef :
public std::false_type {};
1172struct IsDynamicArrayDef<
DynamicArray<T>> :
public std::true_type {};
1176template <
class ContainerT>
1177static constexpr bool kIsDynamicArray =
1178 superdex::details::IsDynamicArrayDef<std::decay_t<ContainerT>>::value;
1184 std::input_iterator_tag,
1185 typename std::iterator_traits<InputIt>::iterator_category>))>
1192#if MOCHI_USE_REFLECTION
1194struct SReflectTypeTraits<
superdex::DynamicArray<T>> {
1195 static constexpr SReflect::CoreType coreType = SReflect::CoreType::CT_array;
1196 static SReflect::ArrayTypeInfo
const& GetTypeInfo() {
1198 static auto* s_typeInfo =
1199 SReflect::MakeDynamicArrayTypeInfo<SReflect::VectorTypeInfo<ArrT>, ArrT, T>(
1200 "superdex::DynamicArray",
true);
Virtual interface class for memory allocation and deallocation.
A dynamically resizable array with syntax and behavior similar to std::pmr::vector.
DynamicArray()=default
Construct an empty DynamicArray with the default allocator.
DynamicArray(size_type size, FromT const &defaultValue, Allocator *allocator=GetDefaultAllocator())
Construct a DynamicArray filled with copies of the specified value.
size_type size() const
Get the number of elements in the array.
void append(InputIt rangeBegin, InputIt rangeEnd)
Add values to the end of this DynamicArray by copying them from the half-open range [rangeBegin,...
T const & back() const
Get a const reference to the last element.
const_iterator cbegin() const
Get a const iterator pointing to the beginning of the array.
DynamicArray(DynamicArray const &other)
Copy construct from another DynamicArray.
T & push_back()
Default construct a new element at the end of the array.
void reset(Args &&... args)
Reset the state of this DynamicArray using the arguments from any constructor.
iterator begin()
Get an iterator pointing to the beginning of the array.
void resize_noinit(size_type newSize)
Set the size but do not initialize any new elements.
DynamicArray & operator=(DynamicArray const &other)
Copy Assignment: Replace the contents of this DynamicArray by copying values from another one.
void push_back(T &&value)
Move the value to a new element at the end of the array.
DynamicArray & operator=(std::initializer_list< T > const &list)
Copy Assignment: Replace the contents of this DynamicArray by copying values from a std::initializer_...
Allocator * get_allocator() const
Get a pointer to the polymorphic allocator.
void resize(size_type newSize)
Set the size.
void resize(size_type newSize, T const &value)
Set the size.
T & operator[](size_type i)
Get a reference to the value at index i.
DynamicArray(size_type size, Allocator *allocator=GetDefaultAllocator())
Construct a DynamicArray with an initial size.
DynamicArray & operator=(DynamicArray &&other)
Move Assignment: Replace the contents of this DynamicArray by moving memory or values from another on...
bool operator==(DynamicArray const &other) const
Comparison returns true if the other array has the same size and all values are equal.
T const & front() const
Get a const reference to the first element.
DynamicArray(std::initializer_list< T > const &list, Allocator *allocator=GetDefaultAllocator())
Construct a DynamicArray and copy values from a std::initializer_list.
T const & operator[](size_type i) const
Get a reference to the value at index i.
T const * data() const
Get a const pointer to the first element.
T * data()
Get a pointer to the first element.
void erase(const_iterator it_)
Remove one element from this array.
void pop_back()
Remove the last element of the array (must not be empty).
void push_back(T const &value)
Copy the value to a new element at the end of the array.
void erase(const_iterator rangeBegin_, const_iterator rangeEnd_)
Remove the half-open range [rangeBegin, rangeEnd) from this array.
void assign(InputIt rangeBegin, InputIt rangeEnd)
Replace the contents of this DynamicArray by copying values from the half-open range [rangeBegin,...
const_iterator cend() const
Get a const iterator pointing to the next element AFTER the end of the array.
void erase_unordered(iterator it)
Remove one element from this array and swap the last element into its place.
DynamicArray(DynamicArray &&other) noexcept
Move construct from another DynamicArray.
bool empty() const
Return true if the array is empty (zero size).
size_type capacity() const
Get the number of elements that could fit in the array without allocating more memory.
DynamicArray(DynamicArray const &other, Allocator *allocator)
Construct a DynamicArray and copy values from another one.
const_iterator end() const
Get a const iterator pointing to the next element AFTER the end of the array.
void clear()
Set the size to zero.
void shrink_to_fit()
Ensure that the capacity (memory allocated) is no larger than the size (memory used).
bool operator!=(DynamicArray const &other) const
Comparison returns false unless the other array has the same size and all values are equal.
T & front()
Get a reference to the first element.
iterator end()
Get an iterator pointing to the next element AFTER the end of the array.
DynamicArray(Allocator *allocator)
Construct an empty DynamicArray with a specific allocator.
T & back()
Get a reference to the last element.
void reserve(size_type newCapacity)
Ensure that this array has at least the specified capacity.
DynamicArray & operator=(InputContainerT const &other)
Copy Assignment: Replace the contents of this DynamicArray by copying values from another iterable co...
~DynamicArray()
Destroy the DynamicArray object and all of its elements.
DynamicArray(InputIt rangeBegin, InputIt rangeEnd, Allocator *allocator=GetDefaultAllocator())
Construct a DynamicArray and copy values from the half-open range: [rangeBegin, rangeEnd).
DynamicArray(DynamicArray &&other, Allocator *allocator)
Construct a DynamicArray by moving memory or values from another one.
void emplace_back(Args &&... args)
Emplace a new element at the end of the array.
DynamicArray(InputContainerT const &other, Allocator *allocator=GetDefaultAllocator())
Construct a DynamicArray and copy values from another iterable container type like superdex::Span,...
void append(InputContainerT const &container)
Add values to the end of this DynamicArray by copying them from another iterable container.
const_iterator begin() const
Get a const iterator pointing to the beginning of the array.
#define MOCHI_DARRAY_ASSERT_FORWARD_ITERATOR(IteratorType)
#define MOCHI_DARRAY_ASSERT(cond,...)
constexpr T const & Min(T const &a, T const &b)
constexpr T RoundDown(T numToRound, T multiple)
constexpr T RoundUp(T numToRound, T multiple)
void Append(DynamicArray< T > &out, InBeginIterator const &inBegin, InEndIterator const &inEnd)
Utility Functions.
constexpr bool kIsResizeNoInitSafe
Helper variable template for IsResizeNoInitSafe.
DynamicArray(InputIt, InputIt, Allocator *=GetDefaultAllocator()) -> DynamicArray< typename std::iterator_traits< InputIt >::value_type >
Allocator * GetDefaultAllocator()
Get an instance of the DefaultAllocator class.
constexpr T const & Max(T const &a, T const &b)
void AppendSum(DynamicArray< T > &out, ContainerIn const &in, T valueToAdd)
Trait to determine if a type is safe for use with DynamicArray::resize_noinit.