// // based on boost.json // // Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com) // Copyright (c) 2020 Krystian Stasiowski (sdkrystian@gmail.com) // Copyright (c) 2023 Klemens Morgenstern (klemens.morgenstern@gmx.net) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef BOOST_COBALT_DETAIL_MONOTONIC_BUFFER_RESOURCE_HPP #define BOOST_COBALT_DETAIL_MONOTONIC_BUFFER_RESOURCE_HPP #include #include namespace boost::cobalt::detail { struct monotonic_resource { private: struct block_ { void* p; std::size_t avail; std::size_t size; std::align_val_t aligned; block_* next; }; block_ buffer_; block_* head_ = &buffer_; std::size_t chunk_size_{buffer_.size}; public: constexpr monotonic_resource(void * buffer, std::size_t size) : buffer_{buffer, size, size, std::align_val_t{0u}, nullptr} {} constexpr monotonic_resource(std::size_t chunk_size = 1024) : buffer_{nullptr, 0u, 0u, std::align_val_t{0u}, nullptr}, chunk_size_(chunk_size) {} monotonic_resource(monotonic_resource && lhs) noexcept = delete; constexpr ~monotonic_resource() { if (head_ != &buffer_) release(); } constexpr void release() { head_ = &buffer_; auto nx = buffer_.next; head_->next = nullptr; head_->avail = head_->size; while (nx != nullptr) { auto p = nx; nx = nx->next; #if defined(__cpp_sized_deallocation) const auto size = sizeof(block_) + p->size; operator delete(p->p, size, p->aligned); #else operator delete(p->p, p->aligned); #endif } } constexpr void * allocate(std::size_t size, std::align_val_t align_ = std::align_val_t(alignof(std::max_align_t))) { const auto align = (std::max)(static_cast(align_), alignof(block_)); // let's say size = 11, and align is 8, that leaves us with 3 { const auto align_offset = size % align ; // padding is 5 const auto padding = align - align_offset; const auto needed_size = size + padding; if (needed_size <= head_->avail) // fits, but we need to check alignment too { const auto offset = head_->size - head_->avail; auto pp = static_cast(head_->p) + offset + padding; head_->avail -= needed_size; return pp; // done } } // alright, we need to alloc something. const auto mem_size = (std::max)(chunk_size_, size); // add padding at the end const auto offset = (mem_size % alignof(block_)); const auto padding = offset == 0 ? 0u : (alignof(block_) - offset); // size to allocate const auto raw_size = mem_size + padding; const auto alloc_size = raw_size + sizeof(block_); const auto aligned = std::align_val_t(align); const auto mem = ::operator new(alloc_size, aligned); const auto block_location = static_cast(mem) + mem_size + offset; head_ = head_->next = new (block_location) block_{mem, raw_size - size, raw_size, aligned, nullptr}; return mem; } }; template struct monotonic_allocator { template monotonic_allocator(monotonic_allocator alloc) : resource_(alloc.resource_) { } using value_type = T; using size_type = std::size_t; using difference_type = std::ptrdiff_t; using propagate_on_container_move_assignment = std::true_type; [[nodiscard]] constexpr T* allocate( std::size_t n ) { if (resource_) return static_cast( resource_->allocate( sizeof(T) * n, std::align_val_t(alignof(T)))); else return std::allocator().allocate(n); } constexpr void deallocate( T* p, std::size_t n ) { if (!resource_) std::allocator().deallocate(p, n); } monotonic_allocator(monotonic_resource * resource = nullptr) : resource_(resource) {} private: template friend struct monotonic_allocator; monotonic_resource * resource_{nullptr}; }; } #endif //BOOST_COBALT_DETAIL_MONOTONIC_BUFFER_RESOURCE_HPP