Add unit tests for monotonic_buffer_resource.

This commit is contained in:
mutouyun 2023-09-17 16:59:32 +08:00
parent 665a347447
commit eee15dda02
3 changed files with 139 additions and 3 deletions

View File

@ -22,7 +22,8 @@ struct has_allocate : std::false_type {};
template <typename T> template <typename T>
struct has_allocate<T, struct has_allocate<T,
typename std::enable_if<std::is_convertible< typename std::enable_if<std::is_convertible<
decltype(std::declval<T &>().allocate(std::declval<std::size_t>())), void * decltype(std::declval<T &>().allocate(std::declval<std::size_t>(),
std::declval<std::size_t>())), void *
>::value>::type> : std::true_type {}; >::value>::type> : std::true_type {};
template <typename T, typename = void> template <typename T, typename = void>
@ -31,6 +32,7 @@ struct has_deallocate : std::false_type {};
template <typename T> template <typename T>
struct has_deallocate<T, struct has_deallocate<T,
decltype(std::declval<T &>().deallocate(std::declval<void *>(), decltype(std::declval<T &>().deallocate(std::declval<void *>(),
std::declval<std::size_t>(),
std::declval<std::size_t>())) std::declval<std::size_t>()))
> : std::true_type {}; > : std::true_type {};

View File

@ -17,7 +17,8 @@ Node *make_node(allocator const &upstream, std::size_t initial_size, std::size_t
auto sz = ::LIBIMP::round_up(sizeof(Node), alignment) + initial_size; auto sz = ::LIBIMP::round_up(sizeof(Node), alignment) + initial_size;
auto *node = static_cast<Node *>(upstream.allocate(sz)); auto *node = static_cast<Node *>(upstream.allocate(sz));
if (node == nullptr) { if (node == nullptr) {
log.error("failed: allocate memory for `monotonic_buffer_resource`."); log.error("failed: allocate memory for `monotonic_buffer_resource`'s node.",
" bytes = ", initial_size, ", alignment = ", alignment);
return nullptr; return nullptr;
} }
node->next = nullptr; node->next = nullptr;
@ -104,7 +105,15 @@ void *monotonic_buffer_resource::allocate(std::size_t bytes, std::size_t alignme
node->next = free_list_; node->next = free_list_;
free_list_ = node; free_list_ = node;
next_size_ = next_buffer_size(next_size_); next_size_ = next_buffer_size(next_size_);
p = reinterpret_cast<::LIBIMP::byte *>(free_list_) + ::LIBIMP::round_up(sizeof(node), alignment); // try again
s = node->size - sizeof(monotonic_buffer_resource::node);
p = std::align(alignment, bytes, (p = node + 1), s);
if (p == nullptr) {
log.error("failed: allocate memory for `monotonic_buffer_resource`.",
" bytes = ", bytes, ", alignment = ", alignment);
return nullptr;
}
tail_ = static_cast<::LIBIMP::byte *>(p) + s;
} }
head_ = static_cast<::LIBIMP::byte *>(p) + bytes; head_ = static_cast<::LIBIMP::byte *>(p) + bytes;
return p; return p;

View File

@ -1,4 +1,8 @@
#include <type_traits>
#include <array>
#include <cstdlib>
#include "gtest/gtest.h" #include "gtest/gtest.h"
#include "libpmr/monotonic_buffer_resource.h" #include "libpmr/monotonic_buffer_resource.h"
@ -13,3 +17,124 @@ TEST(monotonic_buffer_resource, construct) {
pmr::monotonic_buffer_resource{imp::span<imp::byte>{}, pmr::allocator{}}; pmr::monotonic_buffer_resource{imp::span<imp::byte>{}, pmr::allocator{}};
SUCCEED(); SUCCEED();
} }
TEST(monotonic_buffer_resource, no_copy) {
EXPECT_FALSE(std::is_copy_constructible<pmr::monotonic_buffer_resource>::value);
EXPECT_FALSE(std::is_copy_assignable<pmr::monotonic_buffer_resource>::value);
EXPECT_FALSE(std::is_move_constructible<pmr::monotonic_buffer_resource>::value);
EXPECT_FALSE(std::is_move_assignable<pmr::monotonic_buffer_resource>::value);
}
TEST(monotonic_buffer_resource, upstream_resource) {
struct dummy_allocator {
bool allocated = false;
void *allocate(std::size_t, std::size_t) noexcept { allocated = true; return nullptr; }
void deallocate(void *, std::size_t, std::size_t) noexcept {}
} dummy;
pmr::monotonic_buffer_resource tmp{&dummy};
ASSERT_EQ(tmp.upstream_resource().allocate(1), nullptr);
ASSERT_TRUE(dummy.allocated);
}
namespace {
struct dummy_allocator {
std::size_t allocated = 0;
void *allocate(std::size_t size, std::size_t) noexcept {
allocated += size;
return std::malloc(size);
}
void deallocate(void *p, std::size_t size, std::size_t) noexcept {
allocated -= size;
std::free(p);
}
};
} // namespace
TEST(monotonic_buffer_resource, allocate) {
dummy_allocator dummy;
{
pmr::monotonic_buffer_resource tmp{&dummy};
ASSERT_EQ(tmp.allocate(0), nullptr);
ASSERT_EQ(dummy.allocated, 0);
}
ASSERT_EQ(dummy.allocated, 0);
{
pmr::monotonic_buffer_resource tmp{&dummy};
std::size_t sz = 0;
for (std::size_t i = 1; i < 1024; ++i) {
ASSERT_NE(tmp.allocate(i), nullptr);
sz += i;
}
for (std::size_t i = 1; i < 1024; ++i) {
ASSERT_NE(tmp.allocate(1024 - i), nullptr);
sz += 1024 - i;
}
ASSERT_GE(dummy.allocated, sz);
}
ASSERT_EQ(dummy.allocated, 0);
}
TEST(monotonic_buffer_resource, allocate_by_buffer) {
dummy_allocator dummy;
std::array<imp::byte, 4096> buffer;
{
pmr::monotonic_buffer_resource tmp{buffer, &dummy};
for (std::size_t i = 1; i < 64; ++i) {
ASSERT_NE(tmp.allocate(i), nullptr);
}
ASSERT_EQ(dummy.allocated, 0);
std::size_t sz = 0;
for (std::size_t i = 1; i < 64; ++i) {
ASSERT_NE(tmp.allocate(64 - i), nullptr);
sz += 64 - i;
}
ASSERT_GT(dummy.allocated, sz);
}
ASSERT_EQ(dummy.allocated, 0);
}
#include <memory_resource>
TEST(monotonic_buffer_resource, release) {
dummy_allocator dummy;
{
pmr::monotonic_buffer_resource tmp{&dummy};
tmp.release();
ASSERT_EQ(dummy.allocated, 0);
ASSERT_NE(tmp.allocate(1024), nullptr);
ASSERT_GE(dummy.allocated, 1024);
ASSERT_LE(dummy.allocated, 1024 * 1.5);
tmp.release();
ASSERT_EQ(dummy.allocated, 0);
ASSERT_NE(tmp.allocate(1024), nullptr);
ASSERT_GE(dummy.allocated, 1024);
ASSERT_LE(dummy.allocated, 1024 * 1.5);
}
ASSERT_EQ(dummy.allocated, 0);
std::array<imp::byte, 4096> buffer;
{
pmr::monotonic_buffer_resource tmp{buffer, &dummy};
auto *p = tmp.allocate(1024);
ASSERT_EQ(p, buffer.data());
p = tmp.allocate(10240);
ASSERT_NE(p, buffer.data());
ASSERT_LE(dummy.allocated, 10240 + 1024);
tmp.release();
ASSERT_EQ(dummy.allocated, 0);
p = tmp.allocate(1024);
ASSERT_EQ(p, buffer.data());
ASSERT_EQ(dummy.allocated, 0);
}
ASSERT_EQ(dummy.allocated, 0);
// {
// std::pmr::monotonic_buffer_resource res{buffer.data(), buffer.size()};
// std::pmr::polymorphic_allocator<imp::byte> tmp{&res};
// auto *p = tmp.allocate(1024);
// ASSERT_EQ(p, buffer.data());
// p = tmp.allocate(10240);
// ASSERT_NE(p, buffer.data());
// res.release();
// p = tmp.allocate(1024);
// ASSERT_EQ(p, buffer.data());
// }
}