// license:GPL-2.0+ // copyright-holders:Couriersud /* * palloc.h * */ #ifndef PALLOC_H_ #define PALLOC_H_ #include "pconfig.h" #include "pstring.h" #include "ptypes.h" #include // for std::max_align_t (usually long long) //#include #include #include #include #include #if defined(_WIN32) || defined(_WIN64) || defined(_MSC_VER) #include #endif namespace plib { //============================================================ // Standard arena_deleter //============================================================ template struct arena_deleter { //using arena_storage_type = P *; using arena_storage_type = typename std::conditional::type; template typename std::enable_if::type getref(X *x) { return *x;} template typename std::enable_if::type::is_stateless, X&>::type getref(X &x, Y y = nullptr) { unused_var(y); return x; } constexpr arena_deleter(arena_storage_type a = arena_storage_type()) : m_a(a) { } #if 1 template::value>::type> arena_deleter(const arena_deleter &rhs) : m_a(rhs.m_a) { } #else template::value>::type> arena_deleter(const arena_deleter &rhs) : m_a(rhs.m_a) { } #endif void operator()(T *p) //const { /* call destructor */ p->~T(); getref(m_a).deallocate(p); } //private: arena_storage_type m_a; }; //============================================================ // 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; arena_allocator(arena_type & a) noexcept : m_a(a) { } template arena_allocator(const arena_allocator& rhs) : 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)); } void deallocate(T* p, std::size_t n) { unused_var(n); m_a.deallocate(p); } template friend bool operator==(const arena_allocator& lhs, 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 is_stateless = true; 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() { static aligned_arena s_arena; return s_arena; } static inline void *allocate( size_t alignment, size_t 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 aligned_alloc(alignment, size); #endif #else unused_var(alignment); return ::operator new(size); #endif } static inline void deallocate( void *ptr ) { #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 { auto *mema = new (mem) T(std::forward(args)...); return unique_pool_ptr(mema, arena_deleter(*this)); } catch (...) { deallocate(mem); throw; } } template owned_pool_ptr make_owned(Args&&... args) { auto *mem = allocate(alignof(T), sizeof(T)); try { auto *mema = new (mem) T(std::forward(args)...); return owned_pool_ptr(mema, true, arena_deleter(*this)); } catch (...) { deallocate(mem); throw; } } bool operator ==(const aligned_arena &rhs) const { plib::unused_var(rhs); return true; } }; template /*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(p); //if (t & (ALIGN-1)) // printf("alignment error!"); #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 } // FIXME: remove template inline T *pnew(Args&&... args) { auto *p = aligned_arena::allocate(alignof(T), sizeof(T)); return new(p) T(std::forward(args)...); } template inline void pdelete(T *ptr) { ptr->~T(); aligned_arena::deallocate(ptr); } template using unique_ptr = std::unique_ptr>; template plib::unique_ptr make_unique(Args&&... args) { return plib::unique_ptr(pnew(std::forward(args)...)); } #if 0 template static owned_ptr make_owned(Args&&... args) { return owned_ptr(pnew(std::forward(args)...), true); } #endif 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; 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_ */