From 05e62e1381833af7ec1610f543ffea34b27bea20 Mon Sep 17 00:00:00 2001 From: _Redstone_c_ Date: Wed, 8 Feb 2023 23:30:46 +0800 Subject: [PATCH] feat(containers): add TArray and the corresponding testing --- .../Private/Testing/ContainersTesting.cpp | 122 ++ .../Source/Public/Containers/Array.h | 1185 +++++++++++++++++ .../Source/Public/Containers/Containers.h | 4 + .../Source/Public/Testing/ContainersTesting.h | 18 + 4 files changed, 1329 insertions(+) create mode 100644 Redcraft.Utility/Source/Private/Testing/ContainersTesting.cpp create mode 100644 Redcraft.Utility/Source/Public/Containers/Array.h create mode 100644 Redcraft.Utility/Source/Public/Containers/Containers.h create mode 100644 Redcraft.Utility/Source/Public/Testing/ContainersTesting.h diff --git a/Redcraft.Utility/Source/Private/Testing/ContainersTesting.cpp b/Redcraft.Utility/Source/Private/Testing/ContainersTesting.cpp new file mode 100644 index 0000000..bf87608 --- /dev/null +++ b/Redcraft.Utility/Source/Private/Testing/ContainersTesting.cpp @@ -0,0 +1,122 @@ +#include "Testing/ContainersTesting.h" + +#include "Containers/Containers.h" +#include "Miscellaneous/AssertionMacros.h" + +NAMESPACE_REDCRAFT_BEGIN +NAMESPACE_MODULE_BEGIN(Redcraft) +NAMESPACE_MODULE_BEGIN(Utility) + +NAMESPACE_BEGIN(Testing) + +void TestContainers() +{ + TestArray(); +} + +void TestArray() +{ + { + TArray ArrayA; + TArray ArrayB(4); + TArray ArrayC(4, 4); + TArray ArrayD(ArrayC); + TArray ArrayE(MoveTemp(ArrayB)); + TArray ArrayF({ 0, 1, 2, 3 }); + + TArray ArrayG; + TArray ArrayH; + TArray ArrayI; + + ArrayG = ArrayD; + ArrayH = MoveTemp(ArrayE); + ArrayI = { 0, 1, 2, 3 }; + + always_check((ArrayC == TArray({ 4, 4, 4, 4 }))); + always_check((ArrayD == TArray({ 4, 4, 4, 4 }))); + always_check((ArrayG == TArray({ 4, 4, 4, 4 }))); + always_check((ArrayF == TArray({ 0, 1, 2, 3 }))); + always_check((ArrayI == TArray({ 0, 1, 2, 3 }))); + } + + { + TArray ArrayA = { 1, 2, 3 }; + TArray ArrayB = { 7, 8, 9, 10 }; + TArray ArrayC = { 1, 2, 3 }; + + always_check((!(ArrayA == ArrayB))); + always_check(( (ArrayA != ArrayB))); + always_check(( (ArrayA < ArrayB))); + always_check(( (ArrayA <= ArrayB))); + always_check((!(ArrayA > ArrayB))); + always_check((!(ArrayA >= ArrayB))); + + always_check(( (ArrayA == ArrayC))); + always_check((!(ArrayA != ArrayC))); + always_check((!(ArrayA < ArrayC))); + always_check(( (ArrayA <= ArrayC))); + always_check((!(ArrayA > ArrayC))); + always_check(( (ArrayA >= ArrayC))); + } + + { + TArray Array = { 1, 2, 3 }; + + Array.Insert(Array.Begin() + 1, 2); + always_check((Array == TArray({ 1, 2, 2, 3 }))); + + Array.Insert(Array.End(), 2, 4); + always_check((Array == TArray({ 1, 2, 2, 3, 4, 4 }))); + + Array.Insert(Array.Begin(), { 1, 1, 4, 5, 1, 4 }); + always_check((Array == TArray({ 1, 1, 4, 5, 1, 4, 1, 2, 2, 3, 4, 4 }))); + + Array.Emplace(Array.End(), 5); + always_check((Array == TArray({ 1, 1, 4, 5, 1, 4, 1, 2, 2, 3, 4, 4, 5 }))); + + Array.StableErase(Array.End() - 1); + always_check((Array == TArray({ 1, 1, 4, 5, 1, 4, 1, 2, 2, 3, 4, 4 }))); + + Array.StableErase(Array.End() - 2, Array.End()); + always_check((Array == TArray({ 1, 1, 4, 5, 1, 4, 1, 2, 2, 3 }))); + + Array.Erase(Array.End() - 2); + always_check((Array.Num() == 9)); + + Array.Erase(Array.Begin(), Array.Begin() + 6); + always_check((Array.Num() == 3)); + } + + { + TArray Array = { 1, 2, 3 }; + + Array.PushBack(4); + always_check((Array == TArray({ 1, 2, 3, 4 }))); + + Array.EmplaceBack(5); + always_check((Array == TArray({ 1, 2, 3, 4, 5 }))); + + Array.EmplaceBack(5) = 6; + always_check((Array == TArray({ 1, 2, 3, 4, 5, 6 }))); + + Array.PopBack(); + always_check((Array == TArray({ 1, 2, 3, 4, 5 }))); + + Array.SetNum(4); + always_check((Array == TArray({ 1, 2, 3, 4 }))); + + Array.Reserve(64); + always_check((Array.Num() == 4)); + always_check((Array.Max() == 64)); + + Array.Shrink(); + always_check((Array.Num() == 4)); + always_check((Array.Max() == 4)); + } +} + +NAMESPACE_END(Testing) + +NAMESPACE_MODULE_END(Utility) +NAMESPACE_MODULE_END(Redcraft) +NAMESPACE_REDCRAFT_END diff --git a/Redcraft.Utility/Source/Public/Containers/Array.h b/Redcraft.Utility/Source/Public/Containers/Array.h new file mode 100644 index 0000000..27e1f89 --- /dev/null +++ b/Redcraft.Utility/Source/Public/Containers/Array.h @@ -0,0 +1,1185 @@ +#pragma once + +#include "CoreTypes.h" +#include "Templates/Utility.h" +#include "Templates/TypeHash.h" +#include "Templates/Container.h" +#include "TypeTraits/TypeTraits.h" +#include "Memory/MemoryOperator.h" +#include "Memory/ObserverPointer.h" +#include "Memory/DefaultAllocator.h" +#include "Memory/AllocatorInterface.h" +#include "Miscellaneous/AssertionMacros.h" + +NAMESPACE_REDCRAFT_BEGIN +NAMESPACE_MODULE_BEGIN(Redcraft) +NAMESPACE_MODULE_BEGIN(Utility) + +NAMESPACE_PRIVATE_BEGIN + +template && !CFinal> +class TArrayStorage; + +template +class TArrayStorage : private A +{ +public: + + FORCEINLINE constexpr TArrayStorage() = default; + + FORCEINLINE constexpr TArrayStorage(const TArrayStorage&) = delete; + FORCEINLINE constexpr TArrayStorage(TArrayStorage&& InValue) = delete; + FORCEINLINE constexpr TArrayStorage& operator=(const TArrayStorage&) = delete; + FORCEINLINE constexpr TArrayStorage& operator=(TArrayStorage&&) = delete; + + FORCEINLINE constexpr T*& GetPointer() { return Pointer; } + FORCEINLINE constexpr T* GetPointer() const { return Pointer; } + + FORCEINLINE constexpr size_t& GetNum() { return ArrayNum; } + FORCEINLINE constexpr size_t GetNum() const { return ArrayNum; } + FORCEINLINE constexpr size_t& GetMax() { return ArrayMax; } + FORCEINLINE constexpr size_t GetMax() const { return ArrayMax; } + + FORCEINLINE constexpr A& GetAllocator() { return *this; } + FORCEINLINE constexpr const A& GetAllocator() const { return *this; } + +private: + + // NOTE: NO_UNIQUE_ADDRESS is not valid in MSVC, use base class instead of member variable + //NO_UNIQUE_ADDRESS A Allocator; + + T* Pointer; + + size_t ArrayNum; + size_t ArrayMax; + +}; + +template +class TArrayStorage +{ +public: + + FORCEINLINE constexpr TArrayStorage() = default; + + FORCEINLINE constexpr TArrayStorage(const TArrayStorage&) = delete; + FORCEINLINE constexpr TArrayStorage(TArrayStorage&& InValue) = delete; + FORCEINLINE constexpr TArrayStorage& operator=(const TArrayStorage&) = delete; + FORCEINLINE constexpr TArrayStorage& operator=(TArrayStorage&&) = delete; + + FORCEINLINE constexpr T*& GetPointer() { return Pointer; } + FORCEINLINE constexpr T* GetPointer() const { return Pointer; } + + FORCEINLINE constexpr size_t& GetNum() { return ArrayNum; } + FORCEINLINE constexpr size_t GetNum() const { return ArrayNum; } + FORCEINLINE constexpr size_t& GetMax() { return ArrayMax; } + FORCEINLINE constexpr size_t GetMax() const { return ArrayMax; } + + FORCEINLINE constexpr A& GetAllocator() { return Allocator; } + FORCEINLINE constexpr const A& GetAllocator() const { return Allocator; } + +private: + + T* Pointer; + + size_t ArrayNum; + size_t ArrayMax; + + A Allocator; + +}; + +template +class TArrayIterator +{ +public: + +# if DO_CHECK + FORCEINLINE constexpr TArrayIterator() : Owner(nullptr) { } +# elif + FORCEINLINE constexpr TArrayIterator() = default; +# endif + +# if DO_CHECK + FORCEINLINE constexpr TArrayIterator(const TArrayIterator>& InValue) requires (CConst) + : Owner(InValue.Owner), Pointer(InValue.Pointer) + { } +# elif + FORCEINLINE constexpr TArrayIterator(const TArrayIterator>& InValue) requires (CConst) + : Pointer(InValue.Pointer) + { } +# endif + + FORCEINLINE constexpr TArrayIterator(const TArrayIterator&) = default; + FORCEINLINE constexpr TArrayIterator(TArrayIterator&&) = default; + FORCEINLINE constexpr TArrayIterator& operator=(const TArrayIterator&) = default; + FORCEINLINE constexpr TArrayIterator& operator=(TArrayIterator&&) = default; + + NODISCARD friend constexpr bool operator==(const TArrayIterator& LHS, const TArrayIterator& RHS) { return LHS.Pointer == RHS.Pointer; } + + NODISCARD friend constexpr strong_ordering operator<=>(const TArrayIterator & LHS, const TArrayIterator & RHS) { return LHS.Pointer <=> RHS.Pointer; } + + NODISCARD FORCEINLINE constexpr ElementType& operator*() const { CheckThis(true); return *Pointer; } + NODISCARD FORCEINLINE constexpr ElementType* operator->() const { CheckThis(true); return Pointer; } + + FORCEINLINE constexpr TArrayIterator& operator++() { ++Pointer; CheckThis(); return *this; } + FORCEINLINE constexpr TArrayIterator& operator--() { --Pointer; CheckThis(); return *this; } + + FORCEINLINE constexpr TArrayIterator operator++(int) { TArrayIterator Temp = *this; ++Pointer; CheckThis(); return Temp; } + FORCEINLINE constexpr TArrayIterator operator--(int) { TArrayIterator Temp = *this; --Pointer; CheckThis(); return Temp; } + + FORCEINLINE constexpr TArrayIterator& operator+=(ptrdiff Offset) { Pointer += Offset; CheckThis(); return *this; } + FORCEINLINE constexpr TArrayIterator& operator-=(ptrdiff Offset) { Pointer -= Offset; CheckThis(); return *this; } + + NODISCARD FORCEINLINE constexpr TArrayIterator operator+(ptrdiff Offset) const { TArrayIterator Temp = *this; Temp += Offset; Temp.CheckThis(); return Temp; } + NODISCARD FORCEINLINE constexpr TArrayIterator operator-(ptrdiff Offset) const { TArrayIterator Temp = *this; Temp -= Offset; Temp.CheckThis(); return Temp; } + + NODISCARD friend FORCEINLINE constexpr ptrdiff operator-(const TArrayIterator& LHS, const TArrayIterator& RHS) + { + LHS.CheckThis(); + RHS.CheckThis(); + + return LHS.Pointer - RHS.Pointer; + } + + NODISCARD FORCEINLINE constexpr ElementType& operator[](ptrdiff Index) const { TArrayIterator Temp = *this + Index; Temp.CheckThis(); return *Temp; } + +private: + +# if DO_CHECK + const ArrayType* Owner; +# endif + + ElementType* Pointer; + +# if DO_CHECK + FORCEINLINE constexpr TArrayIterator(const ArrayType* InContainer, ElementType* InPointer) + : Owner(InContainer), Pointer(InPointer) + { } +# elif + FORCEINLINE constexpr TArrayIterator(const ArrayType* InContainer, ElementType* InPointer) + : Pointer(InPointer) + { } +# endif + + FORCEINLINE void CheckThis(bool bExceptEnd = false) const + { + checkf(Owner && Owner->IsValidIterator(*this), TEXT("Read access violation. Please check IsValidIterator().")); + checkf(!(bExceptEnd && Owner->End() == *this), TEXT("Read access violation. Please check IsValidIterator().")); + } + + friend ArrayType; + + template + friend class TArrayIterator; + +}; + +NAMESPACE_PRIVATE_END + +/** Dynamic array. The elements are stored contiguously, which means that elements can be accessed not only through iterators, but also using offsets to regular pointers to elements. */ +template requires (!CConst && CDestructible && CInstantiableAllocator) +class TArray final +{ +public: + + using ElementType = T; + using AllocatorType = Allocator; + + using Iterator = NAMESPACE_PRIVATE::TArrayIterator; + using ConstIterator = NAMESPACE_PRIVATE::TArrayIterator; + + /** Default constructor. Constructs an empty container with a default-constructed allocator. */ + FORCEINLINE constexpr TArray() : TArray(0) { } + + /** Constructs the container with 'Count' default instances of T. */ + constexpr explicit TArray(size_t Count) + requires (CDefaultConstructible) + { + Storage.GetNum() = Count; + Storage.GetMax() = Storage.GetAllocator().CalculateSlackReserve(Num()); + Storage.GetPointer() = Storage.GetAllocator().Allocate(Max()); + + Memory::DefaultConstruct(Storage.GetPointer(), Num()); + } + + /** Constructs the container with 'Count' copies of elements with 'InValue'. */ + constexpr TArray(size_t Count, const ElementType& InValue) + requires (CCopyConstructible) + { + Storage.GetNum() = Count; + Storage.GetMax() = Storage.GetAllocator().CalculateSlackReserve(Num()); + Storage.GetPointer() = Storage.GetAllocator().Allocate(Max()); + + for (size_t Index = 0; Index < Num(); ++Index) + { + new (Storage.GetPointer() + Index) ElementType(InValue); + } + } + + /** Copy constructor. Constructs the container with the copy of the contents of 'InValue'. */ + constexpr TArray(const TArray& InValue) + requires (CCopyConstructible) + { + Storage.GetNum() = InValue.Num(); + Storage.GetMax() = Storage.GetAllocator().CalculateSlackReserve(Num()); + Storage.GetPointer() = Storage.GetAllocator().Allocate(Max()); + + Memory::CopyConstruct(Storage.GetPointer(), InValue.Storage.GetPointer(), Num()); + } + + /** Move constructor. After the move, 'InValue' is guaranteed to be empty. */ + constexpr TArray(TArray&& InValue) + requires (CMoveConstructible) + { + Storage.GetNum() = InValue.Num(); + + if (InValue.Storage.GetAllocator().IsTransferable(InValue.Storage.GetPointer())) + { + Storage.GetMax() = InValue.Max(); + Storage.GetPointer() = InValue.Storage.GetPointer(); + + InValue.Storage.GetNum() = 0; + InValue.Storage.GetMax() = InValue.Storage.GetAllocator().CalculateSlackReserve(InValue.Num()); + InValue.Storage.GetPointer() = InValue.Storage.GetAllocator().Allocate(InValue.Max()); + } + else + { + Storage.GetMax() = Storage.GetAllocator().CalculateSlackReserve(Num()); + Storage.GetPointer() = Storage.GetAllocator().Allocate(Max()); + + Memory::MoveConstruct(Storage.GetPointer(), InValue.Storage.GetPointer(), Num()); + } + } + + /** Constructs the container with the contents of the initializer list. */ + constexpr TArray(initializer_list IL) + requires (CCopyConstructible) + { + Storage.GetNum() = GetNum(IL); + Storage.GetMax() = Storage.GetAllocator().CalculateSlackReserve(GetNum(IL)); + Storage.GetPointer() = Storage.GetAllocator().Allocate(Max()); + + Memory::CopyConstruct(Storage.GetPointer(), NAMESPACE_REDCRAFT::GetData(IL), Num()); + } + + /** Destructs the array. The destructors of the elements are called and the used storage is deallocated. */ + constexpr ~TArray() + { + Memory::Destruct(Storage.GetPointer(),Num()); + Storage.GetAllocator().Deallocate(Storage.GetPointer()); + } + + /** Copy assignment operator. Replaces the contents with a copy of the contents of 'InValue'. */ + constexpr TArray& operator=(const TArray& InValue) + requires (CCopyConstructible && CCopyAssignable) + { + if (&InValue == this) UNLIKELY return *this; + + size_t NumToAllocate = InValue.Num(); + + NumToAllocate = NumToAllocate > Max() ? Storage.GetAllocator().CalculateSlackGrow(InValue.Num(), Max()) : NumToAllocate; + NumToAllocate = NumToAllocate < Max() ? Storage.GetAllocator().CalculateSlackShrink(InValue.Num(), Max()) : NumToAllocate; + + if (NumToAllocate != Max()) + { + Memory::Destruct(Storage.GetPointer(), Num()); + Storage.GetAllocator().Deallocate(Storage.GetPointer()); + + Storage.GetNum() = InValue.Num(); + Storage.GetMax() = NumToAllocate; + Storage.GetPointer() = Storage.GetAllocator().Allocate(Max()); + + Memory::CopyConstruct(Storage.GetPointer(), InValue.Storage.GetPointer(), Num()); + + return *this; + } + + if (InValue.Num() <= Num()) + { + Memory::CopyAssign(Storage.GetPointer(), InValue.Storage.GetPointer(), InValue.Num()); + Memory::Destruct(Storage.GetPointer() + InValue.Num(), Num() - InValue.Num()); + } + else if (InValue.Num() <= Max()) + { + Memory::CopyAssign(Storage.GetPointer(), InValue.Storage.GetPointer(), Num()); + Memory::CopyConstruct(Storage.GetPointer() + Num(), InValue.Storage.GetPointer() + Num(), InValue.Num() - Num()); + } + else check_no_entry(); + + Storage.GetNum() = InValue.Num(); + + return *this; + } + + /** Move assignment operator. After the move, 'InValue' is guaranteed to be empty. */ + constexpr TArray& operator=(TArray&& InValue) + requires (CMoveConstructible&& CMoveAssignable) + { + if (&InValue == this) UNLIKELY return *this; + + if (InValue.Storage.GetAllocator().IsTransferable(InValue.Storage.GetPointer())) + { + Memory::Destruct(Storage.GetPointer(), Num()); + Storage.GetAllocator().Deallocate(Storage.GetPointer()); + + Storage.GetPointer() = InValue.Storage.GetPointer(); + + InValue.Storage.GetNum() = 0; + InValue.Storage.GetMax() = InValue.Storage.GetAllocator().CalculateSlackReserve(InValue.Num()); + InValue.Storage.GetPointer() = InValue.Storage.GetAllocator().Allocate(InValue.Max()); + + return *this; + } + + size_t NumToAllocate = InValue.Num(); + + NumToAllocate = NumToAllocate > Max() ? Storage.GetAllocator().CalculateSlackGrow(InValue.Num(), Max()) : NumToAllocate; + NumToAllocate = NumToAllocate < Max() ? Storage.GetAllocator().CalculateSlackShrink(InValue.Num(), Max()) : NumToAllocate; + + if (NumToAllocate != Max()) + { + Memory::Destruct(Storage.GetPointer(), Num()); + Storage.GetAllocator().Deallocate(Storage.GetPointer()); + + Storage.GetNum() = InValue.Num(); + Storage.GetMax() = NumToAllocate; + Storage.GetPointer() = Storage.GetAllocator().Allocate(Max()); + + Memory::MoveConstruct(Storage.GetPointer(), InValue.Storage.GetPointer(), Num()); + + InValue.Reset(); + + return *this; + } + + if (InValue.Num() <= Num()) + { + Memory::MoveAssign(Storage.GetPointer(), InValue.Storage.GetPointer(), InValue.Num()); + Memory::Destruct(Storage.GetPointer() + InValue.Num(), Num() - InValue.Num()); + } + else if (InValue.Num() <= Max()) + { + Memory::MoveAssign(Storage.GetPointer(), InValue.Storage.GetPointer(), Num()); + Memory::MoveConstruct(Storage.GetPointer() + Num(), InValue.Storage.GetPointer() + Num(), InValue.Num() - Num()); + } + else check_no_entry(); + + Storage.GetNum() = InValue.Num(); + + InValue.Reset(); + + return *this; + } + + /** Replaces the contents with those identified by initializer list. */ + constexpr TArray& operator=(initializer_list IL) + requires (CCopyConstructible && CCopyAssignable) + { + size_t NumToAllocate = GetNum(IL); + + NumToAllocate = NumToAllocate > Max() ? Storage.GetAllocator().CalculateSlackGrow(GetNum(IL), Max()) : NumToAllocate; + NumToAllocate = NumToAllocate < Max() ? Storage.GetAllocator().CalculateSlackShrink(GetNum(IL), Max()) : NumToAllocate; + + if (NumToAllocate != Max()) + { + Memory::Destruct(Storage.GetPointer(), Num()); + Storage.GetAllocator().Deallocate(Storage.GetPointer()); + + Storage.GetNum() = GetNum(IL); + Storage.GetMax() = NumToAllocate; + Storage.GetPointer() = Storage.GetAllocator().Allocate(Max()); + + Memory::CopyConstruct(Storage.GetPointer(), NAMESPACE_REDCRAFT::GetData(IL), Num()); + + return *this; + } + + if (GetNum(IL) <= Num()) + { + Memory::CopyAssign(Storage.GetPointer(), NAMESPACE_REDCRAFT::GetData(IL), GetNum(IL)); + Memory::Destruct(Storage.GetPointer() + GetNum(IL), Num() - GetNum(IL)); + } + else if (GetNum(IL) <= Max()) + { + Memory::CopyAssign(Storage.GetPointer(), NAMESPACE_REDCRAFT::GetData(IL), Num()); + Memory::CopyConstruct(Storage.GetPointer() + Num(), NAMESPACE_REDCRAFT::GetData(IL) + Num(), GetNum(IL) - Num()); + } + else check_no_entry(); + + Storage.GetNum() = GetNum(IL); + + return *this; + } + + /** Compares the contents of two arrays. */ + NODISCARD friend constexpr bool operator==(const TArray& LHS, const TArray& RHS) requires (CWeaklyEqualityComparable) + { + if (LHS.Num() != RHS.Num()) return false; + + ConstIterator LHSIter = LHS.Begin(); + ConstIterator RHSIter = RHS.Begin(); + + while (LHSIter != LHS.End()) + { + if (*LHSIter != *RHSIter) return false; + + ++LHSIter; + ++RHSIter; + } + + check(RHSIter == RHS.End()); + + return true; + } + + /** Compares the contents of two arrays. */ + NODISCARD friend constexpr auto operator<=>(const TArray& LHS, const TArray& RHS) requires (CSynthThreeWayComparable) + { + using OrderingType = TSynthThreeWayResult; + + if (LHS.Num() < RHS.Num()) return OrderingType::less; + if (LHS.Num() > RHS.Num()) return OrderingType::greater; + + ConstIterator LHSIter = LHS.Begin(); + ConstIterator RHSIter = RHS.Begin(); + + while (LHSIter != LHS.End()) + { + TSynthThreeWayResult Ordering = SynthThreeWayCompare(*LHSIter, *RHSIter); + + if (Ordering != OrderingType::equivalent) return Ordering; + + ++LHSIter; + ++RHSIter; + } + + check(RHSIter == RHS.End()); + + return OrderingType::equivalent; + } + + /** Inserts 'InValue' before 'Iter' in the container. */ + constexpr Iterator Insert(ConstIterator Iter, const ElementType& InValue) + requires (CCopyConstructible && CCopyAssignable && CMoveConstructible && CMoveAssignable) + { + checkf(IsValidIterator(Iter), TEXT("Read access violation. Please check IsValidIterator().")); + + const size_t InsertIndex = Iter - Begin(); + + const size_t NumToAllocate = Num() + 1 > Max() ? Storage.GetAllocator().CalculateSlackGrow(Num() + 1, Max()) : Max(); + + check(NumToAllocate >= Num() + 1); + + if (NumToAllocate != Max()) + { + ElementType* OldAllocation = Storage.GetPointer(); + const size_t NumToDestruct = Num(); + + Storage.GetNum() = Num() + 1; + Storage.GetMax() = NumToAllocate; + Storage.GetPointer() = Storage.GetAllocator().Allocate(Max()); + + Memory::MoveConstruct(Storage.GetPointer(), OldAllocation, InsertIndex); + new (Storage.GetPointer() + InsertIndex) ElementType(InValue); + Memory::MoveConstruct(Storage.GetPointer() + InsertIndex + 1, OldAllocation + InsertIndex, NumToDestruct - InsertIndex); + + Memory::Destruct(OldAllocation, NumToDestruct); + Storage.GetAllocator().Deallocate(OldAllocation); + + return Iterator(this, Storage.GetPointer() + InsertIndex); + } + + if (InsertIndex != Num()) + { + new (Storage.GetPointer() + Num()) ElementType(MoveTemp(Storage.GetPointer()[Num() - 1])); + + for (size_t Index = Num() - 1; Index != InsertIndex; --Index) + { + Storage.GetPointer()[Index] = MoveTemp(Storage.GetPointer()[Index - 1]); + } + + Storage.GetPointer()[InsertIndex] = InValue; + } + else new (Storage.GetPointer() + Num()) ElementType(InValue); + + Storage.GetNum() = Num() + 1; + + return Iterator(this, Storage.GetPointer() + InsertIndex); + } + + /** Inserts 'InValue' before 'Iter' in the container. */ + constexpr Iterator Insert(ConstIterator Iter, ElementType&& InValue) + requires (CMoveConstructible && CMoveAssignable) + { + checkf(IsValidIterator(Iter), TEXT("Read access violation. Please check IsValidIterator().")); + + const size_t InsertIndex = Iter - Begin(); + + const size_t NumToAllocate = Num() + 1 > Max() ? Storage.GetAllocator().CalculateSlackGrow(Num() + 1, Max()) : Max(); + + check(NumToAllocate >= Num() + 1); + + if (NumToAllocate != Max()) + { + ElementType* OldAllocation = Storage.GetPointer(); + const size_t NumToDestruct = Num(); + + Storage.GetNum() = Num() + 1; + Storage.GetMax() = NumToAllocate; + Storage.GetPointer() = Storage.GetAllocator().Allocate(Max()); + + Memory::MoveConstruct(Storage.GetPointer(), OldAllocation, InsertIndex); + new (Storage.GetPointer() + InsertIndex) ElementType(MoveTemp(InValue)); + Memory::MoveConstruct(Storage.GetPointer() + InsertIndex + 1, OldAllocation + InsertIndex, NumToDestruct - InsertIndex); + + Memory::Destruct(OldAllocation, NumToDestruct); + Storage.GetAllocator().Deallocate(OldAllocation); + + return Iterator(this, Storage.GetPointer() + InsertIndex); + } + + if (InsertIndex != Num()) + { + new (Storage.GetPointer() + Num()) ElementType(MoveTemp(Storage.GetPointer()[Num() - 1])); + + for (size_t Index = Num() - 1; Index != InsertIndex; --Index) + { + Storage.GetPointer()[Index] = MoveTemp(Storage.GetPointer()[Index - 1]); + } + + Storage.GetPointer()[InsertIndex] = MoveTemp(InValue); + } + else new (Storage.GetPointer() + Num()) ElementType(MoveTemp(InValue)); + + Storage.GetNum() = Num() + 1; + + return Iterator(this, Storage.GetPointer() + InsertIndex); + } + + /** Inserts 'Count' copies of the 'InValue' before 'Iter' in the container. */ + constexpr Iterator Insert(ConstIterator Iter, size_t Count, const ElementType& InValue) + requires (CCopyConstructible && CCopyAssignable && CMoveConstructible && CMoveAssignable) + { + checkf(IsValidIterator(Iter), TEXT("Read access violation. Please check IsValidIterator().")); + + const size_t InsertIndex = Iter - Begin(); + + if (Count == 0) return Iterator(this, Storage.GetPointer() + InsertIndex); + + const size_t NumToAllocate = Num() + Count > Max() ? Storage.GetAllocator().CalculateSlackGrow(Num() + Count, Max()) : Max(); + + check(NumToAllocate >= Num() + Count); + + if (NumToAllocate != Max()) + { + ElementType* OldAllocation = Storage.GetPointer(); + const size_t NumToDestruct = Num(); + + Storage.GetNum() = Num() + Count; + Storage.GetMax() = NumToAllocate; + Storage.GetPointer() = Storage.GetAllocator().Allocate(Max()); + + Memory::MoveConstruct(Storage.GetPointer(), OldAllocation, InsertIndex); + + for (size_t Index = InsertIndex; Index != InsertIndex + Count; ++Index) + { + new (Storage.GetPointer() + Index) ElementType(InValue); + } + + Memory::MoveConstruct(Storage.GetPointer() + InsertIndex + Count, OldAllocation + InsertIndex, NumToDestruct - InsertIndex); + + Memory::Destruct(OldAllocation, NumToDestruct); + Storage.GetAllocator().Deallocate(OldAllocation); + + return Iterator(this, Storage.GetPointer() + InsertIndex); + } + + /* + * NO(XA) - No Operation + * IA(AB) - Insert Assignment + * IC(BC) - Insert Construction + * MA(CD) - Move Assignment + * MC(DO) - Move Construction + * + * IR(AC) - Insert Range + * UI(UO) - Uninitialized + * + * |X|-------------------| |-UI-|O| + * |X|----|A|-IR-| C|-----------|O| + * |X|-NO-|A|-IA-|BC|-MA-|D|-MC-|O| + * + * |X|-----------------| |-UI-|O| + * |X|----------|A|-IR-| CD|----|O| + * |X|----NO----|A|-IA-|BCD|-MC-|O| + * + * |X|-----------| |-----UI-----|O| + * |X|----|A|----IR-----|C |----|O| + * |X|-NO-|A|-IA-|B|-IC-|CD|-MC-|O| + * + * |X|----------------| |-UI-| O| + * |X|----------------|A |-IR-|C O| + * |X|-------NO-------|AB|-IC-|CDO| + * + * |X|-----------| |----UI----| O| + * |X|----------------|A |-IR-|C O| + * |X|-------NO-------|AB|-IC-|CDO| + */ + + const size_t IndexA = InsertIndex; + const size_t IndexC = InsertIndex + Count; + const size_t IndexB = Num() > IndexA ? (Num() < IndexC ? Num() : IndexC) : IndexA; + const size_t IndexD = Num() > IndexC ? Num() : IndexC; + + size_t TargetIndex = Num() + Count - 1; + + for (; TargetIndex != IndexD - 1; --TargetIndex) + { + new (Storage.GetPointer() + TargetIndex) ElementType(MoveTemp(Storage.GetPointer()[TargetIndex - Count])); + } + + for (; TargetIndex != IndexC - 1; --TargetIndex) + { + Storage.GetPointer()[TargetIndex] = MoveTemp(Storage.GetPointer()[TargetIndex - Count]); + } + + for (; TargetIndex != IndexB - 1; --TargetIndex) + { + new (Storage.GetPointer() + TargetIndex) ElementType(InValue); + } + + for (; TargetIndex != IndexA - 1; --TargetIndex) + { + Storage.GetPointer()[TargetIndex] = InValue; + } + + Storage.GetNum() = Num() + Count; + + return Iterator(this, Storage.GetPointer() + InsertIndex); + } + + /** Inserts elements from initializer list before 'Iter' in the container. */ + constexpr Iterator Insert(ConstIterator Iter, initializer_list IL) + requires (CCopyConstructible && CCopyAssignable && CMoveConstructible && CMoveAssignable) + { + checkf(IsValidIterator(Iter), TEXT("Read access violation. Please check IsValidIterator().")); + + const size_t InsertIndex = Iter - Begin(); + const size_t Count = GetNum(IL); + + if (Count == 0) return Iterator(this, Storage.GetPointer() + InsertIndex); + + const size_t NumToAllocate = Num() + Count > Max() ? Storage.GetAllocator().CalculateSlackGrow(Num() + Count, Max()) : Max(); + + check(NumToAllocate >= Num() + Count); + + if (NumToAllocate != Max()) + { + ElementType* OldAllocation = Storage.GetPointer(); + const size_t NumToDestruct = Num(); + + Storage.GetNum() = Num() + Count; + Storage.GetMax() = NumToAllocate; + Storage.GetPointer() = Storage.GetAllocator().Allocate(Max()); + + Memory::MoveConstruct(Storage.GetPointer(), OldAllocation, InsertIndex); + + for (size_t Index = InsertIndex; Index != InsertIndex + Count; ++Index) + { + new (Storage.GetPointer() + Index) ElementType(NAMESPACE_REDCRAFT::GetData(IL)[Index - InsertIndex]); + } + + Memory::MoveConstruct(Storage.GetPointer() + InsertIndex + Count, OldAllocation + InsertIndex, NumToDestruct - InsertIndex); + + Memory::Destruct(OldAllocation, NumToDestruct); + Storage.GetAllocator().Deallocate(OldAllocation); + + return Iterator(this, Storage.GetPointer() + InsertIndex); + } + + const size_t IndexA = InsertIndex; + const size_t IndexC = InsertIndex + Count; + const size_t IndexB = Num() > IndexA ? (Num() < IndexC ? Num() : IndexC) : IndexA; + const size_t IndexD = Num() > IndexC ? Num() : IndexC; + + size_t TargetIndex = Num() + Count - 1; + + for (; TargetIndex != IndexD - 1; --TargetIndex) + { + new (Storage.GetPointer() + TargetIndex) ElementType(MoveTemp(Storage.GetPointer()[TargetIndex - Count])); + } + + for (; TargetIndex != IndexC - 1; --TargetIndex) + { + Storage.GetPointer()[TargetIndex] = MoveTemp(Storage.GetPointer()[TargetIndex - Count]); + } + + for (; TargetIndex != IndexB - 1; --TargetIndex) + { + new (Storage.GetPointer() + TargetIndex) ElementType(NAMESPACE_REDCRAFT::GetData(IL)[TargetIndex - InsertIndex]); + } + + for (; TargetIndex != IndexA - 1; --TargetIndex) + { + Storage.GetPointer()[TargetIndex] = NAMESPACE_REDCRAFT::GetData(IL)[TargetIndex - InsertIndex]; + } + + Storage.GetNum() = Num() + Count; + + return Iterator(this, Storage.GetPointer() + InsertIndex); + } + + /** Inserts a new element into the container directly before 'Iter'. */ + template requires (CConstructibleFrom && CMoveConstructible && CMoveAssignable) + constexpr Iterator Emplace(ConstIterator Iter, Ts&&... Args) + { + checkf(IsValidIterator(Iter), TEXT("Read access violation. Please check IsValidIterator().")); + + const size_t InsertIndex = Iter - Begin(); + + const size_t NumToAllocate = Num() + 1 > Max() ? Storage.GetAllocator().CalculateSlackGrow(Num() + 1, Max()) : Max(); + + check(NumToAllocate >= Num() + 1); + + if (NumToAllocate != Max()) + { + ElementType* OldAllocation = Storage.GetPointer(); + const size_t NumToDestruct = Num(); + + Storage.GetNum() = Num() + 1; + Storage.GetMax() = NumToAllocate; + Storage.GetPointer() = Storage.GetAllocator().Allocate(Max()); + + Memory::MoveConstruct(Storage.GetPointer(), OldAllocation, InsertIndex); + new (Storage.GetPointer() + InsertIndex) ElementType(Forward(Args)...); + Memory::MoveConstruct(Storage.GetPointer() + InsertIndex + 1, OldAllocation + InsertIndex, NumToDestruct - InsertIndex); + + Memory::Destruct(OldAllocation, NumToDestruct); + Storage.GetAllocator().Deallocate(OldAllocation); + + return Iterator(this, Storage.GetPointer() + InsertIndex); + } + + if (InsertIndex != Num()) + { + new (Storage.GetPointer() + Num()) ElementType(MoveTemp(Storage.GetPointer()[Num() - 1])); + + for (size_t Index = Num() - 1; Index != InsertIndex; --Index) + { + Storage.GetPointer()[Index] = MoveTemp(Storage.GetPointer()[Index - 1]); + } + + Storage.GetPointer()[InsertIndex] = ElementType(Forward(Args)...); + } + else new (Storage.GetPointer() + Num()) ElementType(Forward(Args)...); + + Storage.GetNum() = Num() + 1; + + return Iterator(this, Storage.GetPointer() + InsertIndex); + } + + /** Removes the element at 'Iter' in the container. Without changing the order of elements. */ + FORCEINLINE constexpr Iterator StableErase(ConstIterator Iter, bool bAllowShrinking = true) + requires (CMoveConstructible && CMoveAssignable) + { + checkf(IsValidIterator(Iter) && Iter != End(), TEXT("Read access violation. Please check IsValidIterator().")); + + return StableErase(Iter, Iter + 1, bAllowShrinking); + } + + /** Removes the elements in the range ['FirstIter', 'LastIter') in the container. Without changing the order of elements. */ + constexpr Iterator StableErase(ConstIterator FirstIter, ConstIterator LastIter, bool bAllowShrinking = true) + requires (CMoveConstructible && CMoveAssignable) + { + checkf(IsValidIterator(FirstIter) && IsValidIterator(LastIter) && FirstIter <= LastIter, TEXT("Read access violation. Please check IsValidIterator().")); + + const size_t EraseIndex = FirstIter - Begin(); + const size_t EraseCount = LastIter - FirstIter; + + if (EraseCount == 0) return Iterator(this, Storage.GetPointer() + EraseIndex); + + const size_t NumToAllocate = bAllowShrinking ? Storage.GetAllocator().CalculateSlackShrink(Num() - EraseCount, Max()) : Max(); + + if (NumToAllocate != Max()) + { + ElementType* OldAllocation = Storage.GetPointer(); + const size_t NumToDestruct = Num(); + + Storage.GetNum() = Num() - EraseCount; + Storage.GetMax() = NumToAllocate; + Storage.GetPointer() = Storage.GetAllocator().Allocate(Max()); + + Memory::MoveConstruct(Storage.GetPointer(), OldAllocation, EraseIndex); + Memory::MoveConstruct(Storage.GetPointer() + EraseIndex, OldAllocation + EraseIndex + EraseCount, NumToDestruct - EraseIndex - EraseCount); + + Memory::Destruct(OldAllocation, NumToDestruct); + Storage.GetAllocator().Deallocate(OldAllocation); + + return Iterator(this, Storage.GetPointer() + EraseIndex); + } + + for (size_t Index = EraseIndex + EraseCount; Index != Num(); ++Index) + { + Storage.GetPointer()[Index - EraseCount] = MoveTemp(Storage.GetPointer()[Index]); + } + + Memory::Destruct(Storage.GetPointer() + Num() - EraseCount, EraseCount); + + Storage.GetNum() = Num() - EraseCount; + + return Iterator(this, Storage.GetPointer() + EraseIndex); + } + + /** Removes the element at 'Iter' in the container. But it may change the order of elements. */ + FORCEINLINE constexpr Iterator Erase(ConstIterator Iter, bool bAllowShrinking = true) + requires (CMoveConstructible && CMoveAssignable) + { + checkf(IsValidIterator(Iter) && Iter != End(), TEXT("Read access violation. Please check IsValidIterator().")); + + return Erase(Iter, Iter + 1, bAllowShrinking); + } + + /** Removes the elements in the range ['FirstIter', 'LastIter') in the container. But it may change the order of elements. */ + constexpr Iterator Erase(ConstIterator FirstIter, ConstIterator LastIter, bool bAllowShrinking = true) + requires (CMoveConstructible && CMoveAssignable) + { + checkf(IsValidIterator(FirstIter) && IsValidIterator(LastIter) && FirstIter <= LastIter, TEXT("Read access violation. Please check IsValidIterator().")); + + const size_t EraseIndex = FirstIter - Begin(); + const size_t EraseCount = LastIter - FirstIter; + + if (EraseCount == 0) return Iterator(this, Storage.GetPointer() + EraseIndex); + + const size_t NumToAllocate = bAllowShrinking ? Storage.GetAllocator().CalculateSlackShrink(Num() - EraseCount, Max()) : Max(); + + if (NumToAllocate != Max()) + { + ElementType* OldAllocation = Storage.GetPointer(); + const size_t NumToDestruct = Num(); + + Storage.GetNum() = Num() - EraseCount; + Storage.GetMax() = NumToAllocate; + Storage.GetPointer() = Storage.GetAllocator().Allocate(Max()); + + Memory::MoveConstruct(Storage.GetPointer(), OldAllocation, EraseIndex); + Memory::MoveConstruct(Storage.GetPointer() + EraseIndex, OldAllocation + EraseIndex + EraseCount, NumToDestruct - EraseIndex - EraseCount); + + Memory::Destruct(OldAllocation, NumToDestruct); + Storage.GetAllocator().Deallocate(OldAllocation); + + return Iterator(this, Storage.GetPointer() + EraseIndex); + } + + for (size_t Index = 0; Index != EraseCount; ++Index) + { + if (EraseIndex + Index >= Num() - EraseCount) break; + + Storage.GetPointer()[EraseIndex + Index] = MoveTemp(Storage.GetPointer()[Num() - Index - 1]); + } + + Memory::Destruct(Storage.GetPointer() + Num() - EraseCount, EraseCount); + + Storage.GetNum() = Num() - EraseCount; + + return Iterator(this, Storage.GetPointer() + EraseIndex); + } + + /** Appends the given element value to the end of the container. */ + FORCEINLINE constexpr void PushBack(const ElementType& InValue) + requires (CCopyConstructible && CCopyAssignable && CMoveConstructible && CMoveAssignable) + { + EmplaceBack(InValue); + } + + /** Appends the given element value to the end of the container. */ + FORCEINLINE constexpr void PushBack(ElementType&& InValue) + requires (CMoveConstructible && CMoveAssignable) + { + EmplaceBack(MoveTemp(InValue)); + } + + /** Appends a new element to the end of the container. */ + template requires (CConstructibleFrom && CMoveConstructible && CMoveAssignable) + constexpr ElementType& EmplaceBack(Ts&&... Args) + { + const size_t NumToAllocate = Num() + 1 > Max() ? Storage.GetAllocator().CalculateSlackGrow(Num() + 1, Max()) : Max(); + + check(NumToAllocate >= Num() + 1); + + if (NumToAllocate != Max()) + { + ElementType* OldAllocation = Storage.GetPointer(); + const size_t NumToDestruct = Num(); + + Storage.GetNum() = Num() + 1; + Storage.GetMax() = NumToAllocate; + Storage.GetPointer() = Storage.GetAllocator().Allocate(Max()); + + Memory::MoveConstruct(Storage.GetPointer(), OldAllocation, Num() - 1); + new (Storage.GetPointer() + Num() - 1) ElementType(Forward(Args)...); + + Memory::Destruct(OldAllocation, NumToDestruct); + Storage.GetAllocator().Deallocate(OldAllocation); + + return Storage.GetPointer()[Num() - 1]; + } + + new (Storage.GetPointer() + Num()) ElementType(Forward(Args)...); + + Storage.GetNum() = Num() + 1; + + return Storage.GetPointer()[Num() - 1]; + } + + /** Removes the last element of the container. The array cannot be empty. */ + FORCEINLINE constexpr void PopBack(bool bAllowShrinking = true) + requires (CMoveConstructible && CMoveAssignable) + { + Erase(End() - 1, bAllowShrinking); + } + + /** Resizes the container to contain 'Count' elements. Additional default elements are appended. */ + constexpr void SetNum(size_t Count, bool bAllowShrinking = true) + requires (CDefaultConstructible && CMoveConstructible && CMoveAssignable) + { + size_t NumToAllocate = Count; + + NumToAllocate = NumToAllocate > Max() ? Storage.GetAllocator().CalculateSlackGrow(Count, Max()) : NumToAllocate; + NumToAllocate = NumToAllocate < Max() ? (bAllowShrinking ? Storage.GetAllocator().CalculateSlackShrink(Count, Max()) : Max()) : NumToAllocate; + + if (NumToAllocate != Max()) + { + ElementType* OldAllocation = Storage.GetPointer(); + const size_t NumToDestruct = Num(); + + Storage.GetNum() = Count; + Storage.GetMax() = NumToAllocate; + Storage.GetPointer() = Storage.GetAllocator().Allocate(Max()); + + if (NumToDestruct <= Num()) + { + Memory::MoveConstruct(Storage.GetPointer(), OldAllocation, NumToDestruct); + Memory::DefaultConstruct(Storage.GetPointer() + NumToDestruct, Num() - NumToDestruct); + } + else + { + Memory::MoveConstruct(Storage.GetPointer(), OldAllocation, Num()); + } + + Memory::Destruct(OldAllocation, NumToDestruct); + Storage.GetAllocator().Deallocate(OldAllocation); + + return; + } + + if (Count <= Num()) + { + Memory::Destruct(Storage.GetPointer() + Count, Num() - Count); + } + else if (Count <= Max()) + { + Memory::DefaultConstruct(Storage.GetPointer() + Num(), Count - Num()); + } + else check_no_entry(); + + Storage.GetNum() = Count; + } + + /** Resizes the container to contain 'Count' elements. Additional copies of 'InValue' are appended. */ + constexpr void SetNum(size_t Count, const ElementType& InValue, bool bAllowShrinking = true) + requires (CCopyConstructible && CMoveConstructible && CMoveAssignable) + { + size_t NumToAllocate = Count; + + NumToAllocate = NumToAllocate > Max() ? Storage.GetAllocator().CalculateSlackGrow(Count, Max()) : NumToAllocate; + NumToAllocate = NumToAllocate < Max() ? (bAllowShrinking ? Storage.GetAllocator().CalculateSlackShrink(Count, Max()) : Max()) : NumToAllocate; + + if (NumToAllocate != Max()) + { + ElementType* OldAllocation = Storage.GetPointer(); + const size_t NumToDestruct = Num(); + + Storage.GetNum() = Count; + Storage.GetMax() = NumToAllocate; + Storage.GetPointer() = Storage.GetAllocator().Allocate(Max()); + + if (NumToDestruct <= Num()) + { + Memory::MoveConstruct(Storage.GetPointer(), OldAllocation, NumToDestruct); + + for (size_t Index = NumToDestruct; Index != Num(); ++Index) + { + new (Storage.GetPointer() + Index) ElementType(InValue); + } + } + else + { + Memory::MoveConstruct(Storage.GetPointer(), OldAllocation, Num()); + } + + Memory::Destruct(OldAllocation, NumToDestruct); + Storage.GetAllocator().Deallocate(OldAllocation); + + return; + } + + if (Count <= Num()) + { + Memory::Destruct(Storage.GetPointer() + Count, Num() - Count); + } + else if (Count <= Max()) + { + for (size_t Index = Num(); Index != Count; ++Index) + { + new (Storage.GetPointer() + Index) ElementType(InValue); + } + } + else check_no_entry(); + + Storage.GetNum() = Count; + } + + /** Increase the max capacity of the array to a value that's greater or equal to 'Count'. */ + constexpr void Reserve(size_t Count) + requires (CMoveConstructible && CMoveAssignable) + { + if (Count <= Max()) return; + + const size_t NumToAllocate = Storage.GetAllocator().CalculateSlackReserve(Count); + ElementType* OldAllocation = Storage.GetPointer(); + + check(NumToAllocate > Max()); + + Storage.GetMax() = NumToAllocate; + Storage.GetPointer() = Storage.GetAllocator().Allocate(Max()); + + Memory::MoveConstruct(Storage.GetPointer(), OldAllocation, Num()); + + Memory::Destruct(OldAllocation, Num()); + Storage.GetAllocator().Deallocate(OldAllocation); + } + + /** Requests the removal of unused capacity. */ + constexpr void Shrink() + { + size_t NumToAllocate = Storage.GetAllocator().CalculateSlackReserve(Num()); + + check(NumToAllocate <= Max()); + + if (NumToAllocate == Max()) return; + + ElementType* OldAllocation = Storage.GetPointer(); + + Storage.GetMax() = NumToAllocate; + Storage.GetPointer() = Storage.GetAllocator().Allocate(Max()); + + Memory::MoveConstruct(Storage.GetPointer(), OldAllocation, Num()); + Memory::Destruct(OldAllocation, Num()); + + Storage.GetAllocator().Deallocate(OldAllocation); + } + + /** @return The pointer to the underlying element storage. */ + NODISCARD FORCEINLINE constexpr TObserverPtr< ElementType[]> GetData() { return Storage.GetPointer(); } + NODISCARD FORCEINLINE constexpr TObserverPtr GetData() const { return Storage.GetPointer(); } + + /** @return The iterator to the first or end element. */ + NODISCARD FORCEINLINE constexpr Iterator Begin() { return Iterator(this, Storage.GetPointer()); } + NODISCARD FORCEINLINE constexpr ConstIterator Begin() const { return ConstIterator(this, Storage.GetPointer()); } + NODISCARD FORCEINLINE constexpr Iterator End() { return Iterator(this, Storage.GetPointer() + Num()); } + NODISCARD FORCEINLINE constexpr ConstIterator End() const { return ConstIterator(this, Storage.GetPointer() + Num()); } + + /** @return The number of elements in the container. */ + NODISCARD FORCEINLINE constexpr size_t Num() const { return Storage.GetNum(); } + + /** @return The number of elements that can be held in currently allocated storage. */ + NODISCARD FORCEINLINE constexpr size_t Max() const { return Storage.GetMax(); } + + /** @return true if the container is empty, false otherwise. */ + NODISCARD FORCEINLINE constexpr bool IsEmpty() const { return Num() == 0; } + + /** @return true if the iterator is valid, false otherwise. */ + NODISCARD FORCEINLINE constexpr bool IsValidIterator(ConstIterator Iter) const { return Begin() <= Iter && Iter <= End(); } + + /** @return The reference to the requested element. */ + NODISCARD FORCEINLINE constexpr ElementType& operator[](size_t Index) { checkf(Index < Num(), TEXT("Read access violation. Please check IsValidIterator().")); return Storage.GetPointer()[Index]; } + NODISCARD FORCEINLINE constexpr const ElementType& operator[](size_t Index) const { checkf(Index < Num(), TEXT("Read access violation. Please check IsValidIterator().")); return Storage.GetPointer()[Index]; } + + /** @return The reference to the first or last element. */ + NODISCARD FORCEINLINE constexpr ElementType& Front() { return *Begin(); } + NODISCARD FORCEINLINE constexpr const ElementType& Front() const { return *Begin(); } + NODISCARD FORCEINLINE constexpr ElementType& Back() { return *(End() - 1); } + NODISCARD FORCEINLINE constexpr const ElementType& Back() const { return *(End() - 1); } + + /** Erases all elements from the container. After this call, Num() returns zero. */ + constexpr void Reset() + { + const size_t NumToAllocate = Storage.GetAllocator().CalculateSlackReserve(0); + + if (NumToAllocate != Max()) + { + Memory::Destruct(Storage.GetPointer(), Num()); + Storage.GetAllocator().Deallocate(Storage.GetPointer()); + + Storage.GetNum() = 0; + Storage.GetMax() = Storage.GetAllocator().CalculateSlackReserve(Num()); + Storage.GetPointer() = Storage.GetAllocator().Allocate(Max()); + + return; + } + + Memory::Destruct(Storage.GetPointer(), Num()); + Storage.GetNum() = 0; + } + + /** Overloads the GetTypeHash algorithm for TArray. */ + NODISCARD friend FORCEINLINE constexpr size_t GetTypeHash(const TArray& A) requires (CHashable) + { + size_t Result = 0; + + for (Iterator Iter = Begin(); Iter != End(); ++Iter) + { + HashCombine(Result, GetTypeHash(*Iter)); + } + + return Result; + } + + /** Overloads the Swap algorithm for TArray. */ + friend constexpr void Swap(TArray& A, TArray& B) + requires (CSwappable && CMoveConstructible && CMoveAssignable) + { + const bool bIsTransferable = + A.Storage.GetAllocator().IsTransferable(A.Storage.GetPointer()) && + B.Storage.GetAllocator().IsTransferable(B.Storage.GetPointer()); + + if (bIsTransferable) + { + Swap(A.Storage.GetNum(), B.Storage.GetNum()); + Swap(A.Storage.GetMax(), B.Storage.GetMax()); + Swap(A.Storage.GetPointer(), B.Storage.GetPointer()); + + return; + } + + TArray Temp = MoveTemp(A); + A = MoveTemp(B); + B = MoveTemp(Temp); + } + +public: // STL-like iterators to enable range-based for loop support, should not be directly used. + + NODISCARD FORCEINLINE constexpr Iterator begin() { return Begin(); } + NODISCARD FORCEINLINE constexpr ConstIterator begin() const { return Begin(); } + NODISCARD FORCEINLINE constexpr Iterator end() { return End(); } + NODISCARD FORCEINLINE constexpr ConstIterator end() const { return End(); } + +private: + + NAMESPACE_PRIVATE::TArrayStorage> Storage; + +}; + +NAMESPACE_MODULE_END(Utility) +NAMESPACE_MODULE_END(Redcraft) +NAMESPACE_REDCRAFT_END diff --git a/Redcraft.Utility/Source/Public/Containers/Containers.h b/Redcraft.Utility/Source/Public/Containers/Containers.h new file mode 100644 index 0000000..04c86f8 --- /dev/null +++ b/Redcraft.Utility/Source/Public/Containers/Containers.h @@ -0,0 +1,4 @@ +#pragma once + +#include "CoreTypes.h" +#include "Containers/Array.h" diff --git a/Redcraft.Utility/Source/Public/Testing/ContainersTesting.h b/Redcraft.Utility/Source/Public/Testing/ContainersTesting.h new file mode 100644 index 0000000..d4cef50 --- /dev/null +++ b/Redcraft.Utility/Source/Public/Testing/ContainersTesting.h @@ -0,0 +1,18 @@ +#pragma once + +#include "CoreTypes.h" + +NAMESPACE_REDCRAFT_BEGIN +NAMESPACE_MODULE_BEGIN(Redcraft) +NAMESPACE_MODULE_BEGIN(Utility) + +NAMESPACE_BEGIN(Testing) + +REDCRAFTUTILITY_API void TestContainers(); +REDCRAFTUTILITY_API void TestArray(); + +NAMESPACE_END(Testing) + +NAMESPACE_MODULE_END(Utility) +NAMESPACE_MODULE_END(Redcraft) +NAMESPACE_REDCRAFT_END