#pragma once #include #include #include #include "def.h" #include "rw_lock.h" #include "platform/detail.h" namespace ipc { namespace mem { class static_alloc { public: static void swap(static_alloc&) {} static void clear() {} static void* alloc(std::size_t size) { return size ? std::malloc(size) : nullptr; } static void free(void* p) { std::free(p); } static void free(void* p, std::size_t /*size*/) { free(p); } }; //////////////////////////////////////////////////////////////// /// Scope allocation -- The destructor will release all allocated blocks. //////////////////////////////////////////////////////////////// namespace detail { constexpr std::size_t aligned(std::size_t size, size_t alignment) noexcept { return ((size - 1) & ~(alignment - 1)) + alignment; } class scope_alloc_base { protected: struct block_t { block_t * next_; std::size_t size_; } * list_ = nullptr; enum : std::size_t { aligned_block_size = aligned(sizeof(block_t), alignof(std::max_align_t)) }; public: void swap(scope_alloc_base & rhs) { std::swap(this->list_, rhs.list_); } void free(void* /*p*/) {} void free(void* /*p*/, std::size_t) {} }; } // namespace detail template class scope_alloc : public detail::scope_alloc_base { public: using base_t = detail::scope_alloc_base; using alloc_policy = AllocP; private: alloc_policy alloc_; void free_all() { while (list_ != nullptr) { auto curr = list_; list_ = list_->next_; alloc_.free(curr, curr->size_); } // now list_ is nullptr } public: scope_alloc() = default; scope_alloc(scope_alloc&& rhs) { this->swap(rhs); } scope_alloc& operator=(scope_alloc&& rhs) { this->swap(rhs); return (*this); } ~scope_alloc() { free_all(); } template void set_allocator(A && alc) { alloc_ = std::forward(alc); } void swap(scope_alloc& rhs) { alloc_.swap(rhs.alloc_); base_t::swap(rhs); } void clear() { free_all(); alloc_.~alloc_policy(); } void* alloc(std::size_t size) { auto curr = static_cast(alloc_.alloc(size += aligned_block_size)); curr->next_ = list_; curr->size_ = size; return (reinterpret_cast(list_ = curr) + aligned_block_size); } }; //////////////////////////////////////////////////////////////// /// Fixed-size blocks allocation //////////////////////////////////////////////////////////////// namespace detail { class fixed_alloc_base { protected: std::size_t init_expand_; void * cursor_; void init(std::size_t init_expand) { init_expand_ = init_expand; cursor_ = nullptr; } static void** node_p(void* node) { return reinterpret_cast(node); } static auto& next(void* node) { return *node_p(node); } public: void swap(fixed_alloc_base& rhs) { std::swap(this->init_expand_, rhs.init_expand_); std::swap(this->cursor_ , rhs.cursor_); } void free(void* p) { if (p == nullptr) return; next(p) = cursor_; cursor_ = p; } void free(void* p, std::size_t) { free(p); } }; struct fixed_expand_policy { enum : std::size_t { base_size = sizeof(void*) * 1024 / 2 }; static std::size_t prev(std::size_t& e) { if ((e /= 2) == 0) e = 1; return e; } static std::size_t next(std::size_t& e) { return e *= 2; } template static std::size_t next(std::size_t & e) { return ipc::detail::max(BlockSize, base_size) * next(e); } }; } // namespace detail template , typename ExpandP = detail::fixed_expand_policy> class fixed_alloc : public detail::fixed_alloc_base { public: using base_t = detail::fixed_alloc_base; using alloc_policy = AllocP; enum : std::size_t { block_size = (ipc::detail::max)(BlockSize, sizeof(void*)) }; private: alloc_policy alloc_; void* try_expand() { if (this->cursor_ != nullptr) { return this->cursor_; } auto size = ExpandP::template next(this->init_expand_); auto p = this->node_p(this->cursor_ = alloc_.alloc(size)); for (std::size_t i = 0; i < (size / block_size) - 1; ++i) p = this->node_p((*p) = reinterpret_cast(p) + block_size); (*p) = nullptr; return this->cursor_; } public: explicit fixed_alloc(std::size_t init_expand = 1) { this->init(init_expand); } fixed_alloc(fixed_alloc&& rhs) : fixed_alloc() { this->swap(rhs); } fixed_alloc& operator=(fixed_alloc&& rhs) { this->swap(rhs); return (*this); } template void set_allocator(A && alc) { alloc_ = std::forward(alc); } void swap(fixed_alloc& rhs) { alloc_.swap(rhs.alloc_); base_t::swap(rhs); } void clear() { ExpandP::prev(this->init_expand_); this->cursor_ = nullptr; alloc_.~alloc_policy(); } void* alloc() { void* p = try_expand(); this->cursor_ = this->next(p); return p; } void* alloc(std::size_t) { return alloc(); } }; //////////////////////////////////////////////////////////////// /// Variable-size blocks allocation (without alignment) //////////////////////////////////////////////////////////////// namespace detail { class variable_alloc_base { protected: struct head_t { head_t * next_; size_t size_; size_t free_; } * head_ = nullptr; enum : std::size_t { aligned_head_size = aligned(sizeof(head_t), alignof(std::max_align_t)) }; static byte_t * buffer(head_t* p) { return reinterpret_cast(p) + aligned_head_size; } public: void swap(variable_alloc_base& rhs) { std::swap(this->head_, rhs.head_); } void free(void* /*p*/) {} void free(void* /*p*/, std::size_t) {} }; } // namespace detail template class variable_alloc : public detail::variable_alloc_base { public: using base_t = detail::variable_alloc_base; using head_t = base_t::head_t; using alloc_policy = AllocP; private: alloc_policy alloc_; head_t* alloc_head(std::size_t size) { size = (ipc::detail::max)(ChunkSize, ipc::detail::max(size, aligned_head_size)); head_t* p = static_cast(alloc_.alloc(size)); p->free_ = (p->size_ = size) - aligned_head_size; return p; } void* alloc_new_chunk(std::size_t size) { head_t* p = alloc_head(aligned_head_size + size); if (p == nullptr) return nullptr; if (size > (ChunkSize - aligned_head_size) && head_ != nullptr) { p->next_ = head_->next_; head_->next_ = p; return base_t::buffer(p) + (p->free_ -= size); } p->next_ = head_; return base_t::buffer(head_ = p) + (p->free_ -= size); } void free_all() { while (head_ != nullptr) { head_t* curr = head_; head_ = head_->next_; alloc_.free(curr, curr->size_); } // now head_ is nullptr } public: variable_alloc() = default; variable_alloc(variable_alloc&& rhs) { this->swap(rhs); } variable_alloc& operator=(variable_alloc&& rhs) { this->swap(rhs); return (*this); } ~variable_alloc() { free_all(); } template void set_allocator(A && alc) { alloc_ = std::forward(alc); } void swap(variable_alloc& rhs) { alloc_.swap(rhs.alloc_); base_t::swap(rhs); } void clear() { free_all(); alloc_.~alloc_policy(); } void* alloc(size_t size) { if ((head_ == nullptr) || head_->free_ < size) { return alloc_new_chunk(size); } return base_t::buffer(head_) + (head_->free_ -= size); } }; } // namespace mem } // namespace ipc