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)
 |