From 93e43d67ec742703bfc8ef41f4b40eee16608cdb Mon Sep 17 00:00:00 2001 From: pknowles Date: Sun, 2 Jun 2024 15:50:52 -0700 Subject: [PATCH] test realloc behaviour and allocator equality --- include/decodeless/allocator.hpp | 37 ++++++++++++---- test/src/allocator.cpp | 75 ++++++++++++++++++++++++++++++++ 2 files changed, 103 insertions(+), 9 deletions(-) diff --git a/include/decodeless/allocator.hpp b/include/decodeless/allocator.hpp index a534096..f17e45e 100644 --- a/include/decodeless/allocator.hpp +++ b/include/decodeless/allocator.hpp @@ -137,13 +137,16 @@ class linear_memory_resource { } // Returns a pointer to the arena/parent allocation. - void* data() const { return reinterpret_cast(m_begin); } + [[nodiscard]] void* data() const { return reinterpret_cast(m_begin); } // Returns the total number of bytes allocated within the arena - size_t size() const { return m_next - reinterpret_cast(m_begin); } + [[nodiscard]] size_t size() const { return m_next - reinterpret_cast(m_begin); } // Returns the size of the arena/parent allocation - size_t capacity() const { return m_end - reinterpret_cast(m_begin); } + [[nodiscard]] size_t capacity() const { return m_end - reinterpret_cast(m_begin); } + + // Provide public access to parent allocator. Primarily used for testing. + [[nodiscard]] ParentAllocator& parent() { return m_parent; } private: ParentAllocator m_parent; @@ -176,15 +179,30 @@ class memory_resource_ref { return m_resource->deallocate(static_cast(p), n); } - bool operator==(const memory_resource_ref& other) const { + [[nodiscard]] constexpr T* reallocate(T* ptr, std::size_t bytes) + requires realloc_memory_resource + { + return static_cast(m_resource->reallocate(ptr, bytes)); + } + + [[nodiscard]] constexpr size_t max_size() const + requires has_max_size + { + return m_resource->max_size(); + } + + template + [[nodiscard]] bool operator==(const memory_resource_ref& other) const { return m_resource == other.m_resource; } - bool operator!=(const memory_resource_ref& other) const { + template + [[nodiscard]] bool operator!=(const memory_resource_ref& other) const { return m_resource != other.m_resource; } - resource_type& resource() const { return *m_resource; } + // Public access required for rebind + [[nodiscard]] resource_type& resource() const { return *m_resource; } // Needed by msvc template @@ -192,14 +210,15 @@ class memory_resource_ref { using other = memory_resource_ref; }; -protected: +private: resource_type* m_resource; }; // STL compatible allocator with an implicit linear_memory_resource memory // resource. The need for this emphasizes why std::pmr is a thing - the // MemoryResource would ideally not affect the type. -template > -using linear_allocator = memory_resource_ref; +template > +using linear_allocator = memory_resource_ref>; } // namespace decodeless diff --git a/test/src/allocator.cpp b/test/src/allocator.cpp index e68cd4f..b78081a 100644 --- a/test/src/allocator.cpp +++ b/test/src/allocator.cpp @@ -29,6 +29,43 @@ struct NullAllocator { bool allocated = false; }; +struct ReallocNullAllocator { + using value_type = std::byte; + value_type* allocate(std::size_t n) { + EXPECT_EQ(size, 0); + size = n; + return nullptr; + } + value_type* reallocate(value_type* p, std::size_t n) noexcept { + EXPECT_EQ(p, nullptr); + EXPECT_GT(size, 0); + size = n; + return nullptr; + } + void deallocate(value_type* p, std::size_t n) noexcept { + EXPECT_GT(size, 0); + (void)p; + (void)n; + } + size_t size = 0; +}; + +static_assert(realloc_allocator); + +struct ReallocNullMemoryResource { + void* allocate(std::size_t, std::size_t) { return nullptr; } + void* reallocate(void*, std::size_t, std::size_t) noexcept { return nullptr; } + void deallocate(void*, std::size_t) noexcept {} +}; + +static_assert(realloc_memory_resource); +static_assert(realloc_allocator>, + "linear_allocator should enable reallocate when possible"); +static_assert(!realloc_allocator>>, + "std::allocator does not reallocate"); +static_assert(!realloc_memory_resource>, + "linear memory does not reallocate, only its parent"); + TEST(Allocate, Object) { linear_memory_resource memory(23); @@ -96,6 +133,44 @@ TEST(Allocate, Initialize) { EXPECT_EQ(span3[2], 5); } +TEST(Allocate, Realloc) { + linear_memory_resource alloc(4); + EXPECT_EQ(alloc.parent().size, 4); + (void)alloc.allocate(sizeof(int), alignof(int)); + EXPECT_EQ(alloc.parent().size, 1 * sizeof(int)); + (void)alloc.allocate(sizeof(int), alignof(int)); + EXPECT_EQ(alloc.parent().size, 2 * sizeof(int)); + + // Allocate exact size for allocations exceeding double capacity + (void)alloc.allocate(sizeof(int) * 1000, alignof(int)); + EXPECT_EQ(alloc.parent().size, 1002 * sizeof(int)); + + // Doubling of capacity for allocations under double existing capacity + (void)alloc.allocate(sizeof(int), alignof(int)); + EXPECT_EQ(alloc.parent().size, 2004 * sizeof(int)); + + // Truncate should truncate the parent allocator + alloc.truncate(); + EXPECT_EQ(alloc.parent().size, 1003 * sizeof(int)); +} + +TEST(Allocate, Equality) { + linear_memory_resource r0(4); + linear_memory_resource r1(4); + linear_memory_resource> r2(4); + linear_allocator a0(r0); + linear_allocator a1(r1); + linear_allocator> a2(r2); + linear_allocator c0(a0); + linear_allocator c1(a1); + linear_allocator> c2(a2); + EXPECT_EQ(a0, c0); + EXPECT_EQ(a1, c1); + EXPECT_EQ(a2, c2); + EXPECT_NE(a0, c1); + EXPECT_NE(a1, c0); +} + // Relaxed test case for MSVC where the debug vector allocates extra crap TEST(Allocate, VectorRelaxed) { linear_memory_resource alloc(100);