diff --git a/include/libconcur/data_model.h b/include/libconcur/data_model.h index 5c5b5c4..514b26d 100644 --- a/include/libconcur/data_model.h +++ b/include/libconcur/data_model.h @@ -77,18 +77,15 @@ private: }; data *init(index_t circ_size) noexcept { - if (!data_allocator_) { - return nullptr; - } void *data_ptr = nullptr; LIBIMP_TRY { - data_ptr = data_allocator_.alloc(data::size_of(circ_size)); + data_ptr = data_allocator_.allocate(data::size_of(circ_size)); if (data_ptr == nullptr) { return nullptr; } return ::LIBIMP::construct(data_ptr, circ_size); } LIBIMP_CATCH(...) { - data_allocator_.dealloc(data_ptr, data::size_of(circ_size)); + data_allocator_.deallocate(data_ptr, data::size_of(circ_size)); return nullptr; } } @@ -128,7 +125,7 @@ public: if (valid()) { auto sz = data_->byte_size(); (void)::LIBIMP::destroy(data_); - data_allocator_.dealloc(data_, sz); + data_allocator_.deallocate(data_, sz); } } @@ -148,7 +145,7 @@ public: : data_model(default_circle_buffer_size) {} bool valid() const noexcept { - return (data_ != nullptr) && data_allocator_.valid(); + return data_ != nullptr; } explicit operator bool() const noexcept { diff --git a/include/libconcur/element.h b/include/libconcur/element.h index ba08690..7c816ec 100644 --- a/include/libconcur/element.h +++ b/include/libconcur/element.h @@ -64,7 +64,7 @@ public: using value_type = T; template - element(A &&... args) + element(A &&...args) noexcept(noexcept(T{std::forward(args)...})) : f_ct_{state::invalid_value} , data_{std::forward(args)...} {} diff --git a/include/libimp/byte.h b/include/libimp/byte.h index da646be..982d173 100644 --- a/include/libimp/byte.h +++ b/include/libimp/byte.h @@ -44,8 +44,7 @@ class byte { std::uint8_t bits_; public: - constexpr byte() noexcept - : byte(0) {} + byte() noexcept = default; template > constexpr byte(T v) noexcept diff --git a/include/libimp/construct.h b/include/libimp/construct.h index 99af50c..851c657 100644 --- a/include/libimp/construct.h +++ b/include/libimp/construct.h @@ -23,7 +23,7 @@ LIBIMP_NAMESPACE_BEG_ */ template -auto construct(void *p, A &&... args) +auto construct(void *p, A &&...args) -> std::enable_if_t<::std::is_constructible::value, T *> { #if defined(LIBIMP_CPP_20) return std::construct_at(static_cast(p), std::forward(args)...); @@ -33,7 +33,7 @@ auto construct(void *p, A &&... args) } template -auto construct(void *p, A &&... args) +auto construct(void *p, A &&...args) -> std::enable_if_t::value, T *> { return ::new (p) T{std::forward(args)...}; } diff --git a/include/libimp/log.h b/include/libimp/log.h index 4231a40..bf6c1a1 100644 --- a/include/libimp/log.h +++ b/include/libimp/log.h @@ -122,7 +122,7 @@ public: , level_limit_(level_limit) {} template - grip const &operator()(log::level l, A &&... args) const noexcept { + grip const &operator()(log::level l, A &&...args) const noexcept { if (underlyof(l) < underlyof(level_limit_)) { return *this; } diff --git a/include/libimp/pimpl.h b/include/libimp/pimpl.h index 63992d0..df7787a 100644 --- a/include/libimp/pimpl.h +++ b/include/libimp/pimpl.h @@ -24,7 +24,7 @@ struct is_comfortable { }; template -auto make(A &&... args) -> std::enable_if_t::value, T *> { +auto make(A &&...args) -> std::enable_if_t::value, T *> { T *buf {}; // construct an object using memory of a pointer construct(&buf, std::forward(args)...); @@ -42,7 +42,7 @@ auto clear(T *p) noexcept -> std::enable_if_t::value> { } template -auto make(A &&... args) -> std::enable_if_t::value, T *> { +auto make(A &&...args) -> std::enable_if_t::value, T *> { return new T{std::forward(args)...}; } @@ -60,7 +60,7 @@ template class Obj { public: template - static T *make(A &&... args) { + static T *make(A &&...args) { return pimpl::make(std::forward(args)...); } diff --git a/include/libimp/result.h b/include/libimp/result.h index aea6738..3960f88 100644 --- a/include/libimp/result.h +++ b/include/libimp/result.h @@ -142,7 +142,7 @@ public: typename = not_match, typename = decltype(type_traits_t::init_code(std::declval() , std::declval()...))> - result(A &&... args) noexcept { + result(A &&...args) noexcept { type_traits_t::init_code(code_, std::forward(args)...); } @@ -171,7 +171,7 @@ public: typename = not_match, typename = decltype(type_traits_t::init_code(std::declval() , std::declval()...))> - result(A &&... args) noexcept { + result(A &&...args) noexcept { type_traits_t::init_code(code_, std::forward(args)...); } diff --git a/include/libpmr/allocator.h b/include/libpmr/allocator.h index fb61efe..d24bab2 100644 --- a/include/libpmr/allocator.h +++ b/include/libpmr/allocator.h @@ -32,63 +32,54 @@ LIBPMR_NAMESPACE_BEG_ */ class LIBIMP_EXPORT allocator { - class holder_base { + class holder_mr_base { public: - virtual ~holder_base() noexcept = default; - virtual void *alloc(std::size_t) = 0; - virtual void dealloc(void *, std::size_t) = 0; - virtual bool valid() const noexcept = 0; + virtual ~holder_mr_base() noexcept = default; + virtual void *alloc(std::size_t, std::size_t) const = 0; + virtual void dealloc(void *, std::size_t, std::size_t) const = 0; }; - class holder_null : public holder_base { - public: - void *alloc(std::size_t) override { return nullptr; } - void dealloc(void *, std::size_t) override {} - bool valid() const noexcept override { return false; } - }; - - template - class holder_memory_resource; - - /** - * \brief A memory resource pointer holder class for type erasure. - * \tparam MR memory resource type - */ - template - class holder_memory_resource> : public holder_base { - MR *p_mem_res_; - - public: - holder_memory_resource(MR *p_mr) noexcept - : p_mem_res_(p_mr) {} - - void *alloc(std::size_t s) override { - return p_mem_res_->allocate(s); - } - - void dealloc(void *p, std::size_t s) override { - p_mem_res_->deallocate(p, s); - } - - bool valid() const noexcept override { - return p_mem_res_ != nullptr; - } - }; + template + class holder_mr; /** * \brief An empty holding class used to calculate a reasonable memory size for the holder. * \tparam MR cannot be converted to the type of memory resource */ template - class holder_memory_resource : public holder_null { - MR *p_dummy_; + class holder_mr : public holder_mr_base { + protected: + MR *res_; + + public: + holder_mr(MR *p_mr) noexcept + : res_(p_mr) {} }; - using void_holder_type = holder_memory_resource; - alignas(void_holder_type) std::array<::LIBIMP::byte, sizeof(void_holder_type)> holder_; + /** + * \brief A memory resource pointer holder class for type erasure. + * \tparam MR memory resource type + */ + template + class holder_mr> : public holder_mr { + public: + holder_mr(MR *p_mr) noexcept + : holder_mr{p_mr} {} - holder_base & get_holder() noexcept; - holder_base const &get_holder() const noexcept; + void *alloc(std::size_t s, std::size_t a) const override { + return res_->allocate(s, a); + } + + void dealloc(void *p, std::size_t s, std::size_t a) const override { + res_->deallocate(p, s, a); + } + }; + + using void_holder_t = holder_mr; + alignas(void_holder_t) std::array<::LIBIMP::byte, sizeof(void_holder_t)> holder_; + + holder_mr_base & get_holder() noexcept; + holder_mr_base const &get_holder() const noexcept; public: allocator() noexcept; @@ -97,24 +88,25 @@ public: allocator(allocator const &other) noexcept = default; allocator &operator=(allocator const &other) & noexcept = default; - allocator(allocator &&other) noexcept; - allocator &operator=(allocator &&other) & noexcept; + allocator(allocator &&other) noexcept = default; + allocator &operator=(allocator &&other) & noexcept = default; /// \brief Constructs a allocator from a memory resource pointer /// The lifetime of the pointer must be longer than that of allocator. template = true> - allocator(T *p_mr) : allocator() { - if (p_mr == nullptr) return; - ::LIBIMP::construct>(holder_.data(), p_mr); + allocator(T *p_mr) noexcept { + if (p_mr == nullptr) { + ::LIBIMP::construct>(holder_.data(), new_delete_resource::get()); + return; + } + ::LIBIMP::construct>(holder_.data(), p_mr); } void swap(allocator &other) noexcept; - bool valid() const noexcept; - explicit operator bool() const noexcept; /// \brief Allocate/deallocate memory. - void *alloc(std::size_t s); - void dealloc(void *p, std::size_t s); + void *allocate(std::size_t s, std::size_t = alignof(std::max_align_t)) const; + void deallocate(void *p, std::size_t s, std::size_t = alignof(std::max_align_t)) const; }; LIBPMR_NAMESPACE_END_ diff --git a/include/libpmr/holder_base.h b/include/libpmr/holder_base.h new file mode 100644 index 0000000..fc52c6d --- /dev/null +++ b/include/libpmr/holder_base.h @@ -0,0 +1,122 @@ +/** + * \file libpmr/small_storage.h + * \author mutouyun (orz@orzz.org) + * \brief Unified SSO (Small Size Optimization) holder base. + * \date 2023-09-02 + */ +#pragma once + +#include +#include +#include +#include +#include + +#include "libimp/export.h" +#include "libimp/construct.h" +#include "libimp/byte.h" +#include "libimp/generic.h" + +#include "libpmr/def.h" + +LIBPMR_NAMESPACE_BEG_ + +class LIBIMP_EXPORT allocator; + +/** + * \class holder_base + * \brief Data holder base class. +*/ +class holder_base { +public: + virtual ~holder_base() noexcept = default; + virtual bool valid() const noexcept = 0; + virtual std::type_info const &type() const noexcept = 0; + virtual std::size_t sizeof_type() const noexcept = 0; + virtual std::size_t count() const noexcept = 0; + virtual std::size_t size () const noexcept = 0; + virtual void *get() noexcept = 0; + virtual void const *get() const noexcept = 0; + virtual void move_to(allocator const &, void *) noexcept = 0; + virtual void copy_to(allocator const &, void *) const noexcept(false) = 0; +}; + +/** + * \class holder_null + * \brief A holder implementation that does not hold any data objects. +*/ +class holder_null : public holder_base { +public: + bool valid() const noexcept override { return false; } + std::type_info const &type() const noexcept override { return typeid(nullptr);} + std::size_t sizeof_type() const noexcept override { return 0; } + std::size_t count() const noexcept override { return 0; } + std::size_t size () const noexcept override { return 0; } + void *get() noexcept override { return nullptr; } + void const *get() const noexcept override { return nullptr; } + + /// \brief The passed destination pointer is never null, + /// and the memory to which it points is uninitialized. + void move_to(allocator const &, void *) noexcept override {} + void copy_to(allocator const &, void *) const noexcept(false) override {} +}; + +template +class holder; + +/** + * \class template holder + * \brief A holder implementation that holds a data object if type `Value` on stack memory. + * \tparam Value The storage type of the holder. + */ +template +class holder : public holder_base { + + alignas(alignof(Value)) std::array<::LIBIMP::byte, sizeof(Value)> value_storage_; + +public: + holder() = default; // uninitialized + + template + holder(::LIBIMP::in_place_t, A &&...args) { + ::LIBIMP::construct(value_storage_.data(), std::forward(args)...); + } + + bool valid() const noexcept override { + return true; + } + + std::type_info const &type() const noexcept override { + return typeid(Value); + } + + std::size_t sizeof_type() const noexcept override { + return sizeof(Value); + } + + std::size_t count() const noexcept override { + return 1; + } + + std::size_t size() const noexcept override { + return value_storage_.size(); + } + + void *get() noexcept override { + return value_storage_.data(); + } + + void const *get() const noexcept override { + return value_storage_.data(); + } + + void move_to(allocator const &, void *p) noexcept override { + ::LIBIMP::construct(p, ::LIBIMP::in_place, std::move(*static_cast(get()))); + } + + void copy_to(allocator const &, void *p) const noexcept(false) override { + ::LIBIMP::construct(p, ::LIBIMP::in_place, *static_cast(get())); + } +}; + +LIBPMR_NAMESPACE_END_ diff --git a/include/libpmr/small_storage.h b/include/libpmr/small_storage.h new file mode 100644 index 0000000..918dc93 --- /dev/null +++ b/include/libpmr/small_storage.h @@ -0,0 +1,35 @@ +/** + * \file libpmr/small_storage.h + * \author mutouyun (orz@orzz.org) + * \brief Unified SSO (Small Size Optimization). + * \date 2023-09-02 + */ +#pragma once + +#include +#include +#include +#include +#include +#include + +#include "libimp/export.h" +#include "libimp/construct.h" +#include "libimp/byte.h" +#include "libimp/generic.h" + +#include "libpmr/def.h" +#include "libpmr/holder_base.h" + +LIBPMR_NAMESPACE_BEG_ + +/** + * \class template holder + * \brief A holder implementation that holds a data object if type `Value` on heap memory. + * \tparam Value The storage type of the holder. + */ +template +class holder : public holder_base { +}; + +LIBPMR_NAMESPACE_END_ diff --git a/src/libpmr/allocator.cpp b/src/libpmr/allocator.cpp index 4760347..68eb9fc 100644 --- a/src/libpmr/allocator.cpp +++ b/src/libpmr/allocator.cpp @@ -5,54 +5,31 @@ LIBPMR_NAMESPACE_BEG_ -allocator::holder_base &allocator::get_holder() noexcept { - return *reinterpret_cast(holder_.data()); +allocator::holder_mr_base &allocator::get_holder() noexcept { + return *reinterpret_cast(holder_.data()); } -allocator::holder_base const &allocator::get_holder() const noexcept { - return *reinterpret_cast(holder_.data()); +allocator::holder_mr_base const &allocator::get_holder() const noexcept { + return *reinterpret_cast(holder_.data()); } -allocator::allocator() noexcept { - ::LIBIMP::construct(holder_.data()); -} +allocator::allocator() noexcept + : allocator(new_delete_resource::get()) {} allocator::~allocator() noexcept { ::LIBIMP::destroy(&get_holder()); } -allocator::allocator(allocator &&other) noexcept - : allocator(other) /*copy*/ { - ::LIBIMP::construct(other.holder_.data()); -} - -allocator &allocator::operator=(allocator &&other) & noexcept { - if (this == &other) return *this; - this->holder_ = other.holder_; - ::LIBIMP::construct(other.holder_.data()); - return *this; -} - void allocator::swap(allocator &other) noexcept { std::swap(this->holder_, other.holder_); } -bool allocator::valid() const noexcept { - return get_holder().valid(); +void *allocator::allocate(std::size_t s, std::size_t a) const { + return get_holder().alloc(s, a); } -allocator::operator bool() const noexcept { - return valid(); -} - -void *allocator::alloc(std::size_t s) { - if (!valid()) return nullptr; - return get_holder().alloc(s); -} - -void allocator::dealloc(void *p, std::size_t s) { - if (!valid()) return; - get_holder().dealloc(p, s); +void allocator::deallocate(void *p, std::size_t s, std::size_t a) const { + get_holder().dealloc(p, s, a); } LIBPMR_NAMESPACE_END_ diff --git a/test/pmr/test_pmr_allocator.cpp b/test/pmr/test_pmr_allocator.cpp index a1d2368..8173c82 100644 --- a/test/pmr/test_pmr_allocator.cpp +++ b/test/pmr/test_pmr_allocator.cpp @@ -8,71 +8,71 @@ TEST(allocator, construct) { pmr::allocator alc; - EXPECT_FALSE(alc.valid()); - EXPECT_FALSE(alc); + SUCCEED(); } -TEST(allocator, construct_with_memory_resource) { - pmr::new_delete_resource mem_res; - pmr::allocator alc {&mem_res}; - EXPECT_TRUE(alc.valid()); - EXPECT_TRUE(alc); - - auto p = alc.alloc(128); +TEST(allocator, construct_value_initialization) { + pmr::allocator alc{}; + auto p = alc.allocate(128); EXPECT_NE(p, nullptr); - EXPECT_NO_THROW(alc.dealloc(p, 128)); + EXPECT_NO_THROW(alc.deallocate(p, 128)); } +namespace { + +class dummy_resource { +public: + void *allocate(std::size_t, std::size_t = 0) noexcept { + return nullptr; + } + void deallocate(void *, std::size_t, std::size_t = 0) noexcept { + return; + } +}; + +} // namespace + TEST(allocator, construct_copy_move) { pmr::new_delete_resource mem_res; + dummy_resource dummy_res; + pmr::allocator alc1{&mem_res}, alc2{&dummy_res}; + auto p = alc1.allocate(128); + ASSERT_NE(p, nullptr); + ASSERT_NO_THROW(alc1.deallocate(p, 128)); + ASSERT_EQ(alc2.allocate(128), nullptr); - pmr::allocator alc1 {&mem_res}, alc2; - EXPECT_TRUE (alc1.valid()); - EXPECT_TRUE (alc1); - EXPECT_FALSE(alc2.valid()); - EXPECT_FALSE(alc2); + pmr::allocator alc3{alc1}, alc4{alc2}, alc5{std::move(alc1)}; - pmr::allocator alc3 {alc1}, alc4{alc2}, alc5 {std::move(alc1)}; - EXPECT_TRUE (alc3.valid()); - EXPECT_TRUE (alc3); - EXPECT_FALSE(alc4.valid()); - EXPECT_FALSE(alc4); - EXPECT_TRUE (alc5.valid()); - EXPECT_TRUE (alc5); - EXPECT_FALSE(alc1.valid()); - EXPECT_FALSE(alc1); + p = alc3.allocate(128); + ASSERT_NE(p, nullptr); + ASSERT_NO_THROW(alc3.deallocate(p, 128)); + + ASSERT_EQ(alc4.allocate(128), nullptr); + + p = alc5.allocate(128); + ASSERT_NE(p, nullptr); + ASSERT_NO_THROW(alc5.deallocate(p, 128)); } TEST(allocator, swap) { pmr::new_delete_resource mem_res; - - pmr::allocator alc1 {&mem_res}, alc2; - EXPECT_TRUE (alc1.valid()); - EXPECT_TRUE (alc1); - EXPECT_FALSE(alc2.valid()); - EXPECT_FALSE(alc2); - + dummy_resource dummy_res; + pmr::allocator alc1{&mem_res}, alc2{&dummy_res}; alc1.swap(alc2); - EXPECT_FALSE(alc1.valid()); - EXPECT_FALSE(alc1); - EXPECT_TRUE (alc2.valid()); - EXPECT_TRUE (alc2); + auto p = alc2.allocate(128); + ASSERT_NE(p, nullptr); + ASSERT_NO_THROW(alc2.deallocate(p, 128)); + ASSERT_EQ(alc1.allocate(128), nullptr); } TEST(allocator, invalid_alloc_free) { - pmr::new_delete_resource mem_res; + pmr::allocator alc1; + EXPECT_EQ(alc1.allocate(0), nullptr); + EXPECT_NO_THROW(alc1.deallocate(nullptr, 128)); + EXPECT_NO_THROW(alc1.deallocate(nullptr, 0)); + EXPECT_NO_THROW(alc1.deallocate(&alc1, 0)); +} - pmr::allocator alc1 {&mem_res}, alc2; - EXPECT_EQ(alc1.alloc(0), nullptr); - EXPECT_NO_THROW(alc1.dealloc(nullptr, 128)); - EXPECT_NO_THROW(alc1.dealloc(nullptr, 0)); - EXPECT_NO_THROW(alc1.dealloc(&alc1, 0)); - - EXPECT_EQ(alc2.alloc(0), nullptr); - EXPECT_NO_THROW(alc2.dealloc(nullptr, 128)); - EXPECT_NO_THROW(alc2.dealloc(nullptr, 0)); - EXPECT_NO_THROW(alc2.dealloc(&alc1, 0)); - - EXPECT_EQ(alc2.alloc(1024), nullptr); - EXPECT_NO_THROW(alc2.dealloc(&alc1, sizeof(alc1))); -} \ No newline at end of file +TEST(allocator, sizeof) { + EXPECT_EQ(sizeof(pmr::allocator), sizeof(void *) * 2); +} diff --git a/test/pmr/test_pmr_small_storage.cpp b/test/pmr/test_pmr_small_storage.cpp new file mode 100644 index 0000000..e3629a3 --- /dev/null +++ b/test/pmr/test_pmr_small_storage.cpp @@ -0,0 +1,7 @@ + +#include "gtest/gtest.h" + +#include "libpmr/small_storage.h" + +TEST(small_storage, construct) { +}