/* * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * Copyright 2018-2020, Danny Robson */ #pragma once #include "traits.hpp" #include "../buffer/traits.hpp" #include "../concepts.hpp" #include "../std.hpp" #include "../view.hpp" #include #include namespace cruft::alloc::easy { /// Provides an interface suitable for allocating and constructing typed /// objects using a reference to an existing allocator. template class passthrough { public: explicit passthrough (AllocatorT &_allocator) : m_allocator (_allocator) { ; } /// Allocate, construct, and return a type `U` using memory from the /// underlying allocator with the default alignment. template U* acquire (Args&&...args)& { auto memory = m_allocator.allocate (sizeof (U), alignof (U)); try { return new (memory.data ()) U (std::forward (args)...); } catch (...) { m_allocator.deallocate (memory.data (), sizeof (U), alignof (U)); throw; } } /// Destruct and deallocate an object which has previously been /// allocated through this interface. template void release (U *ptr) { ptr->~U (); m_allocator.deallocate (ptr, sizeof (U), alignof (U)); } /// Create an object of type `U` using `acquire` and wrap the result /// in a std::unique_ptr that will call release on this interface at d /// struction time. template auto unique (Args &&...args)& { struct deleter { passthrough &m_owner; void operator() (U *ptr) { return m_owner.release (ptr); } }; return std::unique_ptr ( acquire (std::forward (args)...), deleter {*this} ); } template decltype (auto) offset (T const *ptr) { return m_allocator.offset (ptr); } decltype(auto) data (void) { return m_allocator.data (); } decltype(auto) begin (void) { return m_allocator.begin (); } decltype(auto) end (void) { return m_allocator.end (); } decltype(auto) remain (void) { return m_allocator.remain (); } decltype(auto) offset (void const *ptr) { return m_allocator.offset (ptr); } private: AllocatorT &m_allocator; }; /// An interfaces that manages an allocator and a backing store, and /// provides a simple interface for constructing arbitrary objects using /// the child objects. template < typename AllocatorT, typename = std::enable_if_t> > class backed { public: template explicit backed (BufferT &_buffer, Args&&...args) : m_allocator (_buffer, std::forward (args)...) { ; } /// Allocate, construct, and return a type `U` using memory from the /// underlying allocator with the default alignment. template U* acquire (Args&&...args)& { auto memory = m_allocator.allocate (sizeof (U), alignof (U)); try { return new (memory.data ()) U (std::forward (args)...); } catch (...) { m_allocator.deallocate (memory.data (), memory.size (), alignof (U)); throw; } } /// Destruct and deallocate an object which has previously been /// allocated through this interface. template void release (U *ptr) { ptr->~U (); m_allocator.deallocate ( reinterpret_cast (ptr), sizeof (U), alignof (U) ); } template cruft::view array (std::size_t count) { auto mem = m_allocator.allocate (count * sizeof (T), alignof (T)).template cast (); try { new (mem.data ()) T[count]; return mem; } catch (...) { m_allocator.deallocate (mem.data (), count * sizeof (T), alignof (T)); throw; } } /// Allocates storage for an array of ValueT, moves the values from /// ContainerT into this storage, and returns a view over the memory. template < concepts::iterable ContainerT > auto array (ContainerT &&container) { using IteratorT = decltype (std::begin (container)); using ValueT = typename std::iterator_traits::value_type; auto store = array (std::size (container)); std::copy ( std::move_iterator (std::begin (container)), std::move_iterator (std::end (container)), std::begin (store) ); return store; } /// Create an object of type `U` using `acquire` and wrap the result /// in a std::unique_ptr that will call release on this interface at d /// struction time. template auto unique (Args &&...args)& { struct deleter { backed &m_owner; void operator() (U *ptr) { return m_owner.release (ptr); } }; return std::unique_ptr ( acquire (std::forward (args)...), deleter {*this} ); } decltype(auto) data (void) { return m_allocator.data (); } decltype(auto) begin (void) { return m_allocator.begin (); } decltype(auto) end (void) { return m_allocator.end (); } decltype(auto) used (void) { return m_allocator.used (); } decltype(auto) remain (void) { return m_allocator.remain (); } decltype(auto) offset (void const *ptr) { return m_allocator.offset (ptr); } template decltype(auto) offset (cruft::view ptr) { return offset (ptr.data ()); } private: AllocatorT m_allocator; }; /// An interfaces that manages an allocator and a backing store, and /// provides a simple interface for constructing arbitrary objects using /// the child objects. template < typename AllocatorT, typename BufferT, typename = std::enable_if_t && is_allocator_v > > class owned { public: explicit owned (BufferT &&_buffer) : m_buffer (std::move (_buffer)) , m_allocator (m_buffer) { ; } /// Allocate, construct, and return a type `U` using memory from the /// underlying allocator with the default alignment. template U* acquire (Args&&...args)& { auto memory = m_allocator.allocate (sizeof (U), alignof (U)); try { return new (memory.data ()) U (std::forward (args)...); } catch (...) { m_allocator.deallocate (memory.data (), memory.size (), alignof (U)); throw; } } /// Destruct and deallocate an object which has previously been /// allocated through this interface. template void release (U *ptr) { ptr->~U (); m_allocator.deallocate ( reinterpret_cast (ptr), sizeof (U), alignof (U) ); } /// Create an object of type `U` using `acquire` and wrap the result /// in a std::unique_ptr that will call release on this interface at d /// struction time. template auto unique (Args &&...args)& { struct deleter { owned &m_owner; void operator() (U *ptr) { return m_owner.release (ptr); } }; return std::unique_ptr ( acquire (std::forward (args)...), deleter {*this} ); } private: BufferT m_buffer; AllocatorT m_allocator; }; };