// license:GPL-2.0+ // copyright-holders:Couriersud #ifndef PALLOC_H_ #define PALLOC_H_ /// /// \file palloc.h /// #include "pconfig.h" #include "pmath.h" #include "pstring.h" #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 { //============================================================ // Standard arena_deleter //============================================================ template struct arena_deleter { }; template struct arena_deleter::type> { using arena_storage_type = P; constexpr arena_deleter(arena_storage_type *a = nullptr) noexcept : m_a(a) { } template::value>::type> arena_deleter(const arena_deleter &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::type> { using arena_storage_type = P; constexpr arena_deleter(arena_storage_type *a = nullptr) noexcept { plib::unused_var(a); } template::value>::type> arena_deleter(const arena_deleter &rhs) noexcept { plib::unused_var(rhs); } void operator()(T *p) noexcept { // call destructor p->~T(); P::deallocate(p, sizeof(T)); } }; //============================================================ // 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; static constexpr const std::size_t align_size = ALIGN; using arena_type = ARENA; static_assert(align_size >= alignof(T) && (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; explicit arena_allocator(const 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; }; T* allocate(std::size_t n) { return reinterpret_cast(m_a.allocate(ALIGN, sizeof(T) * n)); //NOLINT } void deallocate(T* p, std::size_t n) noexcept { m_a.deallocate(p, 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 //============================================================ struct aligned_arena { static constexpr const bool has_static_deallocator = true; using size_type = std::size_t; template using allocator_type = arena_allocator; template using unique_pool_ptr = std::unique_ptr>; template using owned_pool_ptr = plib::owned_ptr>; static inline aligned_arena &instance() noexcept { static aligned_arena s_arena; return s_arena; } static inline void *allocate( size_t alignment, size_t size ) { m_stat_cur_alloc() += size; if (m_stat_max_alloc() < m_stat_cur_alloc()) m_stat_max_alloc() = m_stat_cur_alloc(); #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 aligned_alloc(alignment, size); #endif #else unused_var(alignment); return ::operator new(size); #endif } static inline void deallocate( void *ptr, size_t size ) noexcept { //unused_var(size); m_stat_cur_alloc() -= size; #if (PUSE_ALIGNED_ALLOCATION) // NOLINTNEXTLINE(cppcoreguidelines-no-malloc) ::free(ptr); #else ::operator delete(ptr); #endif } template unique_pool_ptr make_unique(Args&&... args) { auto *mem = allocate(alignof(T), sizeof(T)); try { // NOLINTNEXTLINE(cppcoreguidelines-owning-memory) auto *mema = new (mem) T(std::forward(args)...); return unique_pool_ptr(mema, arena_deleter(this)); } catch (...) { deallocate(mem, sizeof(T)); throw; } } template owned_pool_ptr make_owned(Args&&... args) { auto *mem = allocate(alignof(T), sizeof(T)); try { // NOLINTNEXTLINE(cppcoreguidelines-owning-memory) auto *mema = new (mem) T(std::forward(args)...); return owned_pool_ptr(mema, true, arena_deleter(*this)); } catch (...) { deallocate(mem, sizeof(T)); throw; } } template static T *alloc(Args&&... args) { auto *p = allocate(alignof(T), sizeof(T)); // NOLINTNEXTLINE(cppcoreguidelines-owning-memory) return new(p) T(std::forward(args)...); } template static void free(T *ptr) noexcept { ptr->~T(); aligned_arena::deallocate(ptr, sizeof(T)); } bool operator ==(const aligned_arena &rhs) const noexcept { plib::unused_var(rhs); return true; } size_type cur_alloc() const noexcept { return m_stat_cur_alloc(); } // NOLINT(readability-convert-member-functions-to-static) size_type max_alloc() const noexcept { return m_stat_max_alloc(); } // NOLINT(readability-convert-member-functions-to-static) private: static size_t &m_stat_cur_alloc() noexcept { static size_t val = 0; return val; } static size_t &m_stat_max_alloc() noexcept { static size_t val = 0; return val; } }; template 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"); #if (PUSE_ALIGNED_HINTS) return reinterpret_cast(__builtin_assume_aligned(p, ALIGN)); #else return p; #endif } template 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 (PUSE_ALIGNED_HINTS) return reinterpret_cast(__builtin_assume_aligned(p, ALIGN)); #else return p; #endif } template using unique_ptr = aligned_arena::unique_pool_ptr; template plib::unique_ptr make_unique(Args&&... args) { return aligned_arena::instance().make_unique(std::forward(args)...); } 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 { 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::value, void>::type> { 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 aligned_vector : public std::vector { public: using base = std::vector; 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 base::base; base & as_base() noexcept { return *this; } const base & as_base() const noexcept { return *this; } C14CONSTEXPR reference operator[](size_type i) noexcept { return assume_aligned_ptr(&(base::operator[](0)))[i]; } constexpr const_reference operator[](size_type i) const noexcept { return assume_aligned_ptr(&(base::operator[](0)))[i]; } pointer data() noexcept { return assume_aligned_ptr(base::data()); } const_pointer data() const noexcept { return assume_aligned_ptr(base::data()); } }; } // namespace plib #endif // PALLOC_H_