|
|
- #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;
- }
- };
- }
|