2023-01-24 10:57:45 +00:00
# pragma once
# include "CoreTypes.h"
# include "Memory/Memory.h"
# include "Templates/Noncopyable.h"
# include "TypeTraits/TypeTraits.h"
# include "Miscellaneous/AssertionMacros.h"
NAMESPACE_REDCRAFT_BEGIN
NAMESPACE_MODULE_BEGIN ( Redcraft )
NAMESPACE_MODULE_BEGIN ( Utility )
struct FAllocatorInterface ;
2023-03-22 14:25:12 +00:00
template < typename T >
2023-03-31 11:25:50 +00:00
concept CAllocatableObject = CObject < T > & & ! CConst < T > & & ! CVolatile < T > & & CDestructible < T > ;
2023-03-22 14:25:12 +00:00
2023-03-04 11:12:47 +00:00
template < typename A , typename T = int >
2023-03-22 14:25:12 +00:00
concept CAllocator = ! CSameAs < A , FAllocatorInterface > & & CAllocatableObject < T >
2023-03-04 11:12:47 +00:00
& & requires ( typename A : : template ForElementType < T > & Allocator , T * InPtr , size_t Num , size_t NumAllocated )
{
{ Allocator . Allocate ( Num ) } - > CSameAs < T * > ;
{ Allocator . Deallocate ( InPtr ) } - > CSameAs < void > ;
{ AsConst ( Allocator ) . IsTransferable ( InPtr ) } - > CBooleanTestable ;
{ AsConst ( Allocator ) . CalculateSlackGrow ( Num , NumAllocated ) } - > CSameAs < size_t > ;
{ AsConst ( Allocator ) . CalculateSlackShrink ( Num , NumAllocated ) } - > CSameAs < size_t > ;
{ AsConst ( Allocator ) . CalculateSlackReserve ( Num ) } - > CSameAs < size_t > ;
} ;
2023-01-24 10:57:45 +00:00
2023-03-22 11:35:42 +00:00
template < typename A , typename T = int >
concept CMultipleAllocator = CAllocator < A , T > & & A : : bSupportsMultipleAllocation ;
2023-01-24 10:57:45 +00:00
/**
* This is the allocator interface , the allocator does not use virtual , this contains the default of
* the allocator interface functions . Unlike std : : allocator , IAllocator should be bound to only a object ,
* such as a container , because there may be side effects between multiple allocations , for example ,
* inline storage cannot be allocated multiple times in TInlineAllocator .
*/
struct FAllocatorInterface
{
2023-03-22 11:35:42 +00:00
/**
* If this flag is false , it is possible to allocate an address that has already been allocated .
* Should be allocated according to the results given by the CalculateSlackReserve ( ) family ,
* without needing to allocate memory of the same size as the allocated memory ,
* this is to support special allocators such as TInlineAllocator .
*/
static constexpr bool bSupportsMultipleAllocation = true ;
2023-03-22 14:25:12 +00:00
template < CAllocatableObject T >
2023-03-04 11:12:47 +00:00
class ForElementType /*: private FSingleton*/
2023-01-24 10:57:45 +00:00
{
2023-02-22 15:33:10 +00:00
public :
2023-03-04 11:12:47 +00:00
ForElementType ( ) = default ;
ForElementType ( const ForElementType & ) = delete ;
ForElementType ( ForElementType & & ) = delete ;
ForElementType & operator = ( const ForElementType & ) = delete ;
ForElementType & operator = ( ForElementType & & ) = delete ;
2023-03-22 11:35:42 +00:00
/** Allocates uninitialized storage. If 'InNum' is zero, return nullptr. */
2023-02-22 15:34:51 +00:00
NODISCARD FORCEINLINE T * Allocate ( size_t InNum ) = delete ;
2023-01-24 10:57:45 +00:00
/** Deallocates storage. */
2023-02-22 15:34:51 +00:00
FORCEINLINE void Deallocate ( T * InPtr ) = delete ;
2023-01-24 10:57:45 +00:00
2023-03-31 11:25:50 +00:00
/** @return true if allocation can be deallocated by another allocator, otherwise false. always return true when bSupportsMultipleAllocation is true. */
2023-02-22 15:34:51 +00:00
NODISCARD FORCEINLINE bool IsTransferable ( T * InPtr ) const { return true ; }
2023-01-24 10:57:45 +00:00
/** Calculates the amount of slack to allocate for an array that has just grown to a given number of elements. */
2023-02-22 15:34:51 +00:00
NODISCARD FORCEINLINE size_t CalculateSlackGrow ( size_t Num , size_t NumAllocated ) const = delete ;
2023-01-24 10:57:45 +00:00
/** Calculates the amount of slack to allocate for an array that has just shrunk to a given number of elements. */
2023-02-22 15:34:51 +00:00
NODISCARD FORCEINLINE size_t CalculateSlackShrink ( size_t Num , size_t NumAllocated ) const = delete ;
2023-01-24 10:57:45 +00:00
/** Calculates the amount of slack to allocate for an array that has just grown or shrunk to a given number of elements. */
2023-02-22 15:34:51 +00:00
NODISCARD FORCEINLINE size_t CalculateSlackReserve ( size_t Num ) const = delete ;
2023-01-24 10:57:45 +00:00
} ;
} ;
2023-03-02 14:51:45 +00:00
# define ALLOCATOR_WRAPPER_BEGIN(Allocator, Type, Name) \
\
2023-03-04 11:12:47 +00:00
struct PREPROCESSOR_JOIN ( F , Name ) /*: private FSingleton*/
2023-03-02 14:51:45 +00:00
# define ALLOCATOR_WRAPPER_END(Allocator, Type, Name) ; \
\
template < typename A , bool = CEmpty < A > & & ! CFinal < A > > \
struct PREPROCESSOR_JOIN ( T , Name ) ; \
\
template < typename A > \
struct PREPROCESSOR_JOIN ( T , Name ) < A , true > : public PREPROCESSOR_JOIN ( F , Name ) , private A \
{ \
NODISCARD FORCEINLINE A & operator * ( ) { return * this ; } \
NODISCARD FORCEINLINE const A & operator * ( ) const { return * this ; } \
NODISCARD FORCEINLINE A * operator - > ( ) { return this ; } \
NODISCARD FORCEINLINE const A * operator - > ( ) const { return this ; } \
} ; \
\
template < typename A > \
struct PREPROCESSOR_JOIN ( T , Name ) < A , false > : public PREPROCESSOR_JOIN ( F , Name ) \
{ \
NODISCARD FORCEINLINE A & operator * ( ) { return AllocatorInstance ; } \
NODISCARD FORCEINLINE const A & operator * ( ) const { return AllocatorInstance ; } \
NODISCARD FORCEINLINE A * operator - > ( ) { return & AllocatorInstance ; } \
NODISCARD FORCEINLINE const A * operator - > ( ) const { return & AllocatorInstance ; } \
\
private : \
\
A AllocatorInstance ; \
\
} ; \
\
PREPROCESSOR_JOIN ( T , Name ) < typename Allocator : : template ForElementType < Type > > Name ;
2023-02-13 10:51:53 +00:00
/** This is heap allocator that calls Memory::Malloc() directly for memory allocation. */
2023-03-04 11:12:47 +00:00
struct FHeapAllocator
2023-02-13 10:51:53 +00:00
{
2023-03-22 11:35:42 +00:00
static constexpr bool bSupportsMultipleAllocation = true ;
2023-03-22 14:25:12 +00:00
template < CAllocatableObject T >
class ForElementType /*: private FSingleton*/
2023-02-13 10:51:53 +00:00
{
2023-02-22 15:33:10 +00:00
public :
2023-03-04 11:12:47 +00:00
ForElementType ( ) = default ;
ForElementType ( const ForElementType & ) = delete ;
ForElementType ( ForElementType & & ) = delete ;
ForElementType & operator = ( const ForElementType & ) = delete ;
ForElementType & operator = ( ForElementType & & ) = delete ;
2023-02-13 10:51:53 +00:00
NODISCARD FORCEINLINE T * Allocate ( size_t InNum )
{
return InNum ! = 0 ? static_cast < T * > ( Memory : : Malloc ( Memory : : QuantizeSize ( InNum * sizeof ( T ) ) , alignof ( T ) ) ) : nullptr ;
}
FORCEINLINE void Deallocate ( T * InPtr )
{
Memory : : Free ( InPtr ) ;
}
2023-03-04 11:12:47 +00:00
NODISCARD FORCEINLINE bool IsTransferable ( T * InPtr ) const { return true ; }
2023-02-22 15:34:51 +00:00
NODISCARD FORCEINLINE size_t CalculateSlackGrow ( size_t Num , size_t NumAllocated ) const
2023-02-13 10:51:53 +00:00
{
const size_t FirstGrow = 4 ;
const size_t ConstantGrow = 16 ;
size_t Result ;
check ( Num > NumAllocated ) ;
Result = ( NumAllocated ! = 0 ) ? ( Num + 3 * Num / 8 + ConstantGrow ) : ( Num > FirstGrow ? Num : FirstGrow ) ;
Result = Memory : : QuantizeSize ( Result * sizeof ( T ) , alignof ( T ) ) / sizeof ( T ) ;
return Result ;
}
2023-02-22 15:34:51 +00:00
NODISCARD FORCEINLINE size_t CalculateSlackShrink ( size_t Num , size_t NumAllocated ) const
2023-02-13 10:51:53 +00:00
{
size_t Result ;
check ( Num < NumAllocated ) ;
const bool bTooManySlackBytes = ( NumAllocated - Num ) * sizeof ( T ) > = 16 * 1024 ;
const bool bTooManySlackElements = 3 * Num < 2 * NumAllocated ;
const bool bNeedToShrink = ( bTooManySlackBytes | | bTooManySlackElements ) & & ( NumAllocated - Num > 64 | | Num = = 0 ) ;
if ( bNeedToShrink )
{
Result = Num ! = 0 ? Memory : : QuantizeSize ( Num * sizeof ( T ) , alignof ( T ) ) / sizeof ( T ) : 0 ;
}
else
{
Result = NumAllocated ;
}
return Result ;
}
2023-02-22 15:34:51 +00:00
NODISCARD FORCEINLINE size_t CalculateSlackReserve ( size_t Num ) const
2023-02-13 10:51:53 +00:00
{
return Num ! = 0 ? Memory : : QuantizeSize ( Num * sizeof ( T ) , alignof ( T ) ) / sizeof ( T ) : 0 ;
}
} ;
} ;
2023-02-22 15:33:10 +00:00
/**
* 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 .
*/
2023-03-22 11:35:42 +00:00
template < size_t NumInline , CAllocator SecondaryAllocator = FHeapAllocator >
2023-03-04 11:12:47 +00:00
struct TInlineAllocator
2023-02-22 15:33:10 +00:00
{
2023-03-22 11:35:42 +00:00
static constexpr bool bSupportsMultipleAllocation = false ;
2023-03-22 14:25:12 +00:00
template < CAllocatableObject T >
class ForElementType /*: private FSingleton*/
2023-02-22 15:33:10 +00:00
{
public :
2023-03-04 11:12:47 +00:00
ForElementType ( ) = default ;
ForElementType ( const ForElementType & ) = delete ;
ForElementType ( ForElementType & & ) = delete ;
ForElementType & operator = ( const ForElementType & ) = delete ;
ForElementType & operator = ( ForElementType & & ) = delete ;
2023-02-22 15:33:10 +00:00
NODISCARD FORCEINLINE T * Allocate ( size_t InNum )
{
if ( InNum = = 0 ) return nullptr ;
check ( InNum > = NumInline ) ;
2023-03-02 14:51:45 +00:00
if ( InNum = = NumInline ) return Impl . GetInline ( ) ;
2023-02-22 15:33:10 +00:00
2023-03-02 14:51:45 +00:00
return Impl - > Allocate ( InNum ) ;
2023-02-22 15:33:10 +00:00
}
FORCEINLINE void Deallocate ( T * InPtr )
{
2023-03-02 14:51:45 +00:00
if ( InPtr = = Impl . GetInline ( ) ) return ;
2023-02-22 15:33:10 +00:00
2023-03-02 14:51:45 +00:00
Impl - > Deallocate ( InPtr ) ;
2023-02-22 15:33:10 +00:00
}
NODISCARD FORCEINLINE bool IsTransferable ( T * InPtr ) const
{
2023-03-02 14:51:45 +00:00
if ( InPtr = = Impl . GetInline ( ) ) return false ;
2023-02-22 15:33:10 +00:00
2023-03-02 14:51:45 +00:00
return Impl - > IsTransferable ( InPtr ) ;
2023-02-22 15:33:10 +00:00
}
2023-02-22 15:34:51 +00:00
NODISCARD FORCEINLINE size_t CalculateSlackGrow ( size_t Num , size_t NumAllocated ) const
2023-02-22 15:33:10 +00:00
{
check ( Num > NumAllocated ) ;
check ( NumAllocated > = NumInline ) ;
if ( Num < = NumInline ) return NumInline ;
2023-03-02 14:51:45 +00:00
return Impl - > CalculateSlackGrow ( Num , NumAllocated < = NumInline ? 0 : NumAllocated ) ;
2023-02-22 15:33:10 +00:00
}
2023-02-22 15:34:51 +00:00
NODISCARD FORCEINLINE size_t CalculateSlackShrink ( size_t Num , size_t NumAllocated ) const
2023-02-22 15:33:10 +00:00
{
check ( Num < NumAllocated ) ;
check ( NumAllocated > = NumInline ) ;
if ( Num < = NumInline ) return NumInline ;
2023-03-02 14:51:45 +00:00
return Impl - > CalculateSlackShrink ( Num , NumAllocated ) ;
2023-02-22 15:33:10 +00:00
}
2023-02-22 15:34:51 +00:00
NODISCARD FORCEINLINE size_t CalculateSlackReserve ( size_t Num ) const
2023-02-22 15:33:10 +00:00
{
if ( Num < = NumInline ) return NumInline ;
2023-03-02 14:51:45 +00:00
return Impl - > CalculateSlackReserve ( Num ) ;
2023-02-22 15:33:10 +00:00
}
private :
2023-03-02 14:51:45 +00:00
ALLOCATOR_WRAPPER_BEGIN ( SecondaryAllocator , T , Impl )
{
TAlignedStorage < sizeof ( T ) , alignof ( T ) > InlineStorage [ NumInline ] ;
2023-02-22 15:33:10 +00:00
2023-03-02 14:51:45 +00:00
NODISCARD FORCEINLINE T * GetInline ( ) { return reinterpret_cast < T * > ( & InlineStorage ) ; }
NODISCARD FORCEINLINE const T * GetInline ( ) const { return reinterpret_cast < const T * > ( & InlineStorage ) ; }
}
ALLOCATOR_WRAPPER_END ( SecondaryAllocator , T , Impl )
2023-02-22 15:33:10 +00:00
} ;
} ;
/** This is a null allocator for which all operations are illegal. */
2023-03-04 11:12:47 +00:00
struct FNullAllocator
2023-02-22 15:33:10 +00:00
{
2023-03-22 11:35:42 +00:00
static constexpr bool bSupportsMultipleAllocation = true ;
2023-03-22 14:25:12 +00:00
template < CAllocatableObject T >
class ForElementType /*: private FSingleton*/
2023-02-22 15:33:10 +00:00
{
public :
2023-03-04 11:12:47 +00:00
ForElementType ( ) = default ;
ForElementType ( const ForElementType & ) = delete ;
ForElementType ( ForElementType & & ) = delete ;
ForElementType & operator = ( const ForElementType & ) = delete ;
ForElementType & operator = ( ForElementType & & ) = delete ;
2023-02-22 15:34:51 +00:00
NODISCARD FORCEINLINE T * Allocate ( size_t InNum ) { check_no_entry ( ) ; return nullptr ; }
2023-02-22 15:33:10 +00:00
2023-02-22 15:34:51 +00:00
FORCEINLINE void Deallocate ( T * InPtr ) { check_no_entry ( ) ; }
2023-02-22 15:33:10 +00:00
2023-02-22 15:34:51 +00:00
NODISCARD FORCEINLINE bool IsTransferable ( T * InPtr ) const { check_no_entry ( ) ; return false ; }
2023-02-22 15:33:10 +00:00
2023-02-22 15:34:51 +00:00
NODISCARD FORCEINLINE size_t CalculateSlackGrow ( size_t Num , size_t NumAllocated ) const { check_no_entry ( ) ; return 0 ; }
2023-02-22 15:33:10 +00:00
2023-02-22 15:34:51 +00:00
NODISCARD FORCEINLINE size_t CalculateSlackShrink ( size_t Num , size_t NumAllocated ) const { check_no_entry ( ) ; return 0 ; }
2023-02-22 15:33:10 +00:00
2023-02-22 15:34:51 +00:00
NODISCARD FORCEINLINE size_t CalculateSlackReserve ( size_t Num ) const { check_no_entry ( ) ; return 0 ; }
2023-02-22 15:33:10 +00:00
} ;
} ;
template < size_t Num >
using TFixedAllocator = TInlineAllocator < Num , FNullAllocator > ;
2023-01-24 10:57:45 +00:00
NAMESPACE_MODULE_END ( Utility )
NAMESPACE_MODULE_END ( Redcraft )
NAMESPACE_REDCRAFT_END