From 342f3ef27e6c63cb8e41c3a6739d739ea4855209 Mon Sep 17 00:00:00 2001 From: mutouyun Date: Sun, 10 Sep 2023 12:05:29 +0800 Subject: [PATCH] The small_storage is complete. --- include/libpmr/small_storage.h | 162 +++++++++++++++++----------- test/pmr/test_pmr_small_storage.cpp | 35 +++++- 2 files changed, 131 insertions(+), 66 deletions(-) diff --git a/include/libpmr/small_storage.h b/include/libpmr/small_storage.h index bc29f64..b2e3a8a 100644 --- a/include/libpmr/small_storage.h +++ b/include/libpmr/small_storage.h @@ -208,20 +208,29 @@ struct holder_info { std::size_t sizeof_type; std::size_t count; void (*copy)(allocator const &, void const *s, void *d); + void (*move)(void *s, void *d); void (*dest)(void *p, std::size_t n); }; -template -void *holder_construct(allocator const &alloc, std::size_t n, Construct &&c) { - void *p = alloc.allocate(n * sizeof(Value)); - if (p == nullptr) return nullptr; - LIBIMP_TRY { - std::forward(c)(static_cast(p), n); - return p; - } LIBIMP_CATCH(...) { - alloc.deallocate(p, n * sizeof(Value)); - LIBIMP_THROW(, nullptr); - } +template +std::size_t full_sizeof(std::size_t count) noexcept { + return ::LIBIMP::round_up(sizeof(detail::holder_info), alignof(std::max_align_t)) + + (sizeof(Value) * count); +} + +std::size_t full_sizeof(holder_info const *info) noexcept { + return ::LIBIMP::round_up(sizeof(detail::holder_info), alignof(std::max_align_t)) + + (info->sizeof_type * info->count); +} + +void *value_ptr(holder_info *info) noexcept { + return reinterpret_cast( + ::LIBIMP::round_up(reinterpret_cast(info + 1), alignof(std::max_align_t))); +} + +void const *value_ptr(holder_info const *info) noexcept { + return reinterpret_cast( + ::LIBIMP::round_up(reinterpret_cast(info + 1), alignof(std::max_align_t))); } class holder_info_ptr { @@ -270,41 +279,46 @@ template <> class holder : public holder_base { detail::holder_info info_; - void *value_ptr_; public: + template + static std::size_t full_sizeof(std::size_t count) noexcept { + return offsetof(holder, info_) + detail::full_sizeof(count); + } + holder() noexcept - : info_{&typeid(nullptr)} - , value_ptr_(nullptr) {} + : info_{&typeid(nullptr)} {} /// \brief Constructs a holder with type of `Value`. /// alignof(Value) must be less than or equal to alignof(std::max_align_t). template = true> - holder(allocator const &alloc, ::LIBIMP::types, std::size_t n) : holder() { - value_ptr_ = detail::holder_construct(alloc, n, - ::LIBIMP::uninitialized_default_construct_n); - if (value_ptr_ == nullptr) return; + holder(allocator const &, ::LIBIMP::types, std::size_t n) : holder() { + ::LIBIMP::uninitialized_default_construct_n(static_cast(get()), n); info_.type = &typeid(Value); info_.sizeof_type = sizeof(Value); info_.count = n; - info_.copy = [](allocator const &alloc, void const *s, void *d) { + info_.copy = [](allocator const &, void const *s, void *d) { auto const &src = *static_cast(s); - auto & dst = *::LIBIMP::construct(d); - if (!src.valid()) return; - dst.value_ptr_ = detail::holder_construct(alloc, src.count(), [s = src.get()](Value *d, std::size_t n) { - std::uninitialized_copy_n(static_cast(s), n, d); - }); - if (dst.value_ptr_ == nullptr) return; + auto & dst = *static_cast(d); + std::uninitialized_copy_n(static_cast(src.get()), src.count(), + static_cast(dst.get())); dst.info_ = src.info_; }; + info_.move = [](void *s, void *d) noexcept { + auto &src = *static_cast(s); + auto &dst = *static_cast(d); + std::uninitialized_move_n(static_cast(src.get()), src.count(), + static_cast(dst.get())); + std::swap(dst.info_, src.info_); + }; info_.dest = [](void *p, std::size_t n) noexcept { ::LIBIMP::destroy_n(static_cast(p), n); }; } bool valid() const noexcept override { - return value_ptr_ != nullptr; + return (info_.type != nullptr) && (info_.type != &typeid(nullptr)); } std::type_info const &type() const noexcept override { @@ -317,7 +331,7 @@ public: } std::size_t sizeof_heap() const noexcept override { - return info_.sizeof_type * info_.count; + return 0; } std::size_t count() const noexcept override { @@ -325,18 +339,17 @@ public: } void *get() noexcept override { - return value_ptr_; + return detail::value_ptr(&info_); } void const *get() const noexcept override { - return value_ptr_; + return detail::value_ptr(&info_); } void move_to(allocator const &, void *p) noexcept override { auto *des = ::LIBIMP::construct(p); if (!valid()) return; - std::swap(value_ptr_, des->value_ptr_); - std::swap(info_, des->info_); + info_.move(this, des); } void copy_to(allocator const &alloc, void *p) const noexcept(false) override { @@ -345,10 +358,9 @@ public: info_.copy(alloc, this, des); } - void destroy(allocator const &alloc) noexcept override { + void destroy(allocator const &) noexcept override { if (!valid()) return; - info_.dest(value_ptr_, count()); - alloc.deallocate(value_ptr_, sizeof_heap()); + info_.dest(get(), count()); } }; @@ -363,7 +375,7 @@ class holder : public holder_base { public: holder() noexcept - : info_ptr_ (nullptr){} + : info_ptr_(nullptr) {} /// \brief Constructs a holder with type of `Value`. /// alignof(Value) must be less than or equal to alignof(std::max_align_t). @@ -378,8 +390,7 @@ public: info_p->count = n; info_p->copy = [](allocator const &alloc, void const *s, void *d) { auto const &src = *static_cast(s); - auto & dst = *::LIBIMP::construct(d); - if (!src.valid()) return; + auto & dst = *static_cast(d); detail::holder_info_ptr info_p{alloc, dst.info_ptr_, sizeof(Value) * src.count()}; if (!info_p) return; ::LIBIMP::uninitialized_default_construct_n(static_cast(dst.get()), src.count()); @@ -408,8 +419,7 @@ public: std::size_t sizeof_heap() const noexcept override { if (!valid()) return 0; - return ::LIBIMP::round_up(sizeof(detail::holder_info), alignof(std::max_align_t)) - + (info_ptr_->sizeof_type * info_ptr_->count); + return detail::full_sizeof(info_ptr_); } std::size_t count() const noexcept override { @@ -418,13 +428,11 @@ public: } void *get() noexcept override { - return reinterpret_cast( - ::LIBIMP::round_up(reinterpret_cast(info_ptr_ + 1), alignof(std::max_align_t))); + return detail::value_ptr(info_ptr_); } void const *get() const noexcept override { - return reinterpret_cast( - ::LIBIMP::round_up(reinterpret_cast(info_ptr_ + 1), alignof(std::max_align_t))); + return detail::value_ptr(info_ptr_); } void move_to(allocator const &, void *p) noexcept override { @@ -447,8 +455,13 @@ public: }; /** - * \class small_storage + * \class template small_storage * \brief Unified SSO (Small Size Optimization). + * + * \note `small_storage` does not release resources at destructor time, + * the user needs to manually call the `release` function and + * specify the appropriate allocator to complete the resource release. + * * \tparam N The size of the storage. */ template @@ -458,6 +471,34 @@ class small_storage { alignas(std::max_align_t) std::array<::LIBIMP::byte, N> storage_; + /// \brief Try to construct an object on the stack, and if there is not enough space, + /// the memory allocator allocates heap memory to complete the construction. + template + struct applicant { + template + T *operator()(small_storage *self, allocator const &alloc, A &&...args) const noexcept(false) { + self->get_holder()->destroy(alloc); + constexpr bool on_stack = (sizeof(holder) <= N); + ::LIBIMP::construct>(self->get_holder(), alloc, std::forward(args)...); + return self->as(); + } + }; + + /// \brief Try to construct an array of objects on the stack, and if there is not enough space, + /// the memory allocator allocates heap memory to complete the construction. + template + struct applicant { + T *operator()(small_storage *self, allocator const &alloc, std::size_t n) const noexcept(false) { + self->get_holder()->destroy(alloc); + if (holder::full_sizeof(n) <= N) { + ::LIBIMP::construct>(self->get_holder(), alloc, ::LIBIMP::types{}, n); + } else { + ::LIBIMP::construct>(self->get_holder(), alloc, ::LIBIMP::types{}, n); + } + return self->as(); + } + }; + public: small_storage(small_storage const &) = delete; small_storage &operator=(small_storage const &) = delete; @@ -495,23 +536,23 @@ public: } template - Value *As() noexcept { + Value *as() noexcept { return static_cast(get_holder()->get()); } template - Value const *As() const noexcept { + Value const *as() const noexcept { return static_cast(get_holder()->get()); } template - Value &AsRef() noexcept { - return *As(); + Value &as_ref() noexcept { + return *as(); } template - Value const &AsRef() const noexcept { - return *As(); + Value const &as_ref() const noexcept { + return *as(); } void move_to(allocator const &alloc, small_storage &des) noexcept { @@ -522,25 +563,16 @@ public: get_holder()->copy_to(alloc, des.get_holder()); } - void reset(allocator const &alloc) noexcept { + template + auto acquire(allocator const &alloc, A &&...args) noexcept(false) { + return applicant{}(this, alloc, std::forward(args)...); + } + + void release(allocator const &alloc) noexcept { if (!valid()) return; get_holder()->destroy(alloc); ::LIBIMP::construct(get_holder()); } - - template - Value *acquire(allocator const &alloc, ::LIBIMP::types, A &&...args) { - get_holder()->destroy(alloc); - constexpr bool on_stack = (sizeof(holder) <= N); - return ::LIBIMP::construct>(get_holder(), alloc, std::forward(args)...); - } - - template - Value *acquire(allocator const &alloc, ::LIBIMP::types, std::size_t n) { - get_holder()->destroy(alloc); - constexpr bool on_stack = (sizeof(holder) <= N); - return ::LIBIMP::construct>(get_holder(), alloc, ::LIBIMP::types{}, n); - } }; LIBPMR_NAMESPACE_END_ diff --git a/test/pmr/test_pmr_small_storage.cpp b/test/pmr/test_pmr_small_storage.cpp index 6e7b547..34c2a44 100644 --- a/test/pmr/test_pmr_small_storage.cpp +++ b/test/pmr/test_pmr_small_storage.cpp @@ -103,7 +103,7 @@ TEST(small_storage, sizeof) { EXPECT_EQ(sizeof(pmr::holder_null), sizeof(void *)); EXPECT_EQ(sizeof(pmr::holder), sizeof(void *) + imp::round_up(sizeof(int), alignof(void *))); EXPECT_EQ(sizeof(pmr::holder), sizeof(void *) + sizeof(void *)); - EXPECT_EQ(sizeof(pmr::holder), sizeof(void *) + sizeof(void *) + sizeof(pmr::detail::holder_info)); + EXPECT_EQ(sizeof(pmr::holder), sizeof(void *) + sizeof(pmr::detail::holder_info)); EXPECT_EQ(sizeof(pmr::holder), sizeof(void *) + sizeof(void *)); // pmr::small_storage<4> s1; @@ -117,3 +117,36 @@ TEST(small_storage, construct) { pmr::small_storage<64> ss; SUCCEED(); } + +TEST(small_storage, acquire) { + pmr::small_storage<128> ss; + pmr::allocator alc; + ASSERT_FALSE(ss.valid()); + int *p = ss.acquire(alc, 3); + ASSERT_TRUE(ss.valid()); + ASSERT_NE(p, nullptr); + ASSERT_EQ(*p, 3); + ASSERT_EQ(p, ss.as()); + ASSERT_EQ(ss.count(), 1); + ASSERT_EQ(ss.sizeof_heap(), 0); + ASSERT_EQ(ss.sizeof_type(), sizeof(int)); + + p = ss.acquire(alc, 3); + ASSERT_TRUE(ss.valid()); + ASSERT_NE(p, nullptr); + ASSERT_EQ(p, ss.as()); + ASSERT_EQ(ss.count(), 3); + ASSERT_EQ(ss.sizeof_heap(), 0); + ASSERT_EQ(ss.sizeof_type(), sizeof(int)); + + p = ss.acquire(alc, 30); + ASSERT_TRUE(ss.valid()); + ASSERT_NE(p, nullptr); + ASSERT_EQ(p, ss.as()); + ASSERT_EQ(ss.count(), 30); + ASSERT_EQ(ss.sizeof_heap(), sizeof(int) * 30 + sizeof(pmr::detail::holder_info)); + ASSERT_EQ(ss.sizeof_type(), sizeof(int)); + + ss.release(alc); + SUCCEED(); +}