/* * 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 2015-2018 Danny Robson <danny@nerdcruft.net> */ #ifndef CRUFT_UTIL_ALLOC_ARENA_HPP #define CRUFT_UTIL_ALLOC_ARENA_HPP #include "../memory/deleter.hpp" #include "../cast.hpp" #include "../view.hpp" #include <memory> #include <utility> namespace cruft::alloc { /// wraps a block allocator with an interface suitable for allocating /// individual objects. template <class T> class arena { public: explicit arena (T &store): m_store (store) { ; } //--------------------------------------------------------------------- template <typename U, typename ...Args> U* acquire (Args&&... args) { U *data = m_store.template allocate<U> (1).data (); try { new (data) U (std::forward<Args> (args)...); } catch (...) { m_store.template deallocate<U> ({data,1}); throw; } return data; } //--------------------------------------------------------------------- template <typename U> void release (U *u) { u->~U (); m_store.template deallocate<U> (cruft::view {u,1u}); } //--------------------------------------------------------------------- template <typename U> using deleter_t = cruft::memory::owner_deleter< U,arena<T>,&arena::release >; template <typename U> using unique_t = std::unique_ptr<U,deleter_t<U>>; // the return type must be auto and the implementation must be inline // otherwise we trigger an internal compiler error in gcc-5.2.0 // "sorry, unimplemented: mangling offset_ref" template <typename U, typename ...Args> auto unique (Args&& ...args) { return unique_t<U> { acquire<U> (std::forward<Args> (args)...), deleter_t<U> (*this) }; } private: T &m_store; }; /// A simple allocator that contains a raw allocator and a forwarded /// allocator. /// /// The raw allocator handles the memory allocation, the forwarded /// allocator performs the initialisation, and we control the construction /// of both. /// /// Ideally we wouldn't forward calls manually and instead do something /// like inherit from arena<T>, but that makes it difficult to initialise /// the raw allocator before we have to supply the reference to the arena. template <typename AllocT> class owned { public: template <typename ...ArgsT> explicit owned (ArgsT &&...args) : m_store (std::forward<ArgsT> (args)...) , m_arena {m_store} { ; } owned (owned &&rhs) : m_store (std::move (rhs.m_store)) , m_arena (m_store) { ; } owned& operator= (owned &&rhs) { m_store = std::move (rhs.m_store); } owned (owned const&) = delete; owned& operator= (owned const&) = delete; template <typename T, typename ...ArgsT> decltype(auto) acquire (ArgsT &&...args) { return m_arena.template acquire<T,ArgsT...> (std::forward<ArgsT> (args)...); } template <typename ...ArgsT> decltype(auto) release (ArgsT &&...args) { return m_arena.release (std::forward<ArgsT> (args)...); } template <typename T, typename ...ArgsT> decltype(auto) unique (ArgsT &&...args) { return m_arena.template unique<T,ArgsT...> (std::forward<ArgsT> (args)...); } template <typename ...Args> decltype(auto) reset (Args&&...args) { return m_store.reset (std::forward<Args> (args)...); } auto const& store (void) const& { return m_store; } auto & store (void) & { return m_store; } private: AllocT m_store; arena<AllocT> m_arena; }; } #endif