diff options
Diffstat (limited to 'src/lib/netlist/plib/palloc.h')
-rw-r--r-- | src/lib/netlist/plib/palloc.h | 729 |
1 files changed, 543 insertions, 186 deletions
diff --git a/src/lib/netlist/plib/palloc.h b/src/lib/netlist/plib/palloc.h index a9b03353b37..1eff726d45d 100644 --- a/src/lib/netlist/plib/palloc.h +++ b/src/lib/netlist/plib/palloc.h @@ -1,19 +1,21 @@ -// license:GPL-2.0+ +// license:BSD-3-Clause // copyright-holders:Couriersud -/* - * palloc.h - * - */ #ifndef PALLOC_H_ #define PALLOC_H_ +/// +/// \file palloc.h +/// + #include "pconfig.h" -#include "pstring.h" +#include "pgsl.h" +#include "pgsl.h" +#include "pmath.h" // FIXME: only uses lcm ... move to ptypes. #include "ptypes.h" +#include <algorithm> #include <cstddef> // for std::max_align_t (usually long long) -//#include <cstdlib> #include <memory> #include <type_traits> #include <utility> @@ -29,36 +31,82 @@ namespace plib { // Standard arena_deleter //============================================================ - template <typename P, typename T> - struct arena_deleter + template <typename P, typename T, std::size_t ALIGN, bool X> + struct arena_deleter_base + { + }; + + struct deleter_info_t + { + std::size_t alignment; + std::size_t size; + }; + + template <typename P, typename T, std::size_t ALIGN> + struct arena_deleter_base<P, T, ALIGN, false> { - //using arena_storage_type = P *; - using arena_storage_type = typename std::conditional<P::is_stateless, P, P *>::type; - template <typename X, typename Y = void> - typename std::enable_if<!X::is_stateless, X&>::type getref(X *x) { return *x;} - template <typename X, typename Y = void *> - typename std::enable_if<std::remove_pointer<X>::type::is_stateless, X&>::type - getref(X &x, Y y = nullptr) + constexpr arena_deleter_base(P *a = nullptr) noexcept + : m_arena(a), m_info({ALIGN ? ALIGN : alignof(T), sizeof(T)} ) { } + + template<typename U, typename = + std::enable_if_t<std::is_convertible_v<U *, T *>>> + constexpr arena_deleter_base(const arena_deleter_base<P, U, ALIGN, false> &rhs) noexcept + : m_arena(rhs.m_arena), m_info(rhs.m_info) { } + + void operator()(T *p) noexcept { - unused_var(y); - return x; + // call destructor + p->~T(); + m_arena->deallocate(p, m_info.alignment, m_info.size); } - constexpr arena_deleter(arena_storage_type a = arena_storage_type()) noexcept - : m_a(a) { } + P *m_arena; + deleter_info_t m_info; + }; - template<typename PU, typename U, typename = typename - std::enable_if<std::is_convertible< U*, T*>::value>::type> - arena_deleter(const arena_deleter<PU, U> &rhs) noexcept : m_a(rhs.m_a) { } + template <typename P, typename T, std::size_t ALIGN> + struct arena_deleter_base<P, T, ALIGN, true> + { + constexpr arena_deleter_base(/*[[maybe_unused]]*/ P *a = nullptr) noexcept + : m_info({ALIGN ? ALIGN : alignof(T), sizeof(T)}) + { + plib::unused_var(a); // GCC 7.x does not like the maybe_unused + } - void operator()(T *p) //const + template<typename U, typename = + std::enable_if_t<std::is_convertible_v<U *, T *>>> + constexpr arena_deleter_base(const arena_deleter_base<P, U, ALIGN, true> &rhs) noexcept + : m_info(rhs.m_info) { - /* call destructor */ + } + + void operator()(T *p) noexcept + { + // call destructor p->~T(); - getref(m_a).deallocate(p); + P::deallocate(p, m_info.alignment, m_info.size); } - //private: - arena_storage_type m_a; + + deleter_info_t m_info; + }; + + + /// + /// \brief alignment aware deleter class + /// + /// The deleter class expects the object to have been allocated with + /// `alignof(T)` alignment if the ALIGN parameter is omitted. If ALIGN is + /// given, this is used. + /// + /// \tparam A Arena type + /// \tparam T Object type + /// \tparam ALIGN alignment + /// + template <typename A, typename T, std::size_t ALIGN = 0> + struct arena_deleter : public arena_deleter_base<A, T, ALIGN, A::has_static_deallocator> + { + using base_type = arena_deleter_base<A, T, ALIGN, A::has_static_deallocator>; + using base_type::base_type; }; //============================================================ @@ -80,27 +128,26 @@ namespace plib { template <typename, typename> friend class owned_ptr; - owned_ptr(pointer p, bool owned) noexcept + owned_ptr(pointer p, bool owned) : m_ptr(p), m_deleter(), m_is_owned(owned) { } - owned_ptr(pointer p, bool owned, D deleter) noexcept - : m_ptr(p), m_deleter(deleter), m_is_owned(owned) + owned_ptr(pointer p, bool owned, D deleter) + : m_ptr(p), m_deleter(std::move(deleter)), m_is_owned(owned) { } owned_ptr(const owned_ptr &r) = delete; owned_ptr & operator =(owned_ptr &r) = delete; - template<typename DC, typename DC_D> - owned_ptr & operator =(owned_ptr<DC, DC_D> &&r) + template <typename DC, typename DC_D> + owned_ptr & operator =(owned_ptr<DC, DC_D> &&r) noexcept { if (m_is_owned && (m_ptr != nullptr)) - //delete m_ptr; m_deleter(m_ptr); m_is_owned = r.m_is_owned; m_ptr = r.m_ptr; - m_deleter = r.m_deleter; + m_deleter = std::move(r.m_deleter); r.m_is_owned = false; r.m_ptr = nullptr; return *this; @@ -108,7 +155,7 @@ namespace plib { owned_ptr(owned_ptr &&r) noexcept : m_ptr(r.m_ptr) - , m_deleter(r.m_deleter) + , m_deleter(std::move(r.m_deleter)) , m_is_owned(r.m_is_owned) { r.m_is_owned = false; @@ -118,7 +165,6 @@ namespace plib { owned_ptr &operator=(owned_ptr &&r) noexcept { if (m_is_owned && (m_ptr != nullptr)) - //delete m_ptr; m_deleter(m_ptr); m_is_owned = r.m_is_owned; m_ptr = r.m_ptr; @@ -131,7 +177,7 @@ namespace plib { template<typename DC, typename DC_D> owned_ptr(owned_ptr<DC, DC_D> &&r) noexcept : m_ptr(static_cast<pointer >(r.get())) - , m_deleter(r.m_deleter) + , m_deleter(std::move(r.m_deleter)) , m_is_owned(r.is_owned()) { r.release(); @@ -148,9 +194,9 @@ namespace plib { m_ptr = nullptr; } - /** - * \brief Return @c true if the stored pointer is not null. - */ + /// + /// \brief Return \c true if the stored pointer is not null. + /// explicit operator bool() const noexcept { return m_ptr != nullptr; } pointer release() @@ -180,73 +226,100 @@ namespace plib { // Arena allocator for use with containers //============================================================ - template <class ARENA, class T, std::size_t ALIGN = alignof(T)> + template <class ARENA, class T, std::size_t ALIGN, bool HSA> class arena_allocator { public: using value_type = T; - static constexpr const std::size_t align_size = ALIGN; + using pointer = T *; + static constexpr const std::size_t align_size = ALIGN ? ALIGN : alignof(T); using arena_type = ARENA; - static_assert(align_size >= alignof(T) && (align_size % alignof(T)) == 0, + static_assert((align_size % alignof(T)) == 0, "ALIGN must be greater than alignof(T) and a multiple"); + template <typename U = int, typename = std::enable_if_t<HSA && sizeof(U)>> + //[[deprecated]] arena_allocator() noexcept : m_a(arena_type::instance()) { } ~arena_allocator() noexcept = default; - arena_allocator(const arena_allocator &rhs) noexcept = default; - arena_allocator& operator=(const arena_allocator&) noexcept = delete; + arena_allocator(const arena_allocator &) = default; + arena_allocator &operator=(const arena_allocator &) = default; + arena_allocator(arena_allocator &&) noexcept = default; + arena_allocator &operator=(arena_allocator &&) noexcept = default; - arena_allocator(arena_allocator&&) noexcept = default; - arena_allocator& operator=(arena_allocator&&) = delete; + template <typename U = int> + arena_allocator(/*[[maybe_unused]]*/ std::enable_if_t<HSA && sizeof(U), arena_type> & a) noexcept + : m_a(arena_type::instance()) + { + plib::unused_var(a); // GCC 7.x does not like the maybe_unused + } - arena_allocator(arena_type & a) noexcept : m_a(a) + template <typename U = int> + arena_allocator(/*[[maybe_unused]]*/ std::enable_if_t<!HSA && sizeof(U), arena_type> & a) noexcept + : m_a(a) { + plib::unused_var(a); // GCC 7.x does not like the maybe_unused } - template <class U> - arena_allocator(const arena_allocator<ARENA, U, ALIGN>& rhs) noexcept + template <class U, typename = std::enable_if_t<!std::is_same_v<T, U>>> + arena_allocator(const arena_allocator<ARENA, U, ALIGN, HSA>& rhs) noexcept : m_a(rhs.m_a) { } - template <class U> struct rebind + template <class U> + struct rebind { - using other = arena_allocator<ARENA, U, ALIGN>; + using other = arena_allocator<ARENA, U, ALIGN, HSA>; }; - T* allocate(std::size_t n) + pointer allocate(std::size_t n) + { + return reinterpret_cast<T *>(m_a.allocate(align_size, sizeof(T) * n)); //NOLINT + } + + void deallocate(pointer p, std::size_t n) noexcept + { + m_a.deallocate(p, align_size, sizeof(T) * n); + } + + template<typename U, typename... Args> + void construct(U* p, Args&&... args) { - return reinterpret_cast<T *>(m_a.allocate(ALIGN, sizeof(T) * n)); + // NOLINTNEXTLINE(cppcoreguidelines-owning-memory) + ::new (void_ptr_cast(p)) U(std::forward<Args>(args)...); } - void deallocate(T* p, std::size_t n) noexcept + template<typename U> + void destroy(U* p) { - unused_var(n); - m_a.deallocate(p); + p->~U(); } - template <class AR1, class T1, std::size_t A1, class AR2, class T2, std::size_t A2> - friend bool operator==(const arena_allocator<AR1, T1, A1>& lhs, - const arena_allocator<AR2, T2, A2>& rhs) noexcept; + template <class AR1, class T1, std::size_t A1, bool HSA1, class AR2, class T2, std::size_t A2, bool HSA2> + friend bool operator==(const arena_allocator<AR1, T1, A1, HSA1>& lhs, // NOLINT + const arena_allocator<AR2, T2, A2, HSA2>& rhs) noexcept; + + template <class AU1, class U1, std::size_t A1, bool HSA1> + friend class arena_allocator; - template <class AU, class U, std::size_t A> friend class arena_allocator; private: arena_type &m_a; }; - template <class AR1, class T1, std::size_t A1, class AR2, class T2, std::size_t A2> - inline bool operator==(const arena_allocator<AR1, T1, A1>& lhs, - const arena_allocator<AR2, T2, A2>& rhs) noexcept + template <class AR1, class T1, std::size_t A1, bool HSA1, class AR2, class T2, std::size_t A2, bool HSA2> + inline bool operator==(const arena_allocator<AR1, T1, A1, HSA1>& lhs, + const arena_allocator<AR2, T2, A2, HSA2>& rhs) noexcept { return A1 == A2 && rhs.m_a == lhs.m_a; } - template <class AR1, class T1, std::size_t A1, class AR2, class T2, std::size_t A2> - inline bool operator!=(const arena_allocator<AR1, T1, A1>& lhs, - const arena_allocator<AR2, T2, A2>& rhs) noexcept + template <class AR1, class T1, std::size_t A1, bool HSA1,class AR2, class T2, std::size_t A2, bool HSA2> + inline bool operator!=(const arena_allocator<AR1, T1, A1, HSA1>& lhs, + const arena_allocator<AR2, T2, A2, HSA2>& rhs) noexcept { return !(lhs == rhs); } @@ -255,140 +328,376 @@ namespace plib { // Memory allocation //============================================================ - struct aligned_arena + //template <typename P, std::size_t MINALIGN, bool HSD, bool HSA> + //struct arena_base; + + template <typename P, std::size_t MINALIGN, bool HSD, bool HSA> + struct arena_core { - static constexpr const bool is_stateless = true; - template <class T, std::size_t ALIGN = alignof(T)> - using allocator_type = arena_allocator<aligned_arena, T, ALIGN>; + static constexpr const bool has_static_deallocator = HSD; + static constexpr const bool has_static_allocator = HSA; + static constexpr const std::size_t min_align = MINALIGN; + using size_type = std::size_t; + + template <class T, size_type ALIGN = 0> + using allocator_type = arena_allocator<P, T, (ALIGN < MINALIGN) ? MINALIGN : ALIGN, HSA>; + + template <class T, size_type ALIGN = 0> + using deleter_type = arena_deleter<P, T, (ALIGN < MINALIGN) ? MINALIGN : ALIGN>; + + template <typename T, size_type ALIGN = 0> + using unique_ptr = std::unique_ptr<T, deleter_type<T, (ALIGN < MINALIGN) ? MINALIGN : ALIGN>>; - template <typename T> - using owned_pool_ptr = plib::owned_ptr<T, arena_deleter<aligned_arena, T>>; + template <typename T, size_type ALIGN = 0> + using owned_ptr = plib::owned_ptr<T, deleter_type<T, (ALIGN < MINALIGN) ? MINALIGN : ALIGN>>; - static inline aligned_arena &instance() + static P &instance() noexcept; + + template <class T, size_type ALIGN = 0> + allocator_type<T, ALIGN> get_allocator() { - static aligned_arena s_arena; - return s_arena; + return *static_cast<P *>(this); } - static inline void *allocate( size_t alignment, size_t size ) + protected: + size_t m_stat_cur_alloc = 0; + size_t m_stat_max_alloc = 0; + + }; + + template <typename P, std::size_t MINALIGN, bool HSD, bool HSA> + inline P & arena_core<P, MINALIGN, HSD, HSA>::instance() noexcept + { + static P s_arena; + return s_arena; + } + + template <typename P, std::size_t MINALIGN, bool HSD, bool HSA> + struct arena_base : public arena_core<P, MINALIGN, HSD, HSA> + { + using base_type = arena_core<P, MINALIGN, HSD, HSA>; + using size_type = typename base_type::size_type; + + ~arena_base() { - #if (USE_ALIGNED_ALLOCATION) - #if defined(_WIN32) || defined(_WIN64) || defined(_MSC_VER) - return _aligned_malloc(size, alignment); - #elif defined(__APPLE__) - void* p; - if (::posix_memalign(&p, alignment, size) != 0) { - p = nullptr; - } - return p; - #else - return aligned_alloc(alignment, size); - #endif - #else - unused_var(alignment); - return ::operator new(size); - #endif + //printf("%s %lu %lu %lu\n", typeid(*this).name(), MINALIGN, cur_alloc(), max_alloc()); } - static inline void deallocate( void *ptr ) + static size_type cur_alloc() noexcept { return base_type::instance().m_stat_cur_alloc; } + static size_type max_alloc() noexcept { return base_type::instance().m_stat_max_alloc; } + + static inline void inc_alloc_stat(size_type size) { - #if (USE_ALIGNED_ALLOCATION) + auto &i = base_type::instance(); + i.m_stat_cur_alloc += size; + if (i.m_stat_max_alloc <i.m_stat_cur_alloc) + i.m_stat_max_alloc = i.m_stat_cur_alloc; + } + static inline void dec_alloc_stat(size_type size) + { + base_type::instance().m_stat_cur_alloc -= size; + } + }; + + template <typename P, std::size_t MINALIGN> + struct arena_base<P, MINALIGN, false, false> : public arena_core<P, MINALIGN, false, false> + { + using size_type = typename arena_core<P, MINALIGN, false, false>::size_type; + + ~arena_base() + { + //printf("%s %lu %lu %lu\n", typeid(*this).name(), MINALIGN, cur_alloc(), max_alloc()); + } + + size_type cur_alloc() const noexcept { return this->m_stat_cur_alloc; } + size_type max_alloc() const noexcept { return this->m_stat_max_alloc; } + + inline void inc_alloc_stat(size_type size) + { + this->m_stat_cur_alloc += size; + if (this->m_stat_max_alloc < this->m_stat_cur_alloc) + this->m_stat_max_alloc = this->m_stat_cur_alloc; + } + inline void dec_alloc_stat(size_type size) + { + this->m_stat_cur_alloc -= size; + } + }; + + template <std::size_t MINALIGN> + struct aligned_arena : public arena_base<aligned_arena<MINALIGN>, MINALIGN, true, true> + { + using base_type = arena_base<aligned_arena<MINALIGN>, MINALIGN, true, true>; + + static inline gsl::owner<void *> allocate( size_t alignment, size_t size ) + { + base_type::inc_alloc_stat(size); +#if 0 + #if (PUSE_ALIGNED_ALLOCATION) + #if defined(_WIN32) || defined(_WIN64) || defined(_MSC_VER) + return _aligned_malloc(size, alignment); + #elif defined(__APPLE__) || defined(__ANDROID__) + void* p; + if (::posix_memalign(&p, alignment, size) != 0) { + p = nullptr; + } + return p; + #else + // see https://en.cppreference.com/w/c/memory/aligned_alloc + size = ((size + alignment - 1) / alignment) * alignment; + return static_cast<gsl::owner<void *>>(aligned_alloc(alignment, size)); + #endif + #else + unused_var(alignment); + return ::operator new(size); + #endif +#else + return ::operator new(size, std::align_val_t(alignment)); +#endif + } + + static inline void deallocate(gsl::owner<void *> ptr, [[maybe_unused]] size_t alignment, size_t size ) noexcept + { + //unused_var(size); + base_type::dec_alloc_stat(size); +#if 0 + #if (PUSE_ALIGNED_ALLOCATION) + #if defined(_WIN32) || defined(_WIN64) || defined(_MSC_VER) // NOLINTNEXTLINE(cppcoreguidelines-no-malloc) - free(ptr); + _aligned_free(ptr); + #else + // NOLINTNEXTLINE(cppcoreguidelines-no-malloc) + ::free(ptr); + #endif #else ::operator delete(ptr); #endif +#else + ::operator delete(ptr, std::align_val_t(alignment)); +#endif } -#if 0 - template<typename T, typename... Args> - owned_pool_ptr<T> make_poolptr(Args&&... args) + bool operator ==([[maybe_unused]] const aligned_arena &rhs) const noexcept { - auto *mem = allocate(alignof(T), sizeof(T)); - return owned_pool_ptr<T>(new (mem) T(std::forward<Args>(args)...), true, arena_deleter<aligned_arena, T>(*this)); + return true; } -#endif - template<typename T, typename... Args> - owned_pool_ptr<T> make_poolptr(Args&&... args) + + }; + + struct std_arena : public arena_base<std_arena, 0, true, true> + { + static inline void *allocate(size_t alignment, size_t size ) { - auto *mem = allocate(alignof(T), sizeof(T)); + inc_alloc_stat(size); + return ::operator new(size, static_cast<std::align_val_t>(alignment)); + } + + static inline void deallocate( void *ptr, size_t alignment, size_t size ) noexcept + { + dec_alloc_stat(size); + ::operator delete(ptr, static_cast<std::align_val_t>(alignment)); + } + + bool operator ==([[maybe_unused]] const std_arena &rhs) const noexcept + { + return true; + } + }; + + namespace detail + { + /// + /// \brief Create new object T with an aligned memory + /// + /// The create object can be deallocate using \ref free or + /// using the arena_deleter type. This is the specialization for arenas + /// which have no state. + /// + /// \tparam T Object type + /// \tparam ALIGN Alignment of object to be created. If ALIGN equals 0, alignof(T) is used. + /// \tparam ARENA Arena type + /// \tparam Args Argument types + /// + /// \param args Arguments to be passed to constructor + /// + template<typename T, std::size_t ALIGN, typename ARENA, typename... Args> + static inline T * alloc(Args&&... args) + { + //using alloc_type = typename ARENA :: template allocator_type<T, alignof(T)>; + auto *mem = ARENA::allocate(ALIGN ? ALIGN : alignof(T), sizeof(T)); try { - auto *mema = new (mem) T(std::forward<Args>(args)...); - return owned_pool_ptr<T>(mema, true, arena_deleter<aligned_arena, T>(*this)); + // NOLINTNEXTLINE(cppcoreguidelines-owning-memory) + return new (mem) T(std::forward<Args>(args)...); } catch (...) { - deallocate(mem); + ARENA::deallocate(mem, ALIGN ? ALIGN : alignof(T), sizeof(T)); throw; } } - }; - - template <typename T, std::size_t ALIGN> - /*inline */ C14CONSTEXPR T *assume_aligned_ptr(T *p) noexcept - { - static_assert(ALIGN >= alignof(T), "Alignment must be greater or equal to alignof(T)"); - static_assert(is_pow2(ALIGN), "Alignment must be a power of 2"); - //auto t = reinterpret_cast<std::uintptr_t>(p); - //if (t & (ALIGN-1)) - // printf("alignment error!"); -#if (USE_ALIGNED_HINTS) - return reinterpret_cast<T *>(__builtin_assume_aligned(p, ALIGN)); -#else - return p; -#endif - } + template<std::size_t ALIGN, typename ARENA, typename T> + static inline void free(T *ptr) noexcept + { + ptr->~T(); + ARENA::deallocate(ptr, ALIGN ? ALIGN : alignof(T), sizeof(T)); + } - template <typename T, std::size_t ALIGN> - constexpr const T *assume_aligned_ptr(const T *p) noexcept - { - static_assert(ALIGN >= alignof(T), "Alignment must be greater or equal to alignof(T)"); - static_assert(is_pow2(ALIGN), "Alignment must be a power of 2"); -#if (USE_ALIGNED_HINTS) - return reinterpret_cast<const T *>(__builtin_assume_aligned(p, ALIGN)); -#else - return p; -#endif - } + /// + /// \brief Create new object T with an aligned memory + /// + /// The create object can be deallocate using \ref free or + /// using the arena_deleter type. This is the specialization for arenas + /// which do have state. + /// + /// \tparam T Object type + /// \tparam ALIGN Alignment of object to be created. If ALIGN equals 0, alignof(T) is used. + /// \tparam ARENA Arena type + /// \tparam Args Argument types + /// + /// \param arena Arena to provide memory + /// \param args Arguments to be passed to constructor + /// + template<typename T, std::size_t ALIGN, typename ARENA, typename... Args> + static inline T * alloc(ARENA &arena, Args&&... args) + { + auto *mem = arena.allocate(ALIGN ? ALIGN : alignof(T), sizeof(T)); + try + { + // NOLINTNEXTLINE(cppcoreguidelines-owning-memory) + return new (mem) T(std::forward<Args>(args)...); + } + catch (...) + { + arena.deallocate(mem, ALIGN ? ALIGN : alignof(T), sizeof(T)); + throw; + } + } - // FIXME: remove - template<typename T, typename... Args> - inline T *pnew(Args&&... args) + template<std::size_t ALIGN, typename ARENA, typename T> + static inline void free(ARENA &arena, T *ptr) noexcept + { + ptr->~T(); + arena.deallocate(ptr, ALIGN ? ALIGN : alignof(T), sizeof(T)); + } + } // namespace detail + + + /// + /// \brief Create new alignment and size aware std::unique_ptr + /// + /// `make_unique` creates a new shared pointer to the object it creates. + /// These version ensure that on deallocation the correct alignment and + /// size is used. Should the unique_ptr be down casted the deleter objects + /// used here track the size and alignment of the object created. + /// + /// std::standard_delete will use size and alignment of the base class. + /// + /// This function is deprecated since it hides the use of arenas. + /// + /// \tparam T Object type + /// \tparam ARENA Arena type + /// \tparam ALIGN Alignment of object to be created. If ALIGN equals 0, alignof(T) is used. + /// \tparam Args Argument types + /// + /// \param args Arguments to be passed to constructor + /// + template<typename T, typename ARENA, std::size_t ALIGN = 0, typename... Args> + //[[deprecated]] + std::enable_if_t<ARENA::has_static_allocator, typename ARENA::template unique_ptr<T, ALIGN>> + make_unique(Args&&... args) { - auto *p = aligned_arena::allocate(alignof(T), sizeof(T)); - return new(p) T(std::forward<Args>(args)...); + using up_type = typename ARENA::template unique_ptr<T, ALIGN>; + using deleter_type = typename ARENA::template deleter_type<T, ALIGN>; + auto *mem = detail::alloc<T, ALIGN, ARENA>(std::forward<Args>(args)...); + return up_type(mem, deleter_type()); } - template<typename T> - inline void pdelete(T *ptr) + /// + /// \brief Create new alignment and size aware std::unique_ptr + /// + /// `make_unique` creates a new shared pointer to the object it creates. + /// These version ensure that on deallocation the correct alignment and + /// size is used. Should the unique_ptr be down casted the deleter objects + /// used here track the size and alignment of the object created. + /// + /// std::standard_delete will use size and alignment of the base class. + /// + /// \tparam T Object type + /// \tparam ARENA Arena type + /// \tparam ALIGN Alignment of object to be created. If ALIGN equals 0, alignof(T) is used. + /// \tparam Args Argument types + /// + /// \param arena Arena to provide memory + /// \param args Arguments to be passed to constructor + /// + template<typename T, typename ARENA, std::size_t ALIGN = 0, typename... Args> + typename ARENA::template unique_ptr<T, ALIGN> + make_unique(ARENA &arena, Args&&... args) { - ptr->~T(); - aligned_arena::deallocate(ptr); + using up_type = typename ARENA::template unique_ptr<T, ALIGN>; + using deleter_type = typename ARENA::template deleter_type<T, ALIGN>; + auto *mem = detail::alloc<T, ALIGN>(arena, std::forward<Args>(args)...); + return up_type(mem, deleter_type(&arena)); } - - template <typename T> - using unique_ptr = std::unique_ptr<T, arena_deleter<aligned_arena, T>>; - - template<typename T, typename... Args> - plib::unique_ptr<T> make_unique(Args&&... args) + /// + /// \brief Create new alignment and size aware plib::owned_ptr + /// + /// `make_unique` creates a new shared pointer to the object it creates. + /// These version ensure that on deallocation the correct alignment and + /// size is used. Should the unique_ptr be down casted the deleter objects + /// used here track the size and alignment of the object created. + /// + /// std::standard_delete will use size and alignment of the base class. + /// + /// This function is deprecated since it hides the use of arenas. + /// + /// \tparam T Object type + /// \tparam ARENA Arena type + /// \tparam ALIGN Alignment of object to be created. If ALIGN equals 0, alignof(T) is used. + /// \tparam Args Argument types + /// + /// \param args Arguments to be passed to constructor + /// + template<typename T, typename ARENA, std::size_t ALIGN = 0, typename... Args> + //[[deprecated]] + std::enable_if_t<ARENA::has_static_allocator, typename ARENA::template owned_ptr<T, ALIGN>> + make_owned(Args&&... args) { - return plib::unique_ptr<T>(pnew<T>(std::forward<Args>(args)...)); + using op_type = typename ARENA::template owned_ptr<T, ALIGN>; + using deleter_type = typename ARENA::template deleter_type<T, ALIGN>; + auto *mem = detail::alloc<T, ALIGN, ARENA>(std::forward<Args>(args)...); + return op_type(mem, true, deleter_type()); } -#if 0 - template<typename T, typename... Args> - static owned_ptr<T> make_owned(Args&&... args) + /// + /// \brief Create new alignment and size aware plib::owned_ptr + /// + /// `make_unique` creates a new shared pointer to the object it creates. + /// These version ensure that on deallocation the correct alignment and + /// size is used. Should the unique_ptr be down casted the deleter objects + /// used here track the size and alignment of the object created. + /// + /// std::standard_delete will use size and alignment of the base class. + /// + /// \tparam T Object type + /// \tparam ARENA Arena type + /// \tparam ALIGN Alignment of object to be created. If ALIGN equals 0, alignof(T) is used. + /// \tparam Args Argument types + /// + /// \param arena Arena to provide memory + /// \param args Arguments to be passed to constructor + /// + template<typename T, typename ARENA, std::size_t ALIGN = 0, typename... Args> + typename ARENA::template owned_ptr<T> make_owned(ARENA &arena, Args&&... args) { - return owned_ptr<T>(pnew<T>(std::forward<Args>(args)...), true); + using op_type = typename ARENA::template owned_ptr<T, ALIGN>; + using deleter_type = typename ARENA::template deleter_type<T, ALIGN>; + auto *mem = detail::alloc<T, ALIGN>(arena, std::forward<Args>(args)...); + return op_type(mem, true, deleter_type(&arena)); } -#endif - - - template <class T, std::size_t ALIGN = alignof(T)> - using aligned_allocator = aligned_arena::allocator_type<T, ALIGN>; //============================================================ // traits to determine alignment size and stride size @@ -397,55 +706,103 @@ namespace plib { PDEFINE_HAS_MEMBER(has_align, align_size); - template <typename T, typename X = void> - struct align_traits + template <typename T, bool X> + struct align_traits_base { + static_assert(!has_align<T>::value, "no align"); static constexpr const std::size_t align_size = alignof(std::max_align_t); static constexpr const std::size_t value_size = sizeof(typename T::value_type); static constexpr const std::size_t stride_size = lcm(align_size, value_size) / value_size; }; template <typename T> - struct align_traits<T, typename std::enable_if<has_align<T>::value, void>::type> + struct align_traits_base<T, true> { + static_assert(has_align<T>::value, "no align"); static constexpr const std::size_t align_size = T::align_size; static constexpr const std::size_t value_size = sizeof(typename T::value_type); static constexpr const std::size_t stride_size = lcm(align_size, value_size) / value_size; }; - //============================================================ - // Aligned vector - //============================================================ - - // FIXME: needs a separate file - template <class T, std::size_t ALIGN = alignof(T)> - class aligned_vector : public std::vector<T, aligned_allocator<T, ALIGN>> + template <typename T> + struct align_traits : public align_traits_base<T, has_align<T>::value> + {}; + + /// + /// \brief Force BASEARENA to align memory allocations on page boundaries + /// + /// \tparam BASEARENA The base arena to use (optional, defaults to aligned_arena) + /// \tparam PG_SIZE The page size to use (optional, defaults to 1024) + /// + template <template<std::size_t> class BASEARENA = aligned_arena, std::size_t PG_SIZE = 1024> + using paged_arena = BASEARENA<PG_SIZE>; + + /// + /// \brief Helper class to create arena versions of standard library sequences + /// + /// \ref arena_vector on how to use this class + /// + /// \tparam A Arena typeThe base arena to use (optional, defaults to aligned_arena) + /// \tparam T Object type of objects in sequence + /// \tparam S Sequence, e.g. std::vector, std::list + /// \tparam ALIGN Alignment to use + /// + template <typename A, typename T, template <typename, typename> class S, std::size_t ALIGN = PALIGN_VECTOROPT> + class arena_sequence : public S<T, typename A::template allocator_type<T, ALIGN>> { public: - using base = std::vector<T, aligned_allocator<T, ALIGN>>; + using arena_allocator_type = typename A::template allocator_type<T, ALIGN>; + using arena_sequence_base = S<T, arena_allocator_type>; + using arena_sequence_base::arena_sequence_base; - using reference = typename base::reference; - using const_reference = typename base::const_reference; - using pointer = typename base::pointer; - using const_pointer = typename base::const_pointer; - using size_type = typename base::size_type; + using size_type = typename arena_sequence_base::size_type; - using base::base; - - C14CONSTEXPR reference operator[](size_type i) noexcept + arena_sequence(A &arena) + : arena_sequence_base(seq_alloc(arena)) { - return assume_aligned_ptr<T, ALIGN>(&(base::operator[](0)))[i]; } - constexpr const_reference operator[](size_type i) const noexcept + + arena_sequence(A &arena, size_type n) + : arena_sequence_base(n, seq_alloc(arena)) { - return assume_aligned_ptr<T, ALIGN>(&(base::operator[](0)))[i]; } - pointer data() noexcept { return assume_aligned_ptr<T, ALIGN>(base::data()); } - const_pointer data() const noexcept { return assume_aligned_ptr<T, ALIGN>(base::data()); } + private: + arena_allocator_type seq_alloc(A &arena) const { return arena.template get_allocator<T, ALIGN>(); } + }; + + /// + /// \brief Vector with arena allocations + /// + /// The Vector allocation will use the arena of type A of which an instance + /// has to be passed to the constructor. Should the minimum alignment exceed + /// the object size, min_align / sizeof(T) elements are reserved. + /// + /// \tparam A Arena type + /// \tparam T Object type of objects in sequence + /// \tparam ALIGN Alignment to use + /// + template <typename A, typename T, std::size_t ALIGN = PALIGN_VECTOROPT> + class arena_vector : public arena_sequence<A, T, std::vector, ALIGN> + { + public: + using arena_vector_base = arena_sequence<A, T, std::vector, ALIGN>; + using arena_vector_base::arena_vector_base; + + /// + /// \brief Constructor + /// + /// \param arena Arena instance to use + /// + arena_vector(A &arena) + : arena_vector_base(arena) + { + if (A::min_align / sizeof(T) > 0) + this->reserve(A::min_align / sizeof(T)); + } }; } // namespace plib -#endif /* PALLOC_H_ */ +#endif // PALLOC_H_ |