//------------------------------------------------------------------------------ /* This file is part of rippled: https://github.com/ripple/rippled Copyright (c) 2012, 2013 Ripple Labs Inc. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ //============================================================================== #ifndef RIPPLE_BASICS_QALLOC_H_INCLUDED #define RIPPLE_BASICS_QALLOC_H_INCLUDED #include #include #include #include #include #include #include #include #include #include #include namespace ripple { namespace detail { template class qalloc_impl { private: class block { private: std::size_t count_ = 0; std::size_t bytes_; std::size_t remain_; std::uint8_t* free_; public: block* next; block(block const&) = delete; block& operator=(block const&) = delete; explicit block(std::size_t bytes); void* allocate(std::size_t bytes, std::size_t align); bool deallocate(); }; block* used_ = nullptr; block* free_ = nullptr; public: static constexpr auto block_size = kilobytes(256); qalloc_impl() = default; qalloc_impl(qalloc_impl const&) = delete; qalloc_impl& operator=(qalloc_impl const&) = delete; ~qalloc_impl(); void* allocate(std::size_t bytes, std::size_t align); void deallocate(void* p); }; } // namespace detail template class qalloc_type { private: template friend class qalloc_type; std::shared_ptr> impl_; public: using value_type = T; using pointer = T*; using const_pointer = T const*; using reference = typename std::add_lvalue_reference::type; using const_reference = typename std::add_lvalue_reference::type; using propagate_on_container_move_assignment = std::true_type; template struct rebind { explicit rebind() = default; using other = qalloc_type; }; qalloc_type(qalloc_type const&) = default; qalloc_type(qalloc_type&& other) noexcept = default; qalloc_type& operator=(qalloc_type const&) = default; qalloc_type& operator=(qalloc_type&&) noexcept = default; qalloc_type(); template qalloc_type(qalloc_type const& u); template U* alloc(std::size_t n); template void dealloc(U* p, std::size_t n); T* allocate(std::size_t n); void deallocate(T* p, std::size_t n); template bool operator==(qalloc_type const& u); template bool operator!=(qalloc_type const& u); qalloc_type select_on_container_copy_construction() const; private: qalloc_type select_on_copy(std::true_type) const; qalloc_type select_on_copy(std::false_type) const; }; /** Allocator optimized for delete in temporal order. This allocator is optimized for the case where objects are deleted in approximately the same order that they were created. Thread Safety: May not be called concurrently. */ using qalloc = qalloc_type; //------------------------------------------------------------------------------ namespace detail { template qalloc_impl<_>::block::block(std::size_t bytes) : bytes_(bytes - sizeof(*this)) , remain_(bytes_) , free_(reinterpret_cast(this + 1)) { } template void* qalloc_impl<_>::block::allocate(std::size_t bytes, std::size_t align) { align = std::max(align, std::alignment_of::value); auto pad = [](void const* p, std::size_t a) { auto const i = reinterpret_cast(p); return (a - (i % a)) % a; }; auto const n0 = pad(free_ + sizeof(block*), align); auto const n1 = n0 + sizeof(block*) + bytes; if (remain_ < n1) return nullptr; auto p = reinterpret_cast(free_ + n0 + sizeof(block*)); assert(pad(p - 1, std::alignment_of::value) == 0); p[-1] = this; ++count_; free_ += n1; remain_ -= n1; return p; } template bool qalloc_impl<_>::block::deallocate() { --count_; if (count_ > 0) return false; remain_ = bytes_; free_ = reinterpret_cast(this + 1); return true; } template qalloc_impl<_>::~qalloc_impl() { if (used_) { used_->~block(); std::free(used_); } while (free_) { auto const next = free_->next; free_->~block(); std::free(free_); free_ = next; } } template void* qalloc_impl<_>::allocate(std::size_t bytes, std::size_t align) { if (used_) { auto const p = used_->allocate(bytes, align); if (p) return p; used_ = nullptr; } if (free_) { auto const p = free_->allocate(bytes, align); if (p) { used_ = free_; free_ = free_->next; return p; } } std::size_t const adj_align = std::max(align, std::alignment_of::value); std::size_t const min_alloc = // align up ((sizeof(block) + sizeof(block*) + bytes) + (adj_align - 1)) & ~(adj_align - 1); auto const n = std::max(block_size, min_alloc); block* const b = new (std::malloc(n)) block(n); if (!b) Throw(); used_ = b; // VFALCO This has to succeed return used_->allocate(bytes, align); } template void qalloc_impl<_>::deallocate(void* p) { auto const b = reinterpret_cast(p)[-1]; if (b->deallocate()) { if (used_ == b) used_ = nullptr; b->next = free_; free_ = b; } } } // namespace detail //------------------------------------------------------------------------------ template qalloc_type::qalloc_type() : impl_(std::make_shared>()) { } template template qalloc_type::qalloc_type(qalloc_type const& u) : impl_(u.impl_) { } template template U* qalloc_type::alloc(std::size_t n) { if (n > std::numeric_limits::max() / sizeof(U)) Throw(); auto const bytes = n * sizeof(U); return static_cast(impl_->allocate(bytes, std::alignment_of::value)); } template template inline void qalloc_type::dealloc(U* p, std::size_t n) { impl_->deallocate(p); } template T* qalloc_type::allocate(std::size_t n) { return alloc(n); } template inline void qalloc_type::deallocate(T* p, std::size_t n) { dealloc(p, n); } template template inline bool qalloc_type::operator==(qalloc_type const& u) { return impl_.get() == u.impl_.get(); } template template inline bool qalloc_type::operator!=(qalloc_type const& u) { return !(*this == u); } template auto qalloc_type::select_on_container_copy_construction() const -> qalloc_type { return select_on_copy(std::integral_constant{}); } template auto qalloc_type::select_on_copy(std::true_type) const -> qalloc_type { return *this; // shared arena } template auto qalloc_type::select_on_copy(std::false_type) const -> qalloc_type { return {}; // new arena } } // namespace ripple #endif