12 template<
typename From,
typename To>
13 concept Castable = std::convertible_to<From, To> && !std::same_as<From, To>;
14 template<
typename From,
typename To>
15 concept PolymorphicConvertible = std::convertible_to<From*, To*> && std::derived_from<From, To> && std::has_virtual_destructor_v<To>;
16 template<
typename From,
typename To>
18 template<
typename T,
typename V>
20 template<
typename T,
typename V>
103 static bool IsShared(
const void* blockptr)
noexcept;
153 concept IsType = !std::is_void_v<remove_all_pointers_t<T>> && !std::is_null_pointer_v<T>;
170 return (
sizeof(T) +
alignof(T) - 1) & ~(
alignof(T) - 1);
174 template<
typename T,
typename F>
176 if constexpr (std::ranges::range<T> && !std::is_same_v<std::decay_t<T>, std::string>) {
177 for (
auto&& elem : container) {
178 Traverse(std::forward<
decltype(elem)>(elem), func);
181 func(std::forward<T>(container));
190 static_assert(!std::is_unbounded_array_v<T>,
"Cannot CoAllocate Unbounded arrays (at least for now, may create a special handle)");
191 constexpr size_t alloc_align = std::max(
alignof(
ControlBlock),
alignof(T));
203 ctr->
raw = ptr + Toffset;
213 template<
typename T,
typename ...Args>
216 new(placement)std::remove_cv_t<T>(std::forward<Args>(args)...);
223 template<
typename T,
typename ...Args>
239 template<
typename T>
requires IsType<T>
241 static_assert(!std::is_reference_v<T>,
"No reference is allowed.");
244 bool HasBeenConverted =
false;
245 const type_info& Converted_Type_Info =
typeid(T);
254 constexpr Shared(T* t)
noexcept {
256 ctr_blk->
raw = (
void*)t;
262 assert(ctr &&
"Attempted to create a Shared with an empty ControlBlock");
265 ctr_blk->
ref_count.fetch_add(1, std::memory_order_release);
271 template<typename ...Args>
272 constexpr Shared(Args&& ...args){
278 if(promote.Expired())
return;
279 this->ctr_blk = promote.ctr_blk;
280 ctr_blk->
ref_count.fetch_add(1, std::memory_order_release);
286 this->ctr_blk = other.ctr_blk;
287 this->ctr_blk->
ref_count.fetch_add(1, std::memory_order_release);
289 if constexpr (!std::is_same_v<std::remove_cv_t<T>, std::remove_cv_t<U>>){
290 HasBeenConverted =
true;
291 Converted_Type_Info =
typeid(U);
298 if(
this == &cpy)
return *
this;
300 this->ctr_blk = cpy.ctr_blk;
303 ctr_blk->
ref_count.fetch_add(1, std::memory_order_acquire);
305 if constexpr (!std::is_same_v<std::remove_cv_t<T>, std::remove_cv_t<U>>){
306 HasBeenConverted =
true;
307 Converted_Type_Info =
typeid(U);
315 std::swap(this->ctr_blk, other->crt_blk);
317 if constexpr (!std::is_same_v<std::remove_cv_t<T>, std::remove_cv_t<U>>){
318 HasBeenConverted =
true;
319 Converted_Type_Info =
typeid(U);
326 if(
this == &mv)
return *
this;
328 std::swap(this->ctr_blk, mv.ctr_blk);
330 if constexpr (!std::is_same_v<std::remove_cv_t<T>, std::remove_cv_t<U>>){
331 HasBeenConverted =
true;
332 Converted_Type_Info =
typeid(U);
345 if (ctr_blk->ref_count.fetch_sub(1, std::memory_order_acq_rel) == 1) {
347 std::destroy_at(std::addressof(t));
349 ctr_blk->raw =
nullptr;
351 if (ctr_blk->weak_count.load(std::memory_order_acq_rel) == 0) {
352 auto loc = ctr_blk->BaseLocation;
353 auto align_val = std::align_val_t{ctr_blk->alloc_align};
354 ctr_blk->~ControlBlock();
355 operator delete(loc, align_val);
362 constexpr T*
get() noexcept {
return ctr_blk ? (T*)ctr_blk->raw :
nullptr;}
363 const T*
get()const noexcept {
return ctr_blk ? (T*)ctr_blk->raw :
nullptr; }
372 return (T*)ctr_blk->raw;
375 return (
const T*)ctr_blk->raw;
381 uint_fast32_t
UseCount() const noexcept {
return ctr_blk ? ctr_blk->ref_count.load(std::memory_order_acquire) : 0; }
391 return ctr_blk && ctr_blk->raw;
396 std::swap(this->ctr_blk, rhs.ctr_blk);
398 if constexpr (!std::is_same_v<std::remove_cv_t<T>, std::remove_cv_t<U>>){
399 HasBeenConverted =
true;
400 Converted_Type_Info =
typeid(U);
420 ctr_blk->weak_count.fetch_add(1, std::memory_order_release);
428 if(ctr_blk->ref_count.load(std::memory_order_acquire) == 0)
return Shared<T>();
433 return ctr_blk && ctr_blk->ref_count.load(std::memory_order_acquire) == 0;
439 if (ctr_blk->weak_count.fetch_sub(1, std::memory_order_relaxed) == 1 &&
440 ctr_blk->ref_count.load(std::memory_order_acquire) == 0) {
441 auto loc = ctr_blk->BaseLocation;
442 auto align_val = std::align_val_t{ctr_blk->alloc_align};
443 ctr_blk->~ControlBlock();
444 operator delete(loc, align_val);
451 return ctr_blk ? ctr_blk->ref_count.load(std::memory_order_acquire) : 0;
454 operator bool()const noexcept{
455 return !
Expired() && ctr_blk->raw;
478 raw =
static_cast<T*
>(other);
484 std::swap(this->raw, other.raw);
488 std::swap(this->raw, mv.raw);
495 if(
this == &mv)
return *
this;
497 std::swap(this->raw, mv.raw);
502 if(
this == &mv)
return *
this;
504 std::swap(this->raw,
static_cast<T*
>(mv.raw));
511 constexpr T*
get() noexcept {
return raw;}
512 constexpr const T*
get()const noexcept {
return raw;}
514 inline void reset(T* ptr =
nullptr) noexcept{
516 if constexpr (std::is_array_v<T>){
526 return std::exchange(raw,
nullptr);
531 std::swap(this->raw,
static_cast<T*
>(rhs.raw));
538 if constexpr (std::is_trivially_copyable_v<T>){
541 memmove(shared.ctr_blk->
raw, raw,
sizeof(T));
542 }
else if constexpr (std::is_move_constructible_v<T>){
544 }
else if constexpr (std::is_copy_constructible_v<T>){
580 constexpr inline const T&
operator*()
const {
return *raw;}
584 return this->raw == rhs.raw;
589 return rhs.raw != this->raw;
592 constexpr inline operator bool()const noexcept{
596 constexpr inline bool operator!() const noexcept {
return !this->raw; }
599 operator T*() =
delete;
600 operator void*() =
delete;
603 template <
typename T>
605 static_assert(!std::is_reference_v<T>,
"Observer<T> cannot be instantiated with a reference type");
611 constexpr
Observer(std::nullptr_t) noexcept : ptr_(
nullptr) {}
613 constexpr explicit Observer(T* ptr) noexcept : ptr_(ptr) {}
642 constexpr void reset(T* ptr =
nullptr) noexcept { ptr_ = ptr; }
643 constexpr void release() noexcept { ptr_ =
nullptr; }
646 constexpr T*
get() const noexcept {
return ptr_; }
648 assert(ptr_ &&
"Observer dereference is null");
653 assert(ptr_ &&
"Observer dereference is null");
657 constexpr explicit operator bool() const noexcept {
return ptr_ !=
nullptr; }
662 template <
typename U>
664 return ptr_ == other.get();
678 template <
typename T>
688template<
typename T,
typename U>
698template<
typename T,
typename U>
static unsigned int TotalArenasAllocated() noexcept
static Arena & CreateArena(size_t size)
Creates a thread-local arena.
static bool IsEngineAllocated(const void *pointer)
static size_t ThreadMemoryFreed() noexcept
static size_t GetMemoryAvailable(const Arena &arena)
static bool IsShared(const void *blockptr) noexcept
static void ExpandInternalArena(size_t width)
Internal use. Arenas are not expandable except the internal arena.
static unsigned int ThreadArenasFreed() noexcept
static unsigned int TotalArenasFreed() noexcept
static unsigned int TotalBlockResizeCount() noexcept
static Block Resize(Block &block, size_t newSize)
Attempts to resize a block, this can move the block to a diffrent location.
static unsigned int ThreadDeallocationCount() noexcept
static bool IsValid(const void *pointer)
static unsigned int ThreadArenasAllocated() noexcept
static unsigned int ThreadBlockResizeCount() noexcept
static Arena & CreateGlobalArena(size_t width)
Allocates a shared arena for multi-threaded use.
static size_t MemoryUsed(const Arena &arena)
static unsigned int TotalDeallocationCount() noexcept
static void Free(Block &buffer)
Frees a buffer.
static void FreeArena(Arena)
Attempts to free an areana, if the arean has any blocks in use this call with throw.
static unsigned int ThreadAllocationCount() noexcept
static SharedBlock AllocShared(size_t size)
Allocates a block with syncing constructs.
static size_t TotalMemoryAllocated() noexcept
static bool IsThreadLocal(const Block &block) noexcept
static size_t ThreadMemoryUsed() noexcept
static size_t TotalMemoryFreed() noexcept
static unsigned int TotalAllocationCount() noexcept
static Block Alloc(size_t bufSize)
Allocates a thread-local block.
static bool IsFree(const Block &block) noexcept
constexpr Observer(const Observer &other) noexcept=default
constexpr Observer & operator=(T *ptr) noexcept
constexpr Observer & operator=(const Observer &other) noexcept=default
constexpr void reset(T *ptr=nullptr) noexcept
constexpr Observer() noexcept=default
constexpr Observer(const Observer< U > &other) noexcept
constexpr bool operator==(const Observer< U > &other) const noexcept
constexpr T * operator->() const
constexpr void release() noexcept
constexpr T * get() const noexcept
constexpr T & operator*() const
constexpr Observer(T *ptr) noexcept
constexpr auto operator<=>(const Observer &) const =default
constexpr Observer & operator=(const Observer< U > &other) noexcept
constexpr Observer(U *ptr) noexcept
The Hubris Engine main namespace.
typename remove_all_pointers< T >::type remove_all_pointers_t
consteval size_t padded_size()
Observer(T *) -> Observer< T >
void Traverse(T &&container, F &&func)
void swap(List< T > &lhs, List< T > &rhs) noexcept
Shared(T *) -> Shared< T >
constexpr void Construct_T_Inplace(void *placement, Args &&...args)
Constructs any object at location (placment new) with the specified arguments.
Observer< T > observer_ptr
constexpr ControlBlock * CoAllocate(Args &&...args)
Allocates and constructs both the ControlBlock and the Object T and returns the instanciated controlb...
constexpr ControlBlock * CoAllocate_Unsafe()
Allocates memory for the ControlBlock and T but does not construct T. This can lead to UB if not used...
Handle(T *) -> Handle< T >
std::atomic_uint32_t ref_count
std::atomic_uint32_t weak_count
constexpr Handle(U *other) noexcept
constexpr T * get() noexcept
constexpr Handle() noexcept=default
constexpr void swap(Handle< U > &rhs) noexcept
constexpr T * release() noexcept
constexpr bool operator!=(const Handle< U > &rhs) const noexcept
constexpr const T * get() const noexcept
constexpr bool operator==(const Handle< U > &rhs) const noexcept
constexpr T & operator*()
Handle & operator=(const Handle &cp)=delete
void reset(T *ptr=nullptr) noexcept
Handle(const Handle &cp) noexcept=delete
constexpr Handle(Handle &&mv) noexcept
Handle & operator=(Handle< U > &&mv) noexcept
constexpr Handle(T *t) noexcept
constexpr Handle(Handle< U > &&other) noexcept
constexpr const T * operator->() const noexcept
constexpr const T & operator*() const
constexpr T * operator->() noexcept
Handle & operator=(Handle &&mv) noexcept
constexpr bool operator!() const noexcept
Shared blocks are thread shared blocks.
constexpr Shared(const Shared< U > &other) noexcept
T * operator->()
Unchecked Operation: This is dangerous if the control block is null. That happens only when the Share...
constexpr Shared(Weak< T > &&promote) noexcept
constexpr T * get() noexcept
Shared & operator=(Shared< U > &&mv) noexcept
constexpr void swap(Shared< U > &rhs) noexcept
bool Constructed() const noexcept
returns true if the control block has been created
uint_fast32_t UseCount() const noexcept
constexpr Shared(Shared< U > &&other) noexcept
Shared & operator=(const Shared< U > &cpy) noexcept
constexpr Shared() noexcept=default
std::remove_cv_t< T > Unqualified
const T * get() const noexcept
const T * operator->() const
const T & operator*() const
constexpr Weak(const Shared< T > &s) noexcept
bool Expired() const noexcept
uint_fast32_t UseCount() const noexcept
Shared< T > Lock() const noexcept
typename remove_all_pointers< T >::type type
typename remove_all_pointers< T >::type type
typename remove_all_pointers< T >::type type
typename remove_all_pointers< T >::type type