diff --git a/include/libpmr/monotonic_buffer_resource.h b/include/libpmr/monotonic_buffer_resource.h index 0fa2df9..fb8079b 100644 --- a/include/libpmr/monotonic_buffer_resource.h +++ b/include/libpmr/monotonic_buffer_resource.h @@ -25,9 +25,22 @@ LIBPMR_NAMESPACE_BEG_ */ class LIBIMP_EXPORT monotonic_buffer_resource { + allocator upstream_; + + struct node { + node *next; + std::size_t size; + } *free_list_; + + ::LIBIMP::byte *head_; + ::LIBIMP::byte *tail_; + + ::LIBIMP::span<::LIBIMP::byte> const initial_buffer_; + std::size_t initial_size_; + public: monotonic_buffer_resource() noexcept; - explicit monotonic_buffer_resource(allocator upstream); + explicit monotonic_buffer_resource(allocator upstream) noexcept; explicit monotonic_buffer_resource(std::size_t initial_size); monotonic_buffer_resource(std::size_t initial_size, allocator upstream); monotonic_buffer_resource(::LIBIMP::span<::LIBIMP::byte> buffer) noexcept; diff --git a/src/libpmr/monotonic_buffer_resource.cpp b/src/libpmr/monotonic_buffer_resource.cpp index 16bbeba..9d30f3a 100644 --- a/src/libpmr/monotonic_buffer_resource.cpp +++ b/src/libpmr/monotonic_buffer_resource.cpp @@ -1,52 +1,107 @@ +#include + #include "libimp/log.h" +#include "libimp/aligned.h" #include "libpmr/monotonic_buffer_resource.h" LIBPMR_NAMESPACE_BEG_ +namespace { -monotonic_buffer_resource::monotonic_buffer_resource() noexcept { - +template +Node *make_node(allocator const &upstream, std::size_t initial_size, std::size_t alignment) { + LIBIMP_LOG_(); + auto sz = ::LIBIMP::round_up(sizeof(Node), alignment) + initial_size; + auto *node = static_cast(upstream.allocate(sz)); + if (node == nullptr) { + log.error("failed: allocate memory for `monotonic_buffer_resource`."); + return nullptr; + } + node->next = nullptr; + node->size = sz; + return node; } -monotonic_buffer_resource::monotonic_buffer_resource(allocator upstream) { +} // namespace -} +monotonic_buffer_resource::monotonic_buffer_resource() noexcept + : monotonic_buffer_resource(allocator{}) {} -monotonic_buffer_resource::monotonic_buffer_resource(std::size_t initial_size) { +monotonic_buffer_resource::monotonic_buffer_resource(allocator upstream) noexcept + : upstream_ (std::move(upstream)) + , free_list_(nullptr) + , head_ (nullptr) + , tail_ (nullptr) + , initial_size_(0) {} -} +monotonic_buffer_resource::monotonic_buffer_resource(std::size_t initial_size) + : monotonic_buffer_resource(initial_size, allocator{}) {} -monotonic_buffer_resource::monotonic_buffer_resource(std::size_t initial_size, allocator upstream) { +monotonic_buffer_resource::monotonic_buffer_resource(std::size_t initial_size, allocator upstream) + : upstream_ (std::move(upstream)) + , free_list_(make_node(upstream_, initial_size, alignof(std::max_align_t))) + , head_ (free_list_ == nullptr ? reinterpret_cast<::LIBIMP::byte *>(free_list_) + sizeof(node) : nullptr) + , tail_ (head_ + initial_size) + , initial_size_(initial_size) {} -} +monotonic_buffer_resource::monotonic_buffer_resource(::LIBIMP::span<::LIBIMP::byte> buffer) noexcept + : monotonic_buffer_resource(buffer, allocator{}) {} -monotonic_buffer_resource::monotonic_buffer_resource(::LIBIMP::span<::LIBIMP::byte> buffer) noexcept { - -} - -monotonic_buffer_resource::monotonic_buffer_resource(::LIBIMP::span<::LIBIMP::byte> buffer, allocator upstream) noexcept { - -} +monotonic_buffer_resource::monotonic_buffer_resource(::LIBIMP::span<::LIBIMP::byte> buffer, allocator upstream) noexcept + : upstream_ (std::move(upstream)) + , free_list_(nullptr) + , head_ (buffer.begin()) + , tail_ (buffer.end()) + , initial_buffer_(buffer) + , initial_size_(initial_buffer_.size()) {} monotonic_buffer_resource::~monotonic_buffer_resource() { - + release(); } allocator monotonic_buffer_resource::upstream_resource() const noexcept { - + return upstream_; } void monotonic_buffer_resource::release() { - + while (free_list_ != nullptr) { + auto *next = free_list_->next; + upstream_.deallocate(free_list_, free_list_->size); + free_list_ = next; + } + head_ = initial_buffer_.begin(); + tail_ = initial_buffer_.end(); } void *monotonic_buffer_resource::allocate(std::size_t bytes, std::size_t alignment) noexcept { - + LIBIMP_LOG_(); + if (bytes == 0) { + log.error("failed: allocate bytes = 0."); + return nullptr; + } + if ((alignment & (alignment - 1)) != 0) { + log.error("failed: allocate alignment is not power of 2."); + return nullptr; + } + auto *p = reinterpret_cast<::LIBIMP::byte *>(::LIBIMP::round_up(reinterpret_cast(head_), alignment)); + auto remain = static_cast(tail_ - p); + if (remain < bytes) { + initial_size_ = (std::max)(initial_size_ * 2, bytes); + auto *node = make_node(upstream_, initial_size_, alignment); + if (node == nullptr) return nullptr; + node->next = free_list_; + free_list_ = node; + p = reinterpret_cast<::LIBIMP::byte *>(free_list_) + ::LIBIMP::round_up(sizeof(node), alignment); + } + return p; } void monotonic_buffer_resource::deallocate(void *p, std::size_t bytes, std::size_t alignment) noexcept { - + static_cast(p); + static_cast(bytes); + static_cast(alignment); + // Do nothing. } LIBPMR_NAMESPACE_END_ diff --git a/test/pmr/test_pmr_monotonic_buffer_resource.cpp b/test/pmr/test_pmr_monotonic_buffer_resource.cpp new file mode 100644 index 0000000..1774519 --- /dev/null +++ b/test/pmr/test_pmr_monotonic_buffer_resource.cpp @@ -0,0 +1,15 @@ + +#include "gtest/gtest.h" + +#include "libpmr/monotonic_buffer_resource.h" + +TEST(monotonic_buffer_resource, construct) { + { pmr::monotonic_buffer_resource tmp; } + pmr::monotonic_buffer_resource{}; + pmr::monotonic_buffer_resource{pmr::allocator{}}; + pmr::monotonic_buffer_resource{0}; + pmr::monotonic_buffer_resource{0, pmr::allocator{}}; + pmr::monotonic_buffer_resource{imp::span{}}; + pmr::monotonic_buffer_resource{imp::span{}, pmr::allocator{}}; + SUCCEED(); +}