feat(memory): add TInlineAllocator and the corresponding testing
This commit is contained in:
parent
bc3cc3d2cc
commit
e8c9f9cc23
@ -14,35 +14,38 @@ void TestContainers()
|
|||||||
TestArray();
|
TestArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
void TestArray()
|
NAMESPACE_UNNAMED_BEGIN
|
||||||
|
|
||||||
|
template <typename Allocator, size_t Capacity>
|
||||||
|
void TestArrayTemplate()
|
||||||
{
|
{
|
||||||
{
|
{
|
||||||
TArray<int32> ArrayA;
|
TArray<int32, Allocator> ArrayA;
|
||||||
TArray<int32> ArrayB(4);
|
TArray<int32, Allocator> ArrayB(4);
|
||||||
TArray<int32> ArrayC(4, 4);
|
TArray<int32, Allocator> ArrayC(4, 4);
|
||||||
TArray<int32> ArrayD(ArrayC);
|
TArray<int32, Allocator> ArrayD(ArrayC);
|
||||||
TArray<int32> ArrayE(MoveTemp(ArrayB));
|
TArray<int32, Allocator> ArrayE(MoveTemp(ArrayB));
|
||||||
TArray<int32> ArrayF({ 0, 1, 2, 3 });
|
TArray<int32, Allocator> ArrayF({ 0, 1, 2, 3 });
|
||||||
|
|
||||||
TArray<int32> ArrayG;
|
TArray<int32, Allocator> ArrayG;
|
||||||
TArray<int32> ArrayH;
|
TArray<int32, Allocator> ArrayH;
|
||||||
TArray<int32> ArrayI;
|
TArray<int32, Allocator> ArrayI;
|
||||||
|
|
||||||
ArrayG = ArrayD;
|
ArrayG = ArrayD;
|
||||||
ArrayH = MoveTemp(ArrayE);
|
ArrayH = MoveTemp(ArrayE);
|
||||||
ArrayI = { 0, 1, 2, 3 };
|
ArrayI = { 0, 1, 2, 3 };
|
||||||
|
|
||||||
always_check((ArrayC == TArray<int32>({ 4, 4, 4, 4 })));
|
always_check((ArrayC == TArray<int32, Allocator>({ 4, 4, 4, 4 })));
|
||||||
always_check((ArrayD == TArray<int32>({ 4, 4, 4, 4 })));
|
always_check((ArrayD == TArray<int32, Allocator>({ 4, 4, 4, 4 })));
|
||||||
always_check((ArrayG == TArray<int32>({ 4, 4, 4, 4 })));
|
always_check((ArrayG == TArray<int32, Allocator>({ 4, 4, 4, 4 })));
|
||||||
always_check((ArrayF == TArray<int32>({ 0, 1, 2, 3 })));
|
always_check((ArrayF == TArray<int32, Allocator>({ 0, 1, 2, 3 })));
|
||||||
always_check((ArrayI == TArray<int32>({ 0, 1, 2, 3 })));
|
always_check((ArrayI == TArray<int32, Allocator>({ 0, 1, 2, 3 })));
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
TArray<int32> ArrayA = { 1, 2, 3 };
|
TArray<int32, Allocator> ArrayA = { 1, 2, 3 };
|
||||||
TArray<int32> ArrayB = { 7, 8, 9, 10 };
|
TArray<int32, Allocator> ArrayB = { 7, 8, 9, 10 };
|
||||||
TArray<int32> ArrayC = { 1, 2, 3 };
|
TArray<int32, Allocator> ArrayC = { 1, 2, 3 };
|
||||||
|
|
||||||
always_check((!(ArrayA == ArrayB)));
|
always_check((!(ArrayA == ArrayB)));
|
||||||
always_check(( (ArrayA != ArrayB)));
|
always_check(( (ArrayA != ArrayB)));
|
||||||
@ -60,25 +63,25 @@ void TestArray()
|
|||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
TArray<int32> Array = { 1, 2, 3 };
|
TArray<int32, Allocator> Array = { 1, 2, 3 };
|
||||||
|
|
||||||
Array.Insert(Array.Begin() + 1, 2);
|
Array.Insert(Array.Begin() + 1, 2);
|
||||||
always_check((Array == TArray<int32>({ 1, 2, 2, 3 })));
|
always_check((Array == TArray<int32, Allocator>({ 1, 2, 2, 3 })));
|
||||||
|
|
||||||
Array.Insert(Array.End(), 2, 4);
|
Array.Insert(Array.End(), 2, 4);
|
||||||
always_check((Array == TArray<int32>({ 1, 2, 2, 3, 4, 4 })));
|
always_check((Array == TArray<int32, Allocator>({ 1, 2, 2, 3, 4, 4 })));
|
||||||
|
|
||||||
Array.Insert(Array.Begin(), { 1, 1, 4, 5, 1, 4 });
|
Array.Insert(Array.Begin(), { 1, 1, 4, 5, 1, 4 });
|
||||||
always_check((Array == TArray<int32>({ 1, 1, 4, 5, 1, 4, 1, 2, 2, 3, 4, 4 })));
|
always_check((Array == TArray<int32, Allocator>({ 1, 1, 4, 5, 1, 4, 1, 2, 2, 3, 4, 4 })));
|
||||||
|
|
||||||
Array.Emplace(Array.End(), 5);
|
Array.Emplace(Array.End(), 5);
|
||||||
always_check((Array == TArray<int32>({ 1, 1, 4, 5, 1, 4, 1, 2, 2, 3, 4, 4, 5 })));
|
always_check((Array == TArray<int32, Allocator>({ 1, 1, 4, 5, 1, 4, 1, 2, 2, 3, 4, 4, 5 })));
|
||||||
|
|
||||||
Array.StableErase(Array.End() - 1);
|
Array.StableErase(Array.End() - 1);
|
||||||
always_check((Array == TArray<int32>({ 1, 1, 4, 5, 1, 4, 1, 2, 2, 3, 4, 4 })));
|
always_check((Array == TArray<int32, Allocator>({ 1, 1, 4, 5, 1, 4, 1, 2, 2, 3, 4, 4 })));
|
||||||
|
|
||||||
Array.StableErase(Array.End() - 2, Array.End());
|
Array.StableErase(Array.End() - 2, Array.End());
|
||||||
always_check((Array == TArray<int32>({ 1, 1, 4, 5, 1, 4, 1, 2, 2, 3 })));
|
always_check((Array == TArray<int32, Allocator>({ 1, 1, 4, 5, 1, 4, 1, 2, 2, 3 })));
|
||||||
|
|
||||||
Array.Erase(Array.End() - 2);
|
Array.Erase(Array.End() - 2);
|
||||||
always_check((Array.Num() == 9));
|
always_check((Array.Num() == 9));
|
||||||
@ -88,33 +91,43 @@ void TestArray()
|
|||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
TArray<int32> Array = { 1, 2, 3 };
|
TArray<int32, Allocator> Array = { 1, 2, 3 };
|
||||||
|
|
||||||
Array.PushBack(4);
|
Array.PushBack(4);
|
||||||
always_check((Array == TArray<int32>({ 1, 2, 3, 4 })));
|
always_check((Array == TArray<int32, Allocator>({ 1, 2, 3, 4 })));
|
||||||
|
|
||||||
Array.EmplaceBack(5);
|
Array.EmplaceBack(5);
|
||||||
always_check((Array == TArray<int32>({ 1, 2, 3, 4, 5 })));
|
always_check((Array == TArray<int32, Allocator>({ 1, 2, 3, 4, 5 })));
|
||||||
|
|
||||||
Array.EmplaceBack(5) = 6;
|
Array.EmplaceBack(5) = 6;
|
||||||
always_check((Array == TArray<int32>({ 1, 2, 3, 4, 5, 6 })));
|
always_check((Array == TArray<int32, Allocator>({ 1, 2, 3, 4, 5, 6 })));
|
||||||
|
|
||||||
Array.PopBack();
|
Array.PopBack();
|
||||||
always_check((Array == TArray<int32>({ 1, 2, 3, 4, 5 })));
|
always_check((Array == TArray<int32, Allocator>({ 1, 2, 3, 4, 5 })));
|
||||||
|
|
||||||
Array.SetNum(4);
|
Array.SetNum(4);
|
||||||
always_check((Array == TArray<int32>({ 1, 2, 3, 4 })));
|
always_check((Array == TArray<int32, Allocator>({ 1, 2, 3, 4 })));
|
||||||
|
|
||||||
Array.Reserve(64);
|
Array.Reserve(64);
|
||||||
always_check((Array.Num() == 4));
|
always_check((Array.Num() == 4));
|
||||||
always_check((Array.Max() == 64));
|
always_check((Array.Max() == 64 || Array.Max() == Capacity));
|
||||||
|
|
||||||
Array.Shrink();
|
Array.Shrink();
|
||||||
always_check((Array.Num() == 4));
|
always_check((Array.Num() == 4));
|
||||||
always_check((Array.Max() == 4));
|
always_check((Array.Max() == 4 || Array.Max() == Capacity));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
NAMESPACE_UNNAMED_END
|
||||||
|
|
||||||
|
void TestArray()
|
||||||
|
{
|
||||||
|
TestArrayTemplate<FDefaultAllocator, 0>();
|
||||||
|
TestArrayTemplate<FHeapAllocator, 0>();
|
||||||
|
TestArrayTemplate<TInlineAllocator<8>, 8>();
|
||||||
|
TestArrayTemplate<TFixedAllocator<64>, 64>();
|
||||||
|
}
|
||||||
|
|
||||||
NAMESPACE_END(Testing)
|
NAMESPACE_END(Testing)
|
||||||
|
|
||||||
NAMESPACE_MODULE_END(Utility)
|
NAMESPACE_MODULE_END(Utility)
|
||||||
|
@ -24,8 +24,10 @@ concept CInstantiableAllocator = CDerivedFrom<T, FAllocatorInterface> && !CSameA
|
|||||||
struct FAllocatorInterface
|
struct FAllocatorInterface
|
||||||
{
|
{
|
||||||
template <CObject T>
|
template <CObject T>
|
||||||
struct ForElementType : private FSingleton
|
class ForElementType : private FSingleton
|
||||||
{
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Allocates uninitialized storage.
|
* Allocates uninitialized storage.
|
||||||
* Should be allocated according to the results given by the CalculateSlackReserve() family,
|
* Should be allocated according to the results given by the CalculateSlackReserve() family,
|
||||||
@ -33,13 +35,13 @@ struct FAllocatorInterface
|
|||||||
* this is to support special allocators such as TInlineAllocator.
|
* this is to support special allocators such as TInlineAllocator.
|
||||||
* If 'InNum' is zero, return nullptr.
|
* If 'InNum' is zero, return nullptr.
|
||||||
*/
|
*/
|
||||||
NODISCARD FORCEINLINE T* Allocate(size_t InNum) = delete;
|
NODISCARD FORCEINLINE constexpr T* Allocate(size_t InNum) = delete;
|
||||||
|
|
||||||
/** Deallocates storage. */
|
/** Deallocates storage. */
|
||||||
FORCEINLINE void Deallocate(T* InPtr) = delete;
|
FORCEINLINE constexpr void Deallocate(T* InPtr) = delete;
|
||||||
|
|
||||||
/** @return true if allocation can be deallocated by another allocator, otherwise false. */
|
/** @return true if allocation can be deallocated by another allocator, otherwise false. */
|
||||||
NODISCARD FORCEINLINE bool IsTransferable(T* InPtr) { return true; }
|
NODISCARD FORCEINLINE constexpr bool IsTransferable(T* InPtr) const { return true; }
|
||||||
|
|
||||||
/** Calculates the amount of slack to allocate for an array that has just grown to a given number of elements. */
|
/** Calculates the amount of slack to allocate for an array that has just grown to a given number of elements. */
|
||||||
NODISCARD FORCEINLINE constexpr size_t CalculateSlackGrow(size_t Num, size_t NumAllocated) const = delete;
|
NODISCARD FORCEINLINE constexpr size_t CalculateSlackGrow(size_t Num, size_t NumAllocated) const = delete;
|
||||||
@ -57,8 +59,10 @@ struct FAllocatorInterface
|
|||||||
struct FHeapAllocator : public FAllocatorInterface
|
struct FHeapAllocator : public FAllocatorInterface
|
||||||
{
|
{
|
||||||
template <CObject T>
|
template <CObject T>
|
||||||
struct ForElementType : public FAllocatorInterface::ForElementType<T>
|
class ForElementType : public FAllocatorInterface::ForElementType<T>
|
||||||
{
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
NODISCARD FORCEINLINE T* Allocate(size_t InNum)
|
NODISCARD FORCEINLINE T* Allocate(size_t InNum)
|
||||||
{
|
{
|
||||||
return InNum != 0 ? static_cast<T*>(Memory::Malloc(Memory::QuantizeSize(InNum * sizeof(T)), alignof(T))) : nullptr;
|
return InNum != 0 ? static_cast<T*>(Memory::Malloc(Memory::QuantizeSize(InNum * sizeof(T)), alignof(T))) : nullptr;
|
||||||
@ -118,6 +122,105 @@ struct FHeapAllocator : public FAllocatorInterface
|
|||||||
|
|
||||||
using FDefaultAllocator = FHeapAllocator;
|
using FDefaultAllocator = FHeapAllocator;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The inline allocator allocates up to a specified number of elements in the same allocation as the container.
|
||||||
|
* Any allocation needed beyond that causes all data to be moved into an indirect allocation.
|
||||||
|
*/
|
||||||
|
template <size_t NumInline, CInstantiableAllocator SecondaryAllocator = FDefaultAllocator>
|
||||||
|
struct TInlineAllocator : public FAllocatorInterface
|
||||||
|
{
|
||||||
|
template <CObject T>
|
||||||
|
class ForElementType : public FAllocatorInterface::ForElementType<T>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
NODISCARD FORCEINLINE T* Allocate(size_t InNum)
|
||||||
|
{
|
||||||
|
if (InNum == 0) return nullptr;
|
||||||
|
|
||||||
|
check(InNum >= NumInline);
|
||||||
|
|
||||||
|
if (InNum == NumInline) return reinterpret_cast<T*>(&InlineStorage);
|
||||||
|
|
||||||
|
return Secondary.Allocate(InNum);
|
||||||
|
}
|
||||||
|
|
||||||
|
FORCEINLINE void Deallocate(T* InPtr)
|
||||||
|
{
|
||||||
|
if (InPtr == reinterpret_cast<T*>(&InlineStorage)) return;
|
||||||
|
|
||||||
|
Secondary.Deallocate(InPtr);
|
||||||
|
}
|
||||||
|
|
||||||
|
NODISCARD FORCEINLINE bool IsTransferable(T* InPtr) const
|
||||||
|
{
|
||||||
|
if (InPtr == reinterpret_cast<const T*>(&InlineStorage)) return false;
|
||||||
|
|
||||||
|
return Secondary.IsTransferable(InPtr);
|
||||||
|
}
|
||||||
|
|
||||||
|
NODISCARD FORCEINLINE constexpr size_t CalculateSlackGrow(size_t Num, size_t NumAllocated) const
|
||||||
|
{
|
||||||
|
check(Num > NumAllocated);
|
||||||
|
check(NumAllocated >= NumInline);
|
||||||
|
|
||||||
|
if (Num <= NumInline) return NumInline;
|
||||||
|
|
||||||
|
return Secondary.CalculateSlackGrow(Num, NumAllocated <= NumInline ? 0 : NumAllocated);
|
||||||
|
}
|
||||||
|
|
||||||
|
NODISCARD FORCEINLINE constexpr size_t CalculateSlackShrink(size_t Num, size_t NumAllocated) const
|
||||||
|
{
|
||||||
|
check(Num < NumAllocated);
|
||||||
|
check(NumAllocated >= NumInline);
|
||||||
|
|
||||||
|
if (Num <= NumInline) return NumInline;
|
||||||
|
|
||||||
|
return Secondary.CalculateSlackShrink(Num, NumAllocated);
|
||||||
|
}
|
||||||
|
|
||||||
|
NODISCARD FORCEINLINE constexpr size_t CalculateSlackReserve(size_t Num) const
|
||||||
|
{
|
||||||
|
if (Num <= NumInline) return NumInline;
|
||||||
|
|
||||||
|
return Secondary.CalculateSlackReserve(Num);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
TAlignedStorage<sizeof(T), alignof(T)> InlineStorage[NumInline];
|
||||||
|
|
||||||
|
typename SecondaryAllocator::template ForElementType<T> Secondary;
|
||||||
|
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
/** This is a null allocator for which all operations are illegal. */
|
||||||
|
struct FNullAllocator : public FAllocatorInterface
|
||||||
|
{
|
||||||
|
template <CObject T>
|
||||||
|
class ForElementType : public FAllocatorInterface::ForElementType<T>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
NODISCARD FORCEINLINE constexpr T* Allocate(size_t InNum) { check_no_entry(); return nullptr; }
|
||||||
|
|
||||||
|
FORCEINLINE constexpr void Deallocate(T* InPtr) { check_no_entry(); }
|
||||||
|
|
||||||
|
NODISCARD FORCEINLINE constexpr bool IsTransferable(T* InPtr) const { check_no_entry(); return false; }
|
||||||
|
|
||||||
|
NODISCARD FORCEINLINE constexpr size_t CalculateSlackGrow(size_t Num, size_t NumAllocated) const { check_no_entry(); return 0; }
|
||||||
|
|
||||||
|
NODISCARD FORCEINLINE constexpr size_t CalculateSlackShrink(size_t Num, size_t NumAllocated) const { check_no_entry(); return 0; }
|
||||||
|
|
||||||
|
NODISCARD FORCEINLINE constexpr size_t CalculateSlackReserve(size_t Num) const { check_no_entry(); return 0; }
|
||||||
|
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
template <size_t Num>
|
||||||
|
using TFixedAllocator = TInlineAllocator<Num, FNullAllocator>;
|
||||||
|
|
||||||
NAMESPACE_MODULE_END(Utility)
|
NAMESPACE_MODULE_END(Utility)
|
||||||
NAMESPACE_MODULE_END(Redcraft)
|
NAMESPACE_MODULE_END(Redcraft)
|
||||||
NAMESPACE_REDCRAFT_END
|
NAMESPACE_REDCRAFT_END
|
||||||
|
Loading…
Reference in New Issue
Block a user