177 lines
		
	
	
		
			5.2 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			177 lines
		
	
	
		
			5.2 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
 | 
						|
#pragma once
 | 
						|
 | 
						|
#if !defined(MIJIN_MEMORY_DYNAMIC_POINTER_HPP_INCLUDED)
 | 
						|
#define MIJIN_MEMORY_DYNAMIC_POINTER_HPP_INCLUDED 1
 | 
						|
 | 
						|
#include <bit>
 | 
						|
#include <cstdint>
 | 
						|
#include <utility>
 | 
						|
 | 
						|
#include "../internal/common.hpp"
 | 
						|
#include "../memory/memutil.hpp"
 | 
						|
#include "../util/concepts.hpp"
 | 
						|
#include "../util/flag.hpp"
 | 
						|
 | 
						|
namespace mijin
 | 
						|
{
 | 
						|
MIJIN_DEFINE_FLAG(Owning);
 | 
						|
 | 
						|
template<typename T, deleter_type<T> TDeleter = std::default_delete<T>>
 | 
						|
class DynamicPointer
 | 
						|
{
 | 
						|
public:
 | 
						|
    using pointer = T*;
 | 
						|
    using element_type = T;
 | 
						|
    using deleter_t = TDeleter;
 | 
						|
private:
 | 
						|
    std::uintptr_t mData = 0;
 | 
						|
    [[no_unique_address]] TDeleter mDeleter;
 | 
						|
public:
 | 
						|
    constexpr DynamicPointer(std::nullptr_t = nullptr) MIJIN_NOEXCEPT {}
 | 
						|
    DynamicPointer(const DynamicPointer&) = delete;
 | 
						|
    constexpr DynamicPointer(pointer ptr, Owning owning, TDeleter deleter = {}) MIJIN_NOEXCEPT_IF(std::is_nothrow_move_constructible_v<TDeleter>)
 | 
						|
        : mData(std::bit_cast<std::uintptr_t>(ptr) | (owning ? 1 : 0)), mDeleter(std::move(deleter))
 | 
						|
    {
 | 
						|
        MIJIN_ASSERT((std::bit_cast<std::uintptr_t>(ptr) & 1) == 0, "Invalid address, DynamicPointer requires addresses to be divisible by two.");
 | 
						|
    }
 | 
						|
    template<typename TOther, typename TOtherDeleter> requires (std::is_constructible_v<TDeleter, TOtherDeleter&&>)
 | 
						|
    constexpr DynamicPointer(DynamicPointer<TOther, TOtherDeleter>&& other) MIJIN_NOEXCEPT_IF((std::is_nothrow_convertible_v<TOtherDeleter, TDeleter>))
 | 
						|
        : mData(std::exchange(other.mData, 0)), mDeleter(std::move(other.mDeleter)) {
 | 
						|
        MIJIN_ASSERT(other.mData == 0, "");
 | 
						|
    }
 | 
						|
    constexpr ~DynamicPointer() noexcept
 | 
						|
    {
 | 
						|
        reset();
 | 
						|
    }
 | 
						|
 | 
						|
    DynamicPointer& operator=(const DynamicPointer&) = delete;
 | 
						|
    DynamicPointer& operator=(std::nullptr_t) MIJIN_NOEXCEPT
 | 
						|
    {
 | 
						|
        reset();
 | 
						|
        return *this;
 | 
						|
    }
 | 
						|
 | 
						|
    template<typename TOther, typename TOtherDeleter> requires(std::is_assignable_v<TDeleter, TOtherDeleter>)
 | 
						|
    DynamicPointer& operator=(DynamicPointer<TOther, TOtherDeleter>&& other) MIJIN_NOEXCEPT_IF((std::is_nothrow_assignable_v<TDeleter, TOtherDeleter>))
 | 
						|
    {
 | 
						|
        if (this != &other)
 | 
						|
        {
 | 
						|
            reset();
 | 
						|
            mData = std::exchange(other.mData, 0);
 | 
						|
            mDeleter = std::move(other.mDeleter);
 | 
						|
        }
 | 
						|
        return *this;
 | 
						|
    }
 | 
						|
 | 
						|
    template<typename TOther, typename TOtherDeleter> requires(std::equality_comparable_with<T, TOther>)
 | 
						|
    auto operator<=>(const DynamicPointer<TOther, TOtherDeleter>& other) MIJIN_NOEXCEPT
 | 
						|
    {
 | 
						|
        return mData <=> other.mData;
 | 
						|
    }
 | 
						|
 | 
						|
    constexpr bool operator==(std::nullptr_t) const MIJIN_NOEXCEPT
 | 
						|
    {
 | 
						|
        return empty();
 | 
						|
    }
 | 
						|
 | 
						|
    constexpr bool operator!=(std::nullptr_t) const MIJIN_NOEXCEPT
 | 
						|
    {
 | 
						|
        return !empty();
 | 
						|
    }
 | 
						|
 | 
						|
    constexpr operator bool() const MIJIN_NOEXCEPT
 | 
						|
    {
 | 
						|
        return !empty();
 | 
						|
    }
 | 
						|
 | 
						|
    constexpr bool operator!() const MIJIN_NOEXCEPT
 | 
						|
    {
 | 
						|
        return empty();
 | 
						|
    }
 | 
						|
 | 
						|
    constexpr pointer operator->() const MIJIN_NOEXCEPT
 | 
						|
    {
 | 
						|
        return get();
 | 
						|
    }
 | 
						|
 | 
						|
    constexpr element_type& operator*() const MIJIN_NOEXCEPT
 | 
						|
    {
 | 
						|
        return *get();
 | 
						|
    }
 | 
						|
 | 
						|
    [[nodiscard]]
 | 
						|
    constexpr bool isOwning() const MIJIN_NOEXCEPT
 | 
						|
    {
 | 
						|
        return (mData & 1) == 1;
 | 
						|
    }
 | 
						|
 | 
						|
    [[nodiscard]]
 | 
						|
    constexpr bool empty() const MIJIN_NOEXCEPT
 | 
						|
    {
 | 
						|
        return mData == 0;
 | 
						|
    }
 | 
						|
 | 
						|
    [[nodiscard]]
 | 
						|
    constexpr pointer get() const MIJIN_NOEXCEPT
 | 
						|
    {
 | 
						|
        return std::bit_cast<pointer>(mData & ~1);
 | 
						|
    }
 | 
						|
 | 
						|
    constexpr void reset(pointer ptr, Owning owning) MIJIN_NOEXCEPT
 | 
						|
    {
 | 
						|
        if (isOwning())
 | 
						|
        {
 | 
						|
            mDeleter(get());
 | 
						|
        }
 | 
						|
        mData = std::bit_cast<std::uintptr_t>(ptr) | (owning ? 1 : 0);
 | 
						|
    }
 | 
						|
 | 
						|
    constexpr void reset() MIJIN_NOEXCEPT
 | 
						|
    {
 | 
						|
        reset(nullptr, Owning::NO);
 | 
						|
    }
 | 
						|
 | 
						|
    [[nodiscard]]
 | 
						|
    pointer release() MIJIN_NOEXCEPT
 | 
						|
    {
 | 
						|
        return std::bit_cast<pointer>(std::exchange(mData, 0) & ~1);
 | 
						|
    }
 | 
						|
 | 
						|
    template<typename TOther, deleter_type<TOther> TOtherDeleter>
 | 
						|
    friend class DynamicPointer;
 | 
						|
};
 | 
						|
 | 
						|
template<typename T, typename TDeleter>
 | 
						|
bool operator==(std::nullptr_t, const DynamicPointer<T, TDeleter>& pointer) MIJIN_NOEXCEPT
 | 
						|
{
 | 
						|
    return pointer == nullptr;
 | 
						|
}
 | 
						|
 | 
						|
template<typename T, typename TDeleter>
 | 
						|
bool operator!=(std::nullptr_t, const DynamicPointer<T, TDeleter>& pointer) MIJIN_NOEXCEPT
 | 
						|
{
 | 
						|
    return pointer != nullptr;
 | 
						|
}
 | 
						|
 | 
						|
template<typename T, typename... TArgs>
 | 
						|
DynamicPointer<T, std::default_delete<T>> makeDynamic(TArgs&&... args) MIJIN_NOEXCEPT_IF((std::is_nothrow_constructible_v<T, TArgs...>))
 | 
						|
{
 | 
						|
    return DynamicPointer<T, std::default_delete<T>>(new T(std::forward<TArgs>(args)...), Owning::YES);
 | 
						|
}
 | 
						|
 | 
						|
template<typename T, allocator_type_for<T> TAllocator, typename... TArgs>
 | 
						|
DynamicPointer<T, AllocatorDeleter<TAllocator>> makeDynamicWithAllocator(TAllocator allocator, TArgs&&... args)
 | 
						|
        MIJIN_NOEXCEPT_IF((std::is_nothrow_constructible_v<T, TArgs...> && std::is_nothrow_move_constructible_v<TAllocator>))
 | 
						|
{
 | 
						|
    T* obj = allocator.allocate(1);
 | 
						|
    if (obj != nullptr)
 | 
						|
    {
 | 
						|
        ::new(obj) T(std::forward<TArgs>(args)...);
 | 
						|
    }
 | 
						|
    return DynamicPointer<T, AllocatorDeleter<TAllocator>>(obj, Owning::YES, AllocatorDeleter<TAllocator>(std::move(allocator)));
 | 
						|
}
 | 
						|
} // namespace mijin
 | 
						|
 | 
						|
#endif // !defined(MIJIN_MEMORY_DYNAMIC_POINTER_HPP_INCLUDED)
 |