Created
November 9, 2022 04:28
-
-
Save Hexlord/d4fffec4030ad3fe3b2d6e71cc659d69 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#pragma once | |
#include "Allocator/Allocator.h" | |
#include "Closure/Function.h" | |
#include "TypeMeta/TypeMeta.h" | |
namespace SE { | |
template<typename TAllocator> | |
class TTypeErasedArray; | |
template<Impl::EGlobalAllocatorType AllocatorType> | |
class TTypeErasedArray<TGlobalAllocator<AllocatorType>> { | |
public: | |
using FAllocator = TGlobalAllocator<AllocatorType>; | |
static constexpr uint32 GrowthRatio = 2u; | |
constexpr TTypeErasedArray() = default; | |
TTypeErasedArray(const TTypeErasedArray &Other) { | |
Meta = Other.Meta; | |
Elements = Other ? Alloc(Other.GetSize()) : nullptr; | |
Meta->CopyConstruct(Elements, *Other, Other.GetSize()); | |
Size = Other.GetSize(); | |
MaxSize = Other.GetSize(); | |
} | |
TTypeErasedArray &operator=(const TTypeErasedArray &Other) { | |
Check((void *)&Other != (void *)this); | |
Check(MaxSize == 0 || Meta->Matches(Other.Meta)); | |
if(MaxSize == 0) { | |
Meta = Other.Meta; | |
} | |
EnsureMaxSize(Other.GetSize()); | |
var LowestSize = Math::Min(Size, Other.GetSize()); | |
// Assign old elements to new elements. | |
Meta->CopyAssign(Elements, *Other, LowestSize); | |
// Construct elements above old size and below new size. | |
if(Size < Other.GetSize()) { | |
Meta->CopyConstruct(Offset(Size), Other.Offset(Size), Other.GetSize() - Size); | |
} | |
if(Size > Other.GetSize()) { | |
Meta->Destruct(Offset(Other.GetSize()), Size - Other.GetSize()); | |
} | |
Size = Other.GetSize(); | |
return *this; | |
} | |
TTypeErasedArray(TTypeErasedArray &&Other) { | |
Swap(Elements, Other.Elements); | |
Swap(Size, Other.Size); | |
Swap(MaxSize, Other.MaxSize); | |
Swap(Meta, Other.Meta); | |
if constexpr(FAllocator::MemoryOwnership) { | |
Swap(MemoryOwnership, Other.MemoryOwnership); | |
} | |
} | |
TTypeErasedArray &operator=(TTypeErasedArray &&Other) { | |
Swap(Elements, Other.Elements); | |
Swap(Size, Other.Size); | |
Swap(MaxSize, Other.MaxSize); | |
Swap(Meta, Other.Meta); | |
if constexpr(FAllocator::MemoryOwnership) { | |
Swap(MemoryOwnership, Other.MemoryOwnership); | |
} | |
return *this; | |
} | |
~TTypeErasedArray() { | |
Meta->Destruct(Elements, Size); | |
Free(Elements, MaxSize); | |
} | |
void Setup(const Meta::FTypeMeta *InMeta) { | |
Check(MaxSize == 0); | |
Meta = InMeta; | |
} | |
void *Create() { | |
EnsureMaxSize(Size + 1); | |
var Result = Offset(Size); | |
Meta->Construct(Result); | |
++Size; | |
return Result; | |
} | |
void Resize(uint32 InSize) { | |
EnsureMaxSize(InSize); | |
// Construct elements above old size and below new size. | |
if(Size < InSize) { | |
Meta->Construct(Offset(Size), InSize - Size); | |
} | |
// Destruct elements above new size. | |
if(Size > InSize) { | |
Meta->Destruct(Offset(InSize), Size - InSize); | |
} | |
Size = InSize; | |
} | |
void Resize(uint32 InSize, const void *Other) { | |
EnsureMaxSize(InSize); | |
// Construct elements above old size and below new size. | |
for(var Index = Size; Index < InSize; ++Index) { | |
Meta->CopyConstruct(Offset(Index), Other, InSize - Size); | |
} | |
// Destruct elements above new size. | |
if(Size > InSize) { | |
Meta->Destruct(Offset(InSize), Size - InSize); | |
} | |
Size = InSize; | |
} | |
void Clear() { | |
Meta->Destruct(Elements, Size); | |
Size = 0; | |
} | |
void Shrink() { | |
if(Size < MaxSize) { | |
if(Meta->Reallocable) { | |
if(Size > 0) { | |
Elements = Realloc(Elements, Size, MaxSize); | |
} else { | |
Free(Elements, MaxSize); | |
Elements = nullptr; | |
} | |
} else { | |
if(Size > 0) { | |
typename FAllocator::FMemoryOwnership OldMemoryOwnership; | |
if constexpr(FAllocator ::MemoryOwnership) { | |
Swap(MemoryOwnership, OldMemoryOwnership); | |
} | |
var NewElements = Alloc(Size); | |
Meta->MoveConstruct(NewElements, Elements, Size); | |
Meta->Destruct(Elements, Size); | |
if constexpr(FAllocator ::MemoryOwnership) { | |
Swap(MemoryOwnership, OldMemoryOwnership); | |
} | |
Free(Elements, MaxSize); | |
if constexpr(FAllocator ::MemoryOwnership) { | |
Swap(MemoryOwnership, OldMemoryOwnership); | |
} | |
Elements = NewElements; | |
} else { | |
Meta->Destruct(Elements, Size); | |
Free(Elements, MaxSize); | |
Elements = nullptr; | |
} | |
} | |
MaxSize = Size; | |
} | |
} | |
void *AddCopy(const void *Value) { | |
Checkf(&Value < **this || &Value > Offset(MaxSize), "Adding own element is unsafe due to realloc"); | |
EnsureMaxSize(Size + 1); | |
var Result = Offset(Size); | |
Meta->CopyConstruct(Result, Value, 1); | |
return Result; | |
} | |
void *AddMove(void *Value) { | |
Checkf(&Value < **this || &Value > Offset(MaxSize), "Adding own element is unsafe due to realloc"); | |
EnsureMaxSize(Size + 1); | |
var Result = Offset(Size); | |
Meta->MoveConstruct(Result, Value, 1); | |
return Result; | |
} | |
template<typename T> | |
T &Add(const T &Value) { | |
VerboseCheck(Meta->Matches<T>()); | |
return *(T *)AddCopy(&Value); | |
} | |
template<typename T> | |
T &Add(T &&Value) { | |
VerboseCheck(Meta->Matches<T>()); | |
return *(T *)AddMove(&Value); | |
} | |
void RemoveNoSwap(uint32 InIndex) { | |
Check(Size > InIndex); | |
--Size; | |
Meta->RemoveNoSwap(Offset(InIndex), Size - InIndex); | |
} | |
void RemoveSwap(uint32 Index) { | |
Check(Size > Index); | |
--Size; | |
if(Index != Size) { | |
Meta->MoveAssign(Offset(Index), Offset(Size), 1); | |
} | |
Meta->Destruct(Offset(Size), 1); | |
} | |
void MoveElement(uint32 From, uint32 To) { | |
Check(Size > From); | |
Check(Size > To); | |
Meta->MoveElement(Elements, From, To); | |
} | |
void RemoveLast() { | |
Check(Size > 0); | |
--Size; | |
Meta->Destruct(Offset(Size), 1); | |
} | |
void *operator*() { | |
return Elements; | |
} | |
const void *operator*() const { | |
return Elements; | |
} | |
uint32 GetSize() const { | |
return Size; | |
} | |
bool IsEmpty() const { | |
return Size == 0; | |
} | |
explicit operator bool() const { | |
return !IsEmpty(); | |
} | |
void EnsureMaxSize(uint32 InMaxSize) { | |
Check(Meta); | |
if(MaxSize < InMaxSize) { | |
uint32 NewMaxSize; | |
if(Meta->ElementSize < 1024) { | |
NewMaxSize = Math::Max(MaxSize * GrowthRatio, InMaxSize); | |
} else { | |
NewMaxSize = InMaxSize; | |
} | |
if(Meta->Reallocable) { | |
if(Elements) { | |
Elements = Realloc(Elements, NewMaxSize, MaxSize); | |
} else { | |
Elements = Alloc(NewMaxSize); | |
} | |
} else { | |
if(Elements) { | |
typename FAllocator::FMemoryOwnership OldMemoryOwnership; | |
if constexpr(FAllocator ::MemoryOwnership) { | |
Swap(MemoryOwnership, OldMemoryOwnership); | |
} | |
var NewElements = Alloc(NewMaxSize); | |
Meta->MoveConstruct(NewElements, Elements, Size); | |
Meta->Destruct(Elements, Size); | |
if constexpr(FAllocator ::MemoryOwnership) { | |
Swap(MemoryOwnership, OldMemoryOwnership); | |
} | |
Free(Elements, MaxSize); | |
if constexpr(FAllocator ::MemoryOwnership) { | |
Swap(MemoryOwnership, OldMemoryOwnership); | |
} | |
Elements = NewElements; | |
} else { | |
Elements = Alloc(NewMaxSize); | |
} | |
} | |
MaxSize = NewMaxSize; | |
} | |
} | |
private: | |
void *Elements = nullptr; | |
uint32 Size = 0; | |
uint32 MaxSize = 0; | |
const Meta::FTypeMeta *Meta = nullptr; | |
[[no_unique_address]] typename FAllocator::FMemoryOwnership MemoryOwnership; | |
void *Alloc(uint32 InSize) { | |
void *Result; | |
if constexpr(FAllocator::MemoryOwnership) { | |
Result = FAllocator::Alloc(InSize * Meta->ElementSize, Meta->ElementAlignment, MemoryOwnership); | |
} else { | |
Result = FAllocator::Alloc(InSize * Meta->ElementSize, Meta->ElementAlignment); | |
} | |
return Result; | |
} | |
void *Realloc(void *Ptr, uint32 InSize, uint32 OldSize) { | |
void *Result; | |
if constexpr(FAllocator::MemoryOwnership) { | |
Result = FAllocator::Realloc(Ptr, InSize * Meta->ElementSize, OldSize * Meta->ElementSize, Meta->ElementAlignment, MemoryOwnership); | |
} else { | |
Result = FAllocator::Realloc(Ptr, InSize * Meta->ElementSize, OldSize * Meta->ElementSize, Meta->ElementAlignment); | |
} | |
return Result; | |
} | |
void Free(void *Ptr, uint32 InSize) { | |
if constexpr(FAllocator::MemoryOwnership) { | |
FAllocator::Free(Ptr, InSize * Meta->ElementSize, Meta->ElementAlignment, MemoryOwnership); | |
} else { | |
FAllocator::Free(Ptr, InSize * Meta->ElementSize, Meta->ElementAlignment); | |
} | |
} | |
void *Offset(uint32 Index) const { | |
return (byte *)Elements + Meta->ElementSize * Index; | |
} | |
}; | |
using FHeapTypeErasedArray = TTypeErasedArray<FHeapAllocator>; | |
using FPoolTypeErasedArray = TTypeErasedArray<FPoolAllocator>; | |
using FFrameTypeErasedArray = TTypeErasedArray<FFrameAllocator>; | |
} // namespace SE |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#pragma once | |
#include "Primitives/Primitives.h" | |
#include "Closure/Function.h" | |
#include "Meta/MetaMemory.h" | |
#include <string.h> | |
namespace SE { | |
namespace Meta { | |
namespace Impl { | |
template<typename T> | |
void ConstructImpl(void *Ptr, uint32 Size) { | |
if constexpr (Meta::IsTrivialConstruct<T>) { | |
// Do nothing. | |
} else { | |
var Elements = (T *)Ptr; | |
for(var Index = 0u; Index < Size; ++Index) { | |
Meta::Construct(Elements[Index]); | |
} | |
} | |
} | |
template<typename T> | |
void CopyConstructImpl(void *Ptr, const void *Other, uint32 Size) { | |
if constexpr (Meta::IsTrivialCopy<T>) { | |
memcpy(Ptr, Other, sizeof(T) * Size); | |
} else { | |
var Elements = (T *)Ptr; | |
var OtherElements = (const T *)Other; | |
for(var Index = 0u; Index < Size; ++Index) { | |
Meta::Construct(Elements[Index], OtherElements[Index]); | |
} | |
} | |
} | |
template<typename T> | |
void CopyAssignImpl(void *Ptr, const void *Other, uint32 Size) { | |
if constexpr (Meta::IsTrivialCopy<T>) { | |
memcpy(Ptr, Other, sizeof(T) * Size); | |
} else { | |
var Elements = (T *)Ptr; | |
var OtherElements = (const T *)Other; | |
for(var Index = 0u; Index < Size; ++Index) { | |
Elements[Index] = OtherElements[Index]; | |
} | |
} | |
} | |
template<typename T> | |
void MoveConstructImpl(void *Ptr, void *Other, uint32 Size) { | |
if constexpr (Meta::IsTrivialMove<T>) { | |
memcpy(Ptr, Other, sizeof(T) * Size); | |
} else { | |
var Elements = (T *)Ptr; | |
var OtherElements = (T *)Other; | |
for(var Index = 0u; Index < Size; ++Index) { | |
Meta::Construct(Elements[Index], Move(OtherElements[Index])); | |
} | |
} | |
} | |
template<typename T> | |
void MoveAssignImpl(void *Ptr, void *Other, uint32 Size) { | |
if constexpr (Meta::IsTrivialMove<T>) { | |
memcpy(Ptr, Other, sizeof(T) * Size); | |
} else { | |
var Elements = (T *)Ptr; | |
var OtherElements = (T *)Other; | |
for(var Index = 0u; Index < Size; ++Index) { | |
Elements[Index] = Move(OtherElements[Index]); | |
} | |
} | |
} | |
template<typename T> | |
void RemoveNoSwapImpl(void *Ptr, uint32 Size) { | |
var Elements = (T *)Ptr; | |
if constexpr(Meta::IsTrivialMove<T> && Meta::IsTrivialDestruct<T>) { | |
memmove(Elements, Elements + 1, sizeof(T) * Size); | |
} else { | |
// Move removed element above the size. | |
for(var Index = 0u; Index < Size; ++Index) { | |
Swap(Elements[Index], Elements[Index + 1]); | |
} | |
} | |
if constexpr(!Meta::IsTrivialDestruct<T>) { | |
Meta::Destruct(Elements[Size]); | |
} | |
} | |
template<typename T> | |
void MoveElementImpl(void *Ptr, uint32 From, uint32 To) { | |
if(From > To) { | |
Swap(From, To); | |
} | |
var Elements = (T *)Ptr; | |
if constexpr(Meta::IsTrivialMove<T> && Meta::IsTrivialDestruct<T>) { | |
var Size = To - From; | |
if(Size > 0) { | |
T Temp = Move(Elements[From]); | |
memmove(Elements + From, Elements + From + 1, sizeof(T) * Size); | |
Elements[To] = Move(Temp); | |
} | |
} else { | |
for(var Index = From; Index < To; ++Index) { | |
Swap(Elements[Index], Elements[Index + 1]); | |
} | |
} | |
} | |
template<typename T> | |
void DestructImpl(void *Ptr, uint32 Size) { | |
if constexpr (Meta::IsTrivialConstruct<T>) { | |
// Do nothing. | |
} else { | |
var Elements = (T *)Ptr; | |
for(var Index = 0u; Index < Size; ++Index) { | |
Meta::Destruct(Elements[Index]); | |
} | |
} | |
} | |
} | |
struct FTypeMeta { | |
uint32 ElementSize = 0; | |
uint32 ElementAlignment = 0; | |
bool Reallocable = false; | |
TFunction<void(void *Ptr, uint32 Size)> *Construct = nullptr; | |
TFunction<void(void *Ptr, const void *Other, uint32 Size)> *CopyConstruct = nullptr; | |
TFunction<void(void *Ptr, const void *Other, uint32 Size)> *CopyAssign = nullptr; | |
TFunction<void(void *Ptr, void *Other, uint32 Size)> *MoveConstruct = nullptr; | |
TFunction<void(void *Ptr, void *Other, uint32 Size)> *MoveAssign = nullptr; | |
TFunction<void(void *Ptr, uint32 Size)> *RemoveNoSwap = nullptr; | |
TFunction<void(void *Ptr, uint32 From, uint32 To)> *MoveElement = nullptr; | |
TFunction<void(void *Ptr, uint32 Size)> *Destruct = nullptr; | |
template<typename T> | |
inline constexpr bool Matches() const { | |
return Construct == Impl::ConstructImpl<T>; | |
} | |
inline constexpr bool Matches(const FTypeMeta &Other) const { | |
return Construct == Other.Construct; | |
} | |
}; | |
template<typename T> | |
FTypeMeta TypeMeta() { | |
FTypeMeta Meta; | |
Meta.ElementSize = sizeof(T); | |
Meta.ElementAlignment = alignof(T); | |
Meta.Reallocable = Meta::IsTrivialMove<T> && Meta::IsTrivialDestruct<T>; | |
Meta.Construct = Impl::ConstructImpl<T>; | |
Meta.CopyConstruct = Impl::CopyConstructImpl<T>; | |
Meta.CopyAssign = Impl::CopyAssignImpl<T>; | |
Meta.MoveConstruct = Impl::MoveConstructImpl<T>; | |
Meta.MoveAssign = Impl::MoveAssignImpl<T>; | |
Meta.RemoveNoSwap = Impl::RemoveNoSwapImpl<T>; | |
Meta.MoveElement = Impl::MoveElementImpl<T>; | |
Meta.Destruct = Impl::ConstructImpl<T>; | |
return Meta; | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment