// license:GPL-2.0+ // copyright-holders:Couriersud #ifndef PALLOC_H_ #define PALLOC_H_ /// /// \file palloc.h /// #include "pconfig.h" #include "pgsl.h" #include "pgsl.h" #include "pmath.h" // FIXME: only uses lcm ... move to ptypes. #include "ptypes.h" #include #include // for std::max_align_t (usually long long) #include #include #include #include #if defined(_WIN32) || defined(_WIN64) || defined(_MSC_VER) #include #endif namespace plib { //============================================================ // aligned types //============================================================ #if 0 #if (PUSE_ALIGNED_HINTS) template using aligned_type __attribute__((aligned(A))) = T; #else template using aligned_type = T; #endif template using aligned_pointer = aligned_type *; template using const_aligned_pointer = const aligned_type *; template using aligned_reference = aligned_type &; template using const_aligned_reference = const aligned_type &; #endif //============================================================ // Standard arena_deleter //============================================================ template struct arena_deleter_base { }; template struct arena_deleter_base { using arena_storage_type = P; constexpr arena_deleter_base(arena_storage_type *a = nullptr) noexcept : m_a(a) { } template::value>> arena_deleter_base(const arena_deleter_base &rhs) noexcept : m_a(rhs.m_a) { } void operator()(T *p) noexcept { // call destructor p->~T(); m_a->deallocate(p, sizeof(T)); } //private: arena_storage_type *m_a; }; template struct arena_deleter_base { using arena_storage_type = P; constexpr arena_deleter_base(arena_storage_type *a = nullptr) noexcept { plib::unused_var(a); } template::value>::type> arena_deleter_base(const arena_deleter_base &rhs) noexcept { plib::unused_var(rhs); } void operator()(T *p) noexcept { // call destructor p->~T(); P::deallocate(p, sizeof(T)); } }; template struct arena_deleter : public arena_deleter_base { using base_type = arena_deleter_base; using base_type::base_type; }; //============================================================ // owned_ptr: smart pointer with ownership information //============================================================ template class owned_ptr { public: using pointer = SC *; using element_type = SC; using deleter_type = D; owned_ptr() : m_ptr(nullptr), m_deleter(), m_is_owned(true) { } template friend class owned_ptr; owned_ptr(pointer p, bool owned) : m_ptr(p), m_deleter(), m_is_owned(owned) { } owned_ptr(pointer p, bool owned, D deleter) : m_ptr(p), m_deleter(deleter), m_is_owned(owned) { } owned_ptr(const owned_ptr &r) = delete; owned_ptr & operator =(owned_ptr &r) = delete; template 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; m_deleter = r.m_deleter; r.m_is_owned = false; r.m_ptr = nullptr; return *this; } owned_ptr(owned_ptr &&r) noexcept : m_ptr(r.m_ptr) , m_deleter(r.m_deleter) , m_is_owned(r.m_is_owned) { r.m_is_owned = false; r.m_ptr = nullptr; } 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; m_deleter = r.m_deleter; r.m_is_owned = false; r.m_ptr = nullptr; return *this; } template owned_ptr(owned_ptr &&r) noexcept : m_ptr(static_cast(r.get())) , m_deleter(r.m_deleter) , m_is_owned(r.is_owned()) { r.release(); } ~owned_ptr() noexcept { if (m_is_owned && (m_ptr != nullptr)) { //delete m_ptr; m_deleter(m_ptr); } m_is_owned = false; m_ptr = nullptr; } /// /// \brief Return \c true if the stored pointer is not null. /// explicit operator bool() const noexcept { return m_ptr != nullptr; } pointer release() { pointer tmp = m_ptr; m_is_owned = false; m_ptr = nullptr; return tmp; } bool is_owned() const { return m_is_owned; } pointer operator ->() const noexcept { return m_ptr; } typename std::add_lvalue_reference::type operator *() const noexcept { return *m_ptr; } pointer get() const noexcept { return m_ptr; } deleter_type& get_deleter() noexcept { return m_deleter; } const deleter_type& get_deleter() const noexcept { return m_deleter; } private: pointer m_ptr; D m_deleter; bool m_is_owned; }; //============================================================ // Arena allocator for use with containers //============================================================ template class arena_allocator { public: using value_type = T; using pointer = T *; static /*constexpr*/ const std::size_t align_size = ALIGN; using arena_type = ARENA; static_assert(align_size >= alignof(T), "ALIGN must be greater than alignof(T) and a multiple"); static_assert((align_size % alignof(T)) == 0, "ALIGN must be greater than alignof(T) and a multiple"); arena_allocator() noexcept : m_a(arena_type::instance()) { } ~arena_allocator() noexcept = default; PCOPYASSIGNMOVE(arena_allocator, default) explicit arena_allocator(arena_type & a) noexcept : m_a(a) { } template arena_allocator(const arena_allocator& rhs) noexcept : m_a(rhs.m_a) { } template struct rebind { using other = arena_allocator; }; pointer allocate(std::size_t n) { return reinterpret_cast(m_a.allocate(ALIGN, sizeof(T) * n)); //NOLINT } void deallocate(pointer p, std::size_t n) noexcept { m_a.deallocate(p, sizeof(T) * n); } template void construct(U* p, Args&&... args) { // NOLINTNEXTLINE(cppcoreguidelines-owning-memory) ::new (static_cast(p)) U(std::forward(args)...); } template void destroy(U* p) { p->~U(); } template friend bool operator==(const arena_allocator& lhs, // NOLINT const arena_allocator& rhs) noexcept; template friend class arena_allocator; private: arena_type &m_a; }; template inline bool operator==(const arena_allocator& lhs, const arena_allocator& rhs) noexcept { return A1 == A2 && rhs.m_a == lhs.m_a; } template inline bool operator!=(const arena_allocator& lhs, const arena_allocator& rhs) noexcept { return !(lhs == rhs); } //============================================================ // Memory allocation //============================================================ // MSVC has an issue with SFINAE and overloading resolution. // A discussion can be found here: // // https://stackoverflow.com/questions/31062892/overloading-on-static-in-conjunction-with-sfinae // // The previous code compiled with gcc and clang on all platforms and // compilers apart from MSVC. template struct arena_base; template struct arena_core { static constexpr const bool has_static_deallocator = HSD; static constexpr const bool has_static_allocator = HSA; using size_type = std::size_t; template using allocator_type = arena_allocator; template using deleter_type = arena_deleter; template using unique_ptr = std::unique_ptr>; template using owned_ptr = plib::owned_ptr>; static inline P &instance() noexcept { static P s_arena; return s_arena; } friend struct arena_base; private: size_t m_stat_cur_alloc = 0; size_t m_stat_max_alloc = 0; }; template struct arena_base : public arena_core { using base_type = arena_core; using size_type = typename base_type::size_type; 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) { auto &i = base_type::instance(); i.m_stat_cur_alloc += size; if (i.m_stat_max_alloc struct arena_base : public arena_core { using size_type = typename arena_core::size_type; 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; } }; struct aligned_arena : public arena_base { static inline gsl::owner allocate( size_t alignment, size_t size ) { inc_alloc_stat(size); #if (PUSE_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 static_cast>(aligned_alloc(alignment, size)); #endif #else unused_var(alignment); return ::operator new(size); #endif } static inline void deallocate(gsl::owner ptr, size_t size ) noexcept { //unused_var(size); dec_alloc_stat(size); #if (PUSE_ALIGNED_ALLOCATION) #if defined(_WIN32) || defined(_WIN64) || defined(_MSC_VER) // NOLINTNEXTLINE(cppcoreguidelines-no-malloc) _aligned_free(ptr); #else // NOLINTNEXTLINE(cppcoreguidelines-no-malloc) ::free(ptr); #endif #else ::operator delete(ptr); #endif } bool operator ==(const aligned_arena &rhs) const noexcept { plib::unused_var(rhs); return true; } }; struct std_arena : public arena_base { static inline void *allocate( size_t alignment, size_t size ) { inc_alloc_stat(size); unused_var(alignment); return ::operator new(size); } static inline void deallocate( void *ptr, size_t size ) noexcept { dec_alloc_stat(size); ::operator delete(ptr); } bool operator ==(const aligned_arena &rhs) const noexcept { plib::unused_var(rhs); return true; } }; namespace detail { template static inline T * alloc(Args&&... args) { auto *mem = ARENA::allocate(alignof(T), sizeof(T)); try { // NOLINTNEXTLINE(cppcoreguidelines-owning-memory) return new (mem) T(std::forward(args)...); } catch (...) { ARENA::deallocate(mem, sizeof(T)); throw; } } template static inline void free(T *ptr) noexcept { ptr->~T(); ARENA::deallocate(ptr, sizeof(T)); } template static inline T * alloc(ARENA &arena, Args&&... args) { auto *mem = arena.allocate(alignof(T), sizeof(T)); try { // NOLINTNEXTLINE(cppcoreguidelines-owning-memory) return new (mem) T(std::forward(args)...); } catch (...) { arena.deallocate(mem, sizeof(T)); throw; } } template static inline void free(ARENA &arena, T *ptr) noexcept { ptr->~T(); arena.deallocate(ptr, sizeof(T)); } } // namespace detail template static inline std::enable_if_t> make_unique(Args&&... args) { using up_type = typename ARENA::template unique_ptr; using deleter_type = typename ARENA::template deleter_type; auto *mem = detail::alloc(std::forward(args)...); return up_type(mem, deleter_type()); } template static inline std::enable_if_t> make_unique(Args&&... args) { return make_unique(ARENA::instance(), std::forward(args)...); } template static inline typename ARENA::template unique_ptr make_unique(ARENA &arena, Args&&... args) { using up_type = typename ARENA::template unique_ptr; using deleter_type = typename ARENA::template deleter_type; auto *mem = detail::alloc(arena, std::forward(args)...); return up_type(mem, deleter_type(&arena)); } template static inline std::enable_if_t> make_owned(Args&&... args) { using op_type = typename ARENA::template owned_ptr; using deleter_type = typename ARENA::template deleter_type; auto *mem = detail::alloc(std::forward(args)...); return op_type(mem, true, deleter_type()); } template static inline std::enable_if_t> make_owned(Args&&... args) { return make_owned(ARENA::instance(), std::forward(args)...); } template static inline typename ARENA::template owned_ptr make_owned(ARENA &arena, Args&&... args) { using op_type = typename ARENA::template owned_ptr; using deleter_type = typename ARENA::template deleter_type; auto *mem = detail::alloc(arena, std::forward(args)...); return op_type(mem, true, deleter_type(&arena)); } template using aligned_allocator = aligned_arena::allocator_type; //============================================================ // traits to determine alignment size and stride size // from types supporting alignment //============================================================ PDEFINE_HAS_MEMBER(has_align, align_size); template struct align_traits_base { static_assert(!has_align::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 struct align_traits_base { static_assert(has_align::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; }; template struct align_traits : public align_traits_base::value> {}; template class paged_arena : public arena_base, true, true> { public: paged_arena() = default; PCOPYASSIGNMOVE(paged_arena, delete) ~paged_arena() = default; static void *allocate(size_t align, size_t size) { plib::unused_var(align); //size = ((size + PG_SIZE - 1) / PG_SIZE) * PG_SIZE; return arena().allocate(PG_SIZE, size); } static void deallocate(void *ptr, size_t size) noexcept { //size = ((size + PG_SIZE - 1) / PG_SIZE) * PG_SIZE; arena().deallocate(ptr, size); } bool operator ==(const paged_arena &rhs) const noexcept { return this == &rhs; } static BASEARENA &arena() noexcept { static BASEARENA m_arena; return m_arena; } private: }; //============================================================ // Aligned vector //============================================================ // FIXME: needs a separate file template >//aligned_arena> class aligned_vector : public std::vector> { public: using base = std::vector>; using base::base; }; } // namespace plib #endif // PALLOC_H_