#pragma once
|
|
|
|
#include "gp/functional/bind_front.hpp"
|
|
#include "gp/algorithms/move.hpp"
|
|
#include "gp/containers/buffer.hpp"
|
|
#include "gp/functional/function.hpp"
|
|
|
|
#include <atomic>
|
|
#include <concepts>
|
|
|
|
namespace gp {
|
|
|
|
template<typename T>
|
|
class unique_ptr {
|
|
T* data;
|
|
gp::allocator& owner;
|
|
|
|
void dirty_clear() {
|
|
if(data) {
|
|
data->~T();
|
|
owner.deallocate(data);
|
|
}
|
|
}
|
|
public:
|
|
unique_ptr(T* _data, gp::allocator& _owner)
|
|
: data(_data)
|
|
, owner(_owner)
|
|
{}
|
|
|
|
template<typename ...U>
|
|
static unique_ptr make(gp::allocator& owner, U&&... args) {
|
|
auto ptr = owner.allocate(sizeof(T));
|
|
return unique_ptr(new(ptr) T(gp::forward<U>(args)...), owner);
|
|
}
|
|
|
|
template<typename U>
|
|
unique_ptr<U> cast() {
|
|
auto save = data;
|
|
data = nullptr;
|
|
return unique_ptr<U>(save, owner);
|
|
}
|
|
|
|
T* operator->() {
|
|
return data;
|
|
}
|
|
|
|
T& operator*() {
|
|
return *data;
|
|
}
|
|
|
|
operator bool() {
|
|
return data != nullptr;
|
|
}
|
|
|
|
unique_ptr(unique_ptr&) = delete;
|
|
|
|
unique_ptr(unique_ptr&& oth)
|
|
: data(oth.data)
|
|
, owner(oth.owner)
|
|
{
|
|
oth.data = nullptr;
|
|
}
|
|
|
|
unique_ptr& operator=(unique_ptr&) = delete;
|
|
|
|
unique_ptr& operator=(unique_ptr&& oth) {
|
|
dirty_clear();
|
|
data = oth.data;
|
|
owner = oth.owner;
|
|
}
|
|
|
|
~unique_ptr() {
|
|
dirty_clear();
|
|
}
|
|
};
|
|
|
|
template<typename T>
|
|
class shared_ptr {
|
|
T* data;
|
|
std::atomic_int* refcounter;
|
|
gp::allocator& owner;
|
|
|
|
shared_ptr(T* _data, gp::allocator& _owner)
|
|
: data(_data)
|
|
, refcounter((std::atomic_int*)owner.allocate(sizeof(std::atomic_int)))
|
|
, owner(_owner)
|
|
{
|
|
refcounter->store(1);
|
|
}
|
|
|
|
shared_ptr(T* _data, std::atomic_int* refctr, gp::allocator& _owner)
|
|
: data(_data)
|
|
, refcounter(refctr)
|
|
, owner(_owner)
|
|
{
|
|
refcounter->store(1);
|
|
}
|
|
|
|
void dirty_clear() {
|
|
if(!refcounter) return;
|
|
if(refcounter->fetch_sub(1, std::memory_order::acq_rel) == 0) {
|
|
if(data) {
|
|
data->~T();
|
|
owner.deallocate(refcounter);
|
|
owner.deallocate(data);
|
|
}
|
|
}
|
|
}
|
|
public:
|
|
template<typename ...U>
|
|
static shared_ptr make(gp::allocator& owner, U&&... args) {
|
|
auto ptr = owner.allocate(sizeof(T));
|
|
return shared_ptr(new(ptr) T(gp::forward<U>(args)...), owner);
|
|
}
|
|
|
|
template<typename U>
|
|
shared_ptr<U> cast() {
|
|
return shared_ptr<U>(data, refcounter, owner);
|
|
}
|
|
|
|
T* operator->() {
|
|
return data;
|
|
}
|
|
|
|
T& operator*() {
|
|
return *data;
|
|
}
|
|
|
|
operator bool() {
|
|
return data != nullptr;
|
|
}
|
|
|
|
shared_ptr(shared_ptr& oth) {
|
|
oth.refcounter->fetch_add(1, std::memory_order::acquire);
|
|
|
|
data = oth.data;
|
|
refcounter = oth.refcounter;
|
|
owner = oth.owner;
|
|
}
|
|
|
|
shared_ptr(shared_ptr&& oth)
|
|
: data(oth.data)
|
|
, owner(oth.owner)
|
|
{
|
|
oth.data = nullptr;
|
|
oth.refcounter = nullptr;
|
|
}
|
|
|
|
shared_ptr& operator=(shared_ptr& oth) {
|
|
dirty_clear();
|
|
(*oth.refcounter)++;
|
|
|
|
data = oth.data;
|
|
refcounter = oth.refcounter;
|
|
owner = oth.owner;
|
|
}
|
|
|
|
shared_ptr& operator=(shared_ptr&& oth) {
|
|
dirty_clear();
|
|
data = oth.data;
|
|
owner = oth.owner;
|
|
}
|
|
|
|
~shared_ptr() {
|
|
dirty_clear();
|
|
}
|
|
};
|
|
|
|
template<std::copyable T>
|
|
struct resource_traits {
|
|
using identifier = uint64_t;
|
|
static constexpr bool create_if_not_exist = false;
|
|
|
|
static T* create(const identifier&, gp::allocator&);
|
|
|
|
static T* retrieve(const identifier&, gp::allocator&);
|
|
|
|
static bool imprint(const identifier&, const T& value);
|
|
|
|
static bool annihilate(const identifier&);
|
|
};
|
|
|
|
template<std::copyable T>
|
|
requires std::copyable<typename resource_traits<T>::identifier>
|
|
class resource_ptr
|
|
{
|
|
typename resource_traits<T>::identifier _identifier;
|
|
gp::allocator& _alloc;
|
|
T* value = nullptr;
|
|
|
|
operator T&() {
|
|
if(!value) {
|
|
value = resource_traits<T>::retrieve(_identifier, _alloc);
|
|
}
|
|
if(value) return *value;
|
|
else if constexpr (resource_traits<T>::create_if_not_exist) {
|
|
value = resource_traits<T>::create(_identifier, _alloc);
|
|
if(value) return *value;
|
|
}
|
|
gp_config::assertion(false, "unavailable resource lead to crash");
|
|
}
|
|
|
|
gp::optional<T&> read() {
|
|
if(!value) {
|
|
value = resource_traits<T>::retrieve(_identifier, _alloc);
|
|
}
|
|
if(value) return *value;
|
|
else if constexpr (resource_traits<T>::create_if_not_exist) {
|
|
value = resource_traits<T>::create(_identifier, _alloc);
|
|
if(value) return *value;
|
|
}
|
|
return gp::nullopt;
|
|
}
|
|
|
|
gp::optional<T&> force_read() {
|
|
value = resource_traits<T>::retrieve(_identifier, _alloc);
|
|
if(value) return *value;
|
|
return gp::nullopt;
|
|
}
|
|
|
|
template<typename U>
|
|
requires std::is_base_of_v<T, U>
|
|
gp::optional<T&> operator=(U& new_value) {
|
|
if(resource_traits<T>::imprint(_identifier, new_value)) {
|
|
if(value) {
|
|
value->~T();
|
|
gp_config::assertion(_alloc.deallocate((void*)value), "deallocation failure during exchange");
|
|
}
|
|
value = _alloc.allocate(sizeof(U));
|
|
gp_config::assertion(value != nullptr, "could not allocate during resource transcription");
|
|
new(value) U(new_value);
|
|
return *value;
|
|
}
|
|
return gp::nullopt;
|
|
}
|
|
|
|
template<typename U>
|
|
requires std::is_base_of_v<T, U>
|
|
gp::optional<T&> operator=(const U& new_value) {
|
|
if(resource_traits<T>::imprint(_identifier, new_value)) {
|
|
if(value) {
|
|
value->~T();
|
|
gp_config::assertion(_alloc.deallocate((void*)value), "deallocation failure during exchange");
|
|
}
|
|
value = _alloc.allocate(sizeof(U));
|
|
gp_config::assertion(value != nullptr, "could not allocate during resource transcription");
|
|
new(value) U(new_value);
|
|
return *value;
|
|
}
|
|
return gp::nullopt;
|
|
}
|
|
|
|
|
|
template<>
|
|
gp::optional<T&> operator=<gp::nullopt_t>(const gp::nullopt_t) {
|
|
if(resource_traits<T>::annihilate(_identifier)) {
|
|
if(value) {
|
|
value->~T();
|
|
gp_config::assertion(_alloc.deallocate((void*)value), "deallocation failure during exchange");
|
|
}
|
|
value = nullptr;
|
|
}
|
|
return gp::nullopt;
|
|
}
|
|
};
|
|
}
|