From 2d75c876726a0377df5c3d5ede8a02ba89950ec1 Mon Sep 17 00:00:00 2001 From: mutouyun Date: Sat, 9 Sep 2023 13:25:47 +0800 Subject: [PATCH] Fixed ut error on win. --- include/libimp/aligned.h | 13 +++ include/libpmr/small_storage.h | 153 ++++++++++++++++++++++++++------- test/imp/test_imp_error.cpp | 4 +- test/imp/test_imp_result.cpp | 12 +-- 4 files changed, 145 insertions(+), 37 deletions(-) diff --git a/include/libimp/aligned.h b/include/libimp/aligned.h index 4514b4c..6dfb4ed 100644 --- a/include/libimp/aligned.h +++ b/include/libimp/aligned.h @@ -59,4 +59,17 @@ public: } }; +/** + * \brief Rounds up the given value to the given alignment. + * \tparam T The type of the value. + * \param value The value to be rounded up. + * \param alignment The alignment to be rounded up to. + * \return The rounded up value. + * \see https://stackoverflow.com/questions/3407012/c-rounding-up-to-the-nearest-multiple-of-a-number +*/ +template +constexpr T round_up(T value, T alignment) noexcept { + return (value + alignment - 1) & ~(alignment - 1); +} + LIBIMP_NAMESPACE_END_ diff --git a/include/libpmr/small_storage.h b/include/libpmr/small_storage.h index 9406a3c..5ffc3be 100644 --- a/include/libpmr/small_storage.h +++ b/include/libpmr/small_storage.h @@ -17,6 +17,7 @@ #include "libimp/uninitialized.h" #include "libimp/byte.h" #include "libimp/generic.h" +#include "libimp/aligned.h" #include "libpmr/def.h" #include "libpmr/allocator.h" @@ -80,14 +81,14 @@ class holder; template class holder : public holder_base { - alignas(alignof(Value)) std::array<::LIBIMP::byte, sizeof(Value)> value_storage_; + ::LIBIMP::aligned value_storage_; public: holder() = default; // uninitialized template holder(allocator const &, A &&...args) { - ::LIBIMP::construct(value_storage_.data(), std::forward(args)...); + ::LIBIMP::construct(value_storage_.ptr(), std::forward(args)...); } bool valid() const noexcept override { @@ -111,11 +112,11 @@ public: } void *get() noexcept override { - return value_storage_.data(); + return value_storage_.ptr(); } void const *get() const noexcept override { - return value_storage_.data(); + return value_storage_.ptr(); } void move_to(allocator const &alloc, void *p) noexcept override { @@ -226,16 +227,19 @@ void *holder_construct(allocator const &alloc, std::size_t n, Construct &&c) { class holder_info_ptr { allocator const &alloc_; holder_info **pptr_; + std::size_t size_; public: - holder_info_ptr(allocator const &alloc, holder_info *(&ptr)) noexcept - : alloc_(alloc), pptr_(&ptr) { - *pptr_ = static_cast(alloc_.allocate(sizeof(holder_info), alignof(holder_info))); + holder_info_ptr(allocator const &alloc, holder_info *(&ptr), std::size_t sz) noexcept + : alloc_(alloc) + , pptr_ (&ptr) + , size_ (::LIBIMP::round_up(sizeof(holder_info), alignof(std::max_align_t)) + sz) { + *pptr_ = static_cast(alloc_.allocate(size_)); } ~holder_info_ptr() noexcept { if (pptr_ == nullptr) return; - alloc_.deallocate(*pptr_, sizeof(holder_info), alignof(holder_info)); + alloc_.deallocate(*pptr_, size_); *pptr_ = nullptr; } @@ -356,23 +360,19 @@ template <> class holder : public holder_base { detail::holder_info *info_ptr_; - void *value_ptr_; public: holder() noexcept - : info_ptr_ (nullptr) - , value_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). template = true> holder(allocator const &alloc, ::LIBIMP::types, std::size_t n) : holder() { - detail::holder_info_ptr info_p{alloc, info_ptr_}; + detail::holder_info_ptr info_p{alloc, info_ptr_, sizeof(Value) * n}; if (!info_p) return; - value_ptr_ = detail::holder_construct(alloc, n, - ::LIBIMP::uninitialized_default_construct_n); - if (value_ptr_ == nullptr) return; + ::LIBIMP::uninitialized_default_construct_n(static_cast(get()), n); info_p->type = &typeid(Value); info_p->sizeof_type = sizeof(Value); info_p->count = n; @@ -380,12 +380,9 @@ public: auto const &src = *static_cast(s); auto & dst = *::LIBIMP::construct(d); if (!src.valid()) return; - detail::holder_info_ptr info_p{alloc, dst.info_ptr_}; + detail::holder_info_ptr info_p{alloc, dst.info_ptr_, sizeof(Value) * src.count()}; if (!info_p) 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; + ::LIBIMP::uninitialized_default_construct_n(static_cast(dst.get()), src.count()); *info_p = *src.info_ptr_; info_p.release(); }; @@ -396,7 +393,7 @@ public: } bool valid() const noexcept override { - return (value_ptr_ != nullptr) && (info_ptr_ != nullptr); + return info_ptr_ != nullptr; } std::type_info const &type() const noexcept override { @@ -411,7 +408,8 @@ public: std::size_t sizeof_heap() const noexcept override { if (!valid()) return 0; - return info_ptr_->sizeof_type * info_ptr_->count + sizeof(detail::holder_info); + return ::LIBIMP::round_up(sizeof(detail::holder_info), alignof(std::max_align_t)) + + (info_ptr_->sizeof_type * info_ptr_->count); } std::size_t count() const noexcept override { @@ -420,18 +418,19 @@ public: } void *get() noexcept override { - return value_ptr_; + return reinterpret_cast( + ::LIBIMP::round_up(reinterpret_cast(info_ptr_ + 1), alignof(std::max_align_t))); } void const *get() const noexcept override { - return value_ptr_; + return reinterpret_cast( + ::LIBIMP::round_up(reinterpret_cast(info_ptr_ + 1), alignof(std::max_align_t))); } 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_ptr_ , des->info_ptr_); + std::swap(info_ptr_, des->info_ptr_); } void copy_to(allocator const &alloc, void *p) const noexcept(false) override { @@ -442,9 +441,105 @@ public: void destroy(allocator const &alloc) noexcept override { if (!valid()) return; - info_ptr_->dest (value_ptr_, count()); - alloc.deallocate(value_ptr_, sizeof_heap()); - alloc.deallocate(info_ptr_ , sizeof(detail::holder_info), alignof(detail::holder_info)); + info_ptr_->dest(get(), count()); + alloc.deallocate(info_ptr_, sizeof_heap()); + } +}; + +/** + * \class small_storage + * \brief Unified SSO (Small Size Optimization). + * \tparam N The size of the storage. + */ +template +class small_storage { + + static_assert(N > holder, "N must be greater than sizeof(holder)"); + + alignas(std::max_align_t) std::array<::LIBIMP::byte, N> storage_; + +public: + small_storage(small_storage const &) = delete; + small_storage &operator=(small_storage const &) = delete; + + small_storage() noexcept { + ::LIBIMP::construct(get_holder()); + } + + holder_base *get_holder() noexcept { + return static_cast(storage_.data()); + } + + holder_base const *get_holder() const noexcept { + return static_cast(storage_.data()); + } + + bool valid() const noexcept { + return get_holder()->valid(); + } + + std::type_info const &type() const noexcept { + return get_holder()->type(); + } + + std::size_t sizeof_type() const noexcept { + return get_holder()->sizeof_type(); + } + + std::size_t sizeof_heap() const noexcept { + return get_holder()->sizeof_heap(); + } + + std::size_t count() const noexcept { + return get_holder()->count(); + } + + template + Value *As() noexcept { + return static_cast(get_holder()->get()); + } + + template + Value const *As() const noexcept { + return static_cast(get_holder()->get()); + } + + template + Value &AsRef() noexcept { + return *As(); + } + + template + Value const &AsRef() const noexcept { + return *As(); + } + + void move_to(allocator const &alloc, small_storage &des) noexcept { + get_holder()->move_to(alloc, des.get_holder()); + } + + void copy_to(allocator const &alloc, small_storage &des) const noexcept(false) { + get_holder()->copy_to(alloc, des.get_holder()); + } + + void reset(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); } }; diff --git a/test/imp/test_imp_error.cpp b/test/imp/test_imp_error.cpp index 8dfb4ef..8f0e31c 100644 --- a/test/imp/test_imp_error.cpp +++ b/test/imp/test_imp_error.cpp @@ -5,10 +5,10 @@ #include "libimp/error.h" #include "libimp/result.h" #include "libimp/fmt.h" +#include "libimp/codecvt.h" TEST(error, error_code) { std::error_code ecode; EXPECT_FALSE(ecode); - std::cout << ecode.message() << "\n"; - EXPECT_EQ(ecode.message(), "Success"); + std::cout << imp::fmt(ecode, '\n'); } diff --git a/test/imp/test_imp_result.cpp b/test/imp/test_imp_result.cpp index efe23b6..3d8ba07 100644 --- a/test/imp/test_imp_result.cpp +++ b/test/imp/test_imp_result.cpp @@ -102,11 +102,11 @@ TEST(result, fmt) { imp::result r3 {&aaa}; EXPECT_EQ(imp::fmt(r3), imp::fmt("succ, value = ", (void *)&aaa)); imp::result r4 {nullptr}; - EXPECT_EQ(imp::fmt(r4), imp::fmt("fail, value = ", nullptr, ", error = [-1: Unknown error -1]")); - r4 = {nullptr, std::error_code{1234, std::generic_category()}}; - EXPECT_EQ(imp::fmt(r4), imp::fmt("fail, value = ", nullptr, ", error = [1234: Unknown error 1234]")); + EXPECT_EQ(imp::fmt(r4), imp::fmt("fail, value = ", nullptr, ", error = ", std::error_code(-1, std::generic_category()))); + r4 = std::error_code(1234, std::generic_category()); + EXPECT_EQ(imp::fmt(r4), imp::fmt("fail, value = ", nullptr, ", error = ", std::error_code(1234, std::generic_category()))); imp::result r5; - EXPECT_EQ(imp::fmt(r5), "fail, value = null, error = [-1: Unknown error -1]"); + EXPECT_EQ(imp::fmt(r5), imp::fmt("fail, value = null, error = ", std::error_code(-1, std::generic_category()))); } { imp::result r1 {-123}; @@ -114,9 +114,9 @@ TEST(result, fmt) { } { imp::result r1; - EXPECT_EQ(imp::fmt(r1), "fail, error = [-1: Unknown error -1]"); + EXPECT_EQ(imp::fmt(r1), imp::fmt("fail, error = ", std::error_code(-1, std::generic_category()))); r1 = std::error_code{}; EXPECT_TRUE(r1); - EXPECT_EQ(imp::fmt(r1), "succ, error = [0: Success]"); + EXPECT_EQ(imp::fmt(r1), imp::fmt("succ, error = ", std::error_code())); } }