mirror of
https://github.com/mutouyun/cpp-ipc.git
synced 2025-12-06 16:56:45 +08:00
add: [pmr] allocator & ut
This commit is contained in:
parent
cc17980b97
commit
1fd5b3825e
@ -7,41 +7,16 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
|
#include <array>
|
||||||
|
|
||||||
#include "libimp/export.h"
|
#include "libimp/export.h"
|
||||||
|
#include "libimp/construct.h"
|
||||||
|
#include "libimp/byte.h"
|
||||||
|
|
||||||
#include "libpmr/def.h"
|
#include "libpmr/def.h"
|
||||||
#include "libpmr/memory_resource.h"
|
#include "libpmr/memory_resource.h"
|
||||||
|
|
||||||
LIBPMR_NAMESPACE_BEG_
|
LIBPMR_NAMESPACE_BEG_
|
||||||
namespace detail {
|
|
||||||
|
|
||||||
/// @brief Helper trait for allocator.
|
|
||||||
|
|
||||||
template <typename T, typename = void>
|
|
||||||
struct has_allocate : std::false_type {};
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
struct has_allocate<T,
|
|
||||||
typename std::enable_if<std::is_convertible<
|
|
||||||
decltype(std::declval<T &>().allocate(std::declval<std::size_t>())), void *
|
|
||||||
>::value>::type> : std::true_type {};
|
|
||||||
|
|
||||||
template <typename T, typename = void>
|
|
||||||
struct has_deallocate : std::false_type {};
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
struct has_deallocate<T,
|
|
||||||
decltype(std::declval<T &>().deallocate(std::declval<void *>(),
|
|
||||||
std::declval<std::size_t>()))
|
|
||||||
> : std::true_type {};
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
using is_memory_resource =
|
|
||||||
typename std::enable_if<has_allocate <T>::value &&
|
|
||||||
has_deallocate<T>::value>::type;
|
|
||||||
|
|
||||||
} // namespace detail
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief An allocator which exhibits different allocation behavior
|
* @brief An allocator which exhibits different allocation behavior
|
||||||
@ -57,6 +32,78 @@ using is_memory_resource =
|
|||||||
*/
|
*/
|
||||||
class LIBIMP_EXPORT allocator {
|
class LIBIMP_EXPORT allocator {
|
||||||
|
|
||||||
|
class holder_base {
|
||||||
|
public:
|
||||||
|
virtual ~holder_base() noexcept = default;
|
||||||
|
virtual void *alloc(std::size_t) = 0;
|
||||||
|
virtual void free (void *, std::size_t) = 0;
|
||||||
|
virtual bool valid() const noexcept = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
class holder_null : public holder_base {
|
||||||
|
public:
|
||||||
|
void *alloc(std::size_t) override { return nullptr; }
|
||||||
|
void free (void *, std::size_t) override {}
|
||||||
|
bool valid() const noexcept override { return false; }
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename MemRes>
|
||||||
|
class holder_memory_resource : public holder_base {
|
||||||
|
MemRes *p_mem_res_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
holder_memory_resource(MemRes *p_mr) noexcept
|
||||||
|
: p_mem_res_(p_mr) {}
|
||||||
|
|
||||||
|
void *alloc(std::size_t s) override {
|
||||||
|
return p_mem_res_->allocate(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
void free(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_memory_resource<void> : public holder_null {
|
||||||
|
void *p_dummy_;
|
||||||
|
};
|
||||||
|
|
||||||
|
using void_holder_type = holder_memory_resource<void>;
|
||||||
|
alignas(void_holder_type) std::array<::LIBIMP_::byte, sizeof(void_holder_type)> holder_;
|
||||||
|
|
||||||
|
holder_base & get_holder() noexcept;
|
||||||
|
holder_base const &get_holder() const noexcept;
|
||||||
|
|
||||||
|
public:
|
||||||
|
allocator() noexcept;
|
||||||
|
~allocator() noexcept;
|
||||||
|
|
||||||
|
allocator(allocator const &other) noexcept = default;
|
||||||
|
allocator &operator=(allocator const &other) & noexcept = default;
|
||||||
|
|
||||||
|
allocator(allocator &&other) noexcept;
|
||||||
|
allocator &operator=(allocator &&other) & noexcept;
|
||||||
|
|
||||||
|
/// @brief Constructs a allocator from a memory resource pointer
|
||||||
|
/// @remark The lifetime of the pointer must be longer than that of allocator.
|
||||||
|
template <typename T, typename = is_memory_resource<T>>
|
||||||
|
allocator(T *p_mr) : allocator() {
|
||||||
|
if (p_mr == nullptr) return;
|
||||||
|
::LIBIMP_::construct<holder_memory_resource<T>>(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 free (void *p, std::size_t s);
|
||||||
};
|
};
|
||||||
|
|
||||||
LIBPMR_NAMESPACE_END_
|
LIBPMR_NAMESPACE_END_
|
||||||
|
|||||||
@ -6,6 +6,7 @@
|
|||||||
*/
|
*/
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <type_traits>
|
||||||
#include <cstddef> // std::size_t, std::max_align_t
|
#include <cstddef> // std::size_t, std::max_align_t
|
||||||
|
|
||||||
#include "libimp/export.h"
|
#include "libimp/export.h"
|
||||||
@ -13,14 +14,42 @@
|
|||||||
|
|
||||||
LIBPMR_NAMESPACE_BEG_
|
LIBPMR_NAMESPACE_BEG_
|
||||||
|
|
||||||
|
/// @brief Helper trait for memory resource.
|
||||||
|
|
||||||
|
template <typename T, typename = void>
|
||||||
|
struct has_allocate : std::false_type {};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct has_allocate<T,
|
||||||
|
typename std::enable_if<std::is_convertible<
|
||||||
|
decltype(std::declval<T &>().allocate(std::declval<std::size_t>())), void *
|
||||||
|
>::value>::type> : std::true_type {};
|
||||||
|
|
||||||
|
template <typename T, typename = void>
|
||||||
|
struct has_deallocate : std::false_type {};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct has_deallocate<T,
|
||||||
|
decltype(std::declval<T &>().deallocate(std::declval<void *>(),
|
||||||
|
std::declval<std::size_t>()))
|
||||||
|
> : std::true_type {};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
using is_memory_resource =
|
||||||
|
typename std::enable_if<has_allocate <T>::value &&
|
||||||
|
has_deallocate<T>::value>::type;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief A memory resource that uses the
|
* @brief A memory resource that uses the
|
||||||
* global operator new and operator delete to allocate memory.
|
* standard memory allocation and deallocation interface to allocate memory.
|
||||||
*
|
*
|
||||||
* @see https://en.cppreference.com/w/cpp/memory/new_delete_resource
|
* @see https://en.cppreference.com/w/cpp/memory/new_delete_resource
|
||||||
*/
|
*/
|
||||||
class LIBIMP_EXPORT new_delete_resource {
|
class LIBIMP_EXPORT new_delete_resource {
|
||||||
public:
|
public:
|
||||||
|
/// @brief Returns a pointer to a new_delete_resource.
|
||||||
|
static new_delete_resource *get() noexcept;
|
||||||
|
|
||||||
/// @brief Allocates storage with a size of at least bytes bytes, aligned to the specified alignment.
|
/// @brief Allocates storage with a size of at least bytes bytes, aligned to the specified alignment.
|
||||||
/// @remark Returns nullptr if storage of the requested size and alignment cannot be obtained.
|
/// @remark Returns nullptr if storage of the requested size and alignment cannot be obtained.
|
||||||
/// @see https://en.cppreference.com/w/cpp/memory/memory_resource/do_allocate
|
/// @see https://en.cppreference.com/w/cpp/memory/memory_resource/do_allocate
|
||||||
|
|||||||
58
src/libpmr/allocator.cpp
Normal file
58
src/libpmr/allocator.cpp
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
|
||||||
|
#include <algorithm> // std::swap
|
||||||
|
|
||||||
|
#include "libpmr/allocator.h"
|
||||||
|
|
||||||
|
LIBPMR_NAMESPACE_BEG_
|
||||||
|
|
||||||
|
allocator::holder_base &allocator::get_holder() noexcept {
|
||||||
|
return *::LIBIMP_::byte_cast<holder_base>(holder_.data());
|
||||||
|
}
|
||||||
|
|
||||||
|
allocator::holder_base const &allocator::get_holder() const noexcept {
|
||||||
|
return *::LIBIMP_::byte_cast<holder_base const>(holder_.data());
|
||||||
|
}
|
||||||
|
|
||||||
|
allocator::allocator() noexcept {
|
||||||
|
::LIBIMP_::construct<holder_null>(holder_.data());
|
||||||
|
}
|
||||||
|
|
||||||
|
allocator::~allocator() noexcept {
|
||||||
|
::LIBIMP_::destroy(&get_holder());
|
||||||
|
}
|
||||||
|
|
||||||
|
allocator::allocator(allocator &&other) noexcept
|
||||||
|
: allocator(other) /*copy*/ {
|
||||||
|
::LIBIMP_::construct<holder_null>(other.holder_.data());
|
||||||
|
}
|
||||||
|
|
||||||
|
allocator &allocator::operator=(allocator &&other) & noexcept {
|
||||||
|
if (this == &other) return *this;
|
||||||
|
this->holder_ = other.holder_;
|
||||||
|
::LIBIMP_::construct<holder_null>(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();
|
||||||
|
}
|
||||||
|
|
||||||
|
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::free(void *p, std::size_t s) {
|
||||||
|
if (!valid()) return;
|
||||||
|
get_holder().free(p, s);
|
||||||
|
}
|
||||||
|
|
||||||
|
LIBPMR_NAMESPACE_END_
|
||||||
@ -27,6 +27,16 @@ bool verify_args(::LIBIMP_::log::gripper &log, std::size_t bytes, std::size_t al
|
|||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns a pointer to a new_delete_resource.
|
||||||
|
*
|
||||||
|
* @return new_delete_resource*
|
||||||
|
*/
|
||||||
|
new_delete_resource *new_delete_resource::get() noexcept {
|
||||||
|
static new_delete_resource mem_res;
|
||||||
|
return &mem_res;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Allocates storage with a size of at least bytes bytes, aligned to the specified alignment.
|
* @brief Allocates storage with a size of at least bytes bytes, aligned to the specified alignment.
|
||||||
* @remark Alignment shall be a power of two.
|
* @remark Alignment shall be a power of two.
|
||||||
|
|||||||
@ -1,32 +1,78 @@
|
|||||||
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#if defined(LIBIMP_CPP_17) && defined(__cpp_lib_memory_resource)
|
#include <utility>
|
||||||
#include <memory_resource>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "gtest/gtest.h"
|
#include "gtest/gtest.h"
|
||||||
|
|
||||||
#include "libpmr/allocator.h"
|
#include "libpmr/allocator.h"
|
||||||
|
|
||||||
TEST(allocator, detail) {
|
TEST(allocator, construct) {
|
||||||
EXPECT_FALSE(pmr::detail::has_allocate<void>::value);
|
pmr::allocator alc;
|
||||||
EXPECT_FALSE(pmr::detail::has_allocate<int>::value);
|
EXPECT_FALSE(alc.valid());
|
||||||
EXPECT_FALSE(pmr::detail::has_allocate<std::vector<int>>::value);
|
EXPECT_FALSE(alc);
|
||||||
EXPECT_TRUE (pmr::detail::has_allocate<std::allocator<int>>::value);
|
|
||||||
#if defined(LIBIMP_CPP_17) && defined(__cpp_lib_memory_resource)
|
|
||||||
EXPECT_TRUE (pmr::detail::has_allocate<std::pmr::memory_resource>::value);
|
|
||||||
EXPECT_TRUE (pmr::detail::has_allocate<std::pmr::polymorphic_allocator<int>>::value);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
EXPECT_FALSE(pmr::detail::has_deallocate<void>::value);
|
|
||||||
EXPECT_FALSE(pmr::detail::has_deallocate<int>::value);
|
|
||||||
EXPECT_FALSE(pmr::detail::has_deallocate<std::vector<int>>::value);
|
|
||||||
EXPECT_FALSE(pmr::detail::has_deallocate<std::allocator<int>>::value);
|
|
||||||
#if defined(LIBIMP_CPP_17) && defined(__cpp_lib_memory_resource)
|
|
||||||
EXPECT_TRUE (pmr::detail::has_deallocate<std::pmr::memory_resource>::value);
|
|
||||||
EXPECT_FALSE(pmr::detail::has_deallocate<std::pmr::polymorphic_allocator<int>>::value);
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(allocator, construct) {
|
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);
|
||||||
|
EXPECT_NE(p, nullptr);
|
||||||
|
EXPECT_NO_THROW(alc.free(p, 128));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(allocator, construct_copy_move) {
|
||||||
|
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);
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
alc1.swap(alc2);
|
||||||
|
EXPECT_FALSE(alc1.valid());
|
||||||
|
EXPECT_FALSE(alc1);
|
||||||
|
EXPECT_TRUE (alc2.valid());
|
||||||
|
EXPECT_TRUE (alc2);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(allocator, invalid_alloc_free) {
|
||||||
|
pmr::new_delete_resource mem_res;
|
||||||
|
|
||||||
|
pmr::allocator alc1 {&mem_res}, alc2;
|
||||||
|
EXPECT_EQ(alc1.alloc(0), nullptr);
|
||||||
|
EXPECT_NO_THROW(alc1.free(nullptr, 128));
|
||||||
|
EXPECT_NO_THROW(alc1.free(nullptr, 0));
|
||||||
|
EXPECT_NO_THROW(alc1.free(&alc1, 0));
|
||||||
|
|
||||||
|
EXPECT_EQ(alc2.alloc(0), nullptr);
|
||||||
|
EXPECT_NO_THROW(alc2.free(nullptr, 128));
|
||||||
|
EXPECT_NO_THROW(alc2.free(nullptr, 0));
|
||||||
|
EXPECT_NO_THROW(alc2.free(&alc1, 0));
|
||||||
|
|
||||||
|
EXPECT_EQ(alc2.alloc(1024), nullptr);
|
||||||
|
EXPECT_NO_THROW(alc2.free(&alc1, sizeof(alc1)));
|
||||||
}
|
}
|
||||||
@ -1,5 +1,8 @@
|
|||||||
|
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
#if defined(LIBIMP_CPP_17) && defined(__cpp_lib_memory_resource)
|
||||||
|
#include <memory_resource>
|
||||||
|
#endif
|
||||||
|
|
||||||
#include "gtest/gtest.h"
|
#include "gtest/gtest.h"
|
||||||
|
|
||||||
@ -21,20 +24,40 @@ void *test_mr(T &&mr, std::size_t bytes, std::size_t alignment) {
|
|||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
|
TEST(memory_resource, traits) {
|
||||||
|
EXPECT_FALSE(pmr::has_allocate<void>::value);
|
||||||
|
EXPECT_FALSE(pmr::has_allocate<int>::value);
|
||||||
|
EXPECT_FALSE(pmr::has_allocate<std::vector<int>>::value);
|
||||||
|
EXPECT_TRUE (pmr::has_allocate<std::allocator<int>>::value);
|
||||||
|
#if defined(LIBIMP_CPP_17) && defined(__cpp_lib_memory_resource)
|
||||||
|
EXPECT_TRUE (pmr::has_allocate<std::pmr::memory_resource>::value);
|
||||||
|
EXPECT_TRUE (pmr::has_allocate<std::pmr::polymorphic_allocator<int>>::value);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
EXPECT_FALSE(pmr::has_deallocate<void>::value);
|
||||||
|
EXPECT_FALSE(pmr::has_deallocate<int>::value);
|
||||||
|
EXPECT_FALSE(pmr::has_deallocate<std::vector<int>>::value);
|
||||||
|
EXPECT_FALSE(pmr::has_deallocate<std::allocator<int>>::value);
|
||||||
|
#if defined(LIBIMP_CPP_17) && defined(__cpp_lib_memory_resource)
|
||||||
|
EXPECT_TRUE (pmr::has_deallocate<std::pmr::memory_resource>::value);
|
||||||
|
EXPECT_FALSE(pmr::has_deallocate<std::pmr::polymorphic_allocator<int>>::value);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
TEST(memory_resource, new_delete_resource) {
|
TEST(memory_resource, new_delete_resource) {
|
||||||
pmr::new_delete_resource m_res;
|
pmr::new_delete_resource mem_res;
|
||||||
|
|
||||||
EXPECT_EQ(test_mr(m_res, 0, 0), nullptr);
|
EXPECT_EQ(test_mr(mem_res, 0, 0), nullptr);
|
||||||
EXPECT_EQ(test_mr(m_res, 0, 1), nullptr);
|
EXPECT_EQ(test_mr(mem_res, 0, 1), nullptr);
|
||||||
EXPECT_EQ(test_mr(m_res, 0, 2), nullptr);
|
EXPECT_EQ(test_mr(mem_res, 0, 2), nullptr);
|
||||||
EXPECT_EQ(test_mr(m_res, 0, 3), nullptr);
|
EXPECT_EQ(test_mr(mem_res, 0, 3), nullptr);
|
||||||
EXPECT_EQ(test_mr(m_res, 0, 8), nullptr);
|
EXPECT_EQ(test_mr(mem_res, 0, 8), nullptr);
|
||||||
EXPECT_EQ(test_mr(m_res, 0, 64), nullptr);
|
EXPECT_EQ(test_mr(mem_res, 0, 64), nullptr);
|
||||||
|
|
||||||
EXPECT_EQ(test_mr(m_res, 1, 0), nullptr);
|
EXPECT_EQ(test_mr(mem_res, 1, 0), nullptr);
|
||||||
EXPECT_NE(test_mr(m_res, 1, 1), nullptr);
|
EXPECT_NE(test_mr(mem_res, 1, 1), nullptr);
|
||||||
EXPECT_NE(test_mr(m_res, 1, 2), nullptr);
|
EXPECT_NE(test_mr(mem_res, 1, 2), nullptr);
|
||||||
EXPECT_EQ(test_mr(m_res, 1, 3), nullptr);
|
EXPECT_EQ(test_mr(mem_res, 1, 3), nullptr);
|
||||||
EXPECT_NE(test_mr(m_res, 1, 8), nullptr);
|
EXPECT_NE(test_mr(mem_res, 1, 8), nullptr);
|
||||||
EXPECT_NE(test_mr(m_res, 1, 64), nullptr);
|
EXPECT_NE(test_mr(mem_res, 1, 64), nullptr);
|
||||||
}
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user