aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authoraarongreen <aarongreen@google.com>2023-11-08 19:21:36 +0000
committerCQ Bot Account <pigweed-scoped@luci-project-accounts.iam.gserviceaccount.com>2023-11-08 19:21:36 +0000
commit74ed509bc15e65bc898284b697cacde6684c8288 (patch)
treed5d96f74042fc0385245f98ea2333281bb75d8d7
parent87636efd044bee24f360c979153abc7df28a7690 (diff)
downloadpigweed-74ed509bc15e65bc898284b697cacde6684c8288.tar.gz
pw_allocator: Refactor test support and example allocator
This CL does the following: * It move the SimpleAllocator previously described in Block's class-level comments to an actual source file, and adds a test for it. The class is now included directly into the module docs. * It refactors the FakeAllocator to use SimpleAllocator and renames it to AllocatorForTest. It also moves this from pw_allocator_private/ to public/pw_allocator/, as these utilities may be useful for testing custom allocators provided by downstream projects. * It updates the callers of FakeAllocator to use AllocatorForTest. Bug: b/306686936 Change-Id: I41953e3b64fa831bd23418496ffa683917944b3f Reviewed-on: https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/177653 Commit-Queue: Aaron Green <aarongreen@google.com> Reviewed-by: Taylor Cramer <cramertj@google.com> Reviewed-by: Keir Mierle <keir@google.com>
-rw-r--r--pw_allocator/BUILD.bazel56
-rw-r--r--pw_allocator/BUILD.gn33
-rw-r--r--pw_allocator/CMakeLists.txt59
-rw-r--r--pw_allocator/allocator_metric_proxy_test.cc20
-rw-r--r--pw_allocator/allocator_test.cc15
-rw-r--r--pw_allocator/allocator_testing.cc110
-rw-r--r--pw_allocator/docs.rst13
-rw-r--r--pw_allocator/fallback_allocator_test.cc148
-rw-r--r--pw_allocator/libc_allocator_test.cc3
-rw-r--r--pw_allocator/public/pw_allocator/allocator_testing.h134
-rw-r--r--pw_allocator/public/pw_allocator/block.h49
-rw-r--r--pw_allocator/public/pw_allocator/simple_allocator.h87
-rw-r--r--pw_allocator/pw_allocator_private/allocator_testing.h78
-rw-r--r--pw_allocator/simple_allocator_test.cc66
-rw-r--r--pw_allocator/split_free_list_allocator_test.cc158
-rw-r--r--pw_allocator/unique_ptr_test.cc40
16 files changed, 645 insertions, 424 deletions
diff --git a/pw_allocator/BUILD.bazel b/pw_allocator/BUILD.bazel
index 13c82dbbc..c7c7d9ae3 100644
--- a/pw_allocator/BUILD.bazel
+++ b/pw_allocator/BUILD.bazel
@@ -121,49 +121,62 @@ pw_cc_library(
)
pw_cc_library(
- name = "split_free_list_allocator",
+ name = "libc_allocator",
srcs = [
- "split_free_list_allocator.cc",
+ "libc_allocator.cc",
],
hdrs = [
- "public/pw_allocator/split_free_list_allocator.h",
+ "public/pw_allocator/libc_allocator.h",
],
includes = ["public"],
deps = [
":allocator",
- ":block",
"//pw_assert",
"//pw_bytes",
- "//pw_result",
"//pw_status",
],
)
pw_cc_library(
- name = "libc_allocator",
- srcs = [
- "libc_allocator.cc",
+ name = "null_allocator",
+ hdrs = [
+ "public/pw_allocator/null_allocator.h",
+ ],
+ includes = ["public"],
+ deps = [
+ ":allocator",
],
+)
+
+pw_cc_library(
+ name = "simple_allocator",
hdrs = [
- "public/pw_allocator/libc_allocator.h",
+ "public/pw_allocator/simple_allocator.h",
],
includes = ["public"],
deps = [
":allocator",
- "//pw_assert",
+ ":block",
"//pw_bytes",
- "//pw_status",
],
)
pw_cc_library(
- name = "null_allocator",
+ name = "split_free_list_allocator",
+ srcs = [
+ "split_free_list_allocator.cc",
+ ],
hdrs = [
- "public/pw_allocator/null_allocator.h",
+ "public/pw_allocator/split_free_list_allocator.h",
],
includes = ["public"],
deps = [
":allocator",
+ ":block",
+ "//pw_assert",
+ "//pw_bytes",
+ "//pw_result",
+ "//pw_status",
],
)
@@ -173,13 +186,16 @@ pw_cc_library(
"allocator_testing.cc",
],
hdrs = [
- "pw_allocator_private/allocator_testing.h",
+ "public/pw_allocator/allocator_testing.h",
],
+ includes = ["public"],
deps = [
":allocator",
":block",
+ ":simple_allocator",
"//pw_assert",
"//pw_bytes",
+ "//pw_unit_test",
],
)
@@ -279,11 +295,23 @@ pw_cc_test(
)
pw_cc_test(
+ name = "simple_allocator_test",
+ srcs = [
+ "simple_allocator_test.cc",
+ ],
+ deps = [
+ ":allocator_testing",
+ ":simple_allocator",
+ ],
+)
+
+pw_cc_test(
name = "split_free_list_allocator_test",
srcs = [
"split_free_list_allocator_test.cc",
],
deps = [
+ ":allocator_testing",
":split_free_list_allocator",
"//pw_bytes",
"//pw_containers:vector",
diff --git a/pw_allocator/BUILD.gn b/pw_allocator/BUILD.gn
index f1f2ac7c7..e7039f0a1 100644
--- a/pw_allocator/BUILD.gn
+++ b/pw_allocator/BUILD.gn
@@ -136,6 +136,16 @@ pw_source_set("null_allocator") {
public_deps = [ ":allocator" ]
}
+pw_source_set("simple_allocator") {
+ public_configs = [ ":default_config" ]
+ public = [ "public/pw_allocator/simple_allocator.h" ]
+ public_deps = [
+ ":allocator",
+ ":block",
+ dir_pw_bytes,
+ ]
+}
+
pw_source_set("split_free_list_allocator") {
public_configs = [ ":default_config" ]
public = [ "public/pw_allocator/split_free_list_allocator.h" ]
@@ -181,19 +191,22 @@ pw_test_group("tests") {
":freelist_heap_test",
":libc_allocator_test",
":null_allocator_test",
+ ":simple_allocator_test",
":split_free_list_allocator_test",
":unique_ptr_test",
]
}
pw_source_set("allocator_testing") {
- public = [ "pw_allocator_private/allocator_testing.h" ]
+ public = [ "public/pw_allocator/allocator_testing.h" ]
public_deps = [
":allocator",
":block",
- dir_pw_assert,
+ ":simple_allocator",
dir_pw_bytes,
+ dir_pw_unit_test,
]
+ deps = [ dir_pw_assert ]
sources = [ "allocator_testing.cc" ]
}
@@ -258,8 +271,19 @@ pw_test("null_allocator_test") {
sources = [ "null_allocator_test.cc" ]
}
+pw_test("simple_allocator_test") {
+ configs = [ ":enable_heap_poison" ]
+ deps = [
+ ":allocator_testing",
+ ":simple_allocator",
+ ]
+ sources = [ "simple_allocator_test.cc" ]
+}
+
pw_test("split_free_list_allocator_test") {
+ configs = [ ":enable_heap_poison" ]
deps = [
+ ":allocator_testing",
":split_free_list_allocator",
"$dir_pw_containers:vector",
dir_pw_bytes,
@@ -273,7 +297,10 @@ pw_test("unique_ptr_test") {
}
pw_doc_group("docs") {
- inputs = [ "doc_resources/pw_allocator_heap_visualizer_demo.png" ]
+ inputs = [
+ "doc_resources/pw_allocator_heap_visualizer_demo.png",
+ "public/pw_allocator/simple_allocator.h",
+ ]
sources = [ "docs.rst" ]
report_deps = [ ":allocator_size_report" ]
}
diff --git a/pw_allocator/CMakeLists.txt b/pw_allocator/CMakeLists.txt
index 82ed74540..6f923edc7 100644
--- a/pw_allocator/CMakeLists.txt
+++ b/pw_allocator/CMakeLists.txt
@@ -102,55 +102,69 @@ pw_add_library(pw_allocator.freelist_heap STATIC
freelist_heap.cc
)
-pw_add_library(pw_allocator.split_free_list_allocator STATIC
+pw_add_library(pw_allocator.libc_allocator STATIC
+ SOURCES
+ libc_allocator.cc
HEADERS
- public/pw_allocator/split_free_list_allocator.h
+ public/pw_allocator/libc_allocator.h
PUBLIC_INCLUDES
public
PUBLIC_DEPS
pw_allocator.allocator
- pw_allocator.block
- pw_bytes
- pw_result
pw_status
PRIVATE_DEPS
pw_assert
- SOURCES
- split_free_list_allocator.cc
+ pw_bytes
)
-pw_add_library(pw_allocator.libc_allocator STATIC
- SOURCES
- libc_allocator.cc
+pw_add_library(pw_allocator.null_allocator INTERFACE
HEADERS
- public/pw_allocator/libc_allocator.h
+ public/pw_allocator/null_allocator.h
PUBLIC_INCLUDES
public
PUBLIC_DEPS
pw_allocator.allocator
- pw_status
- PRIVATE_DEPS
- pw_assert
+)
+
+pw_add_library(pw_allocator.simple_allocator INTERFACE
+ HEADERS
+ public/pw_allocator/simple_allocator.h
+ PUBLIC_DEPS
+ pw_allocator.allocator
+ pw_allocator.block
pw_bytes
)
-pw_add_library(pw_allocator.null_allocator INTERFACE
+pw_add_library(pw_allocator.split_free_list_allocator STATIC
HEADERS
- public/pw_allocator/null_allocator.h
+ public/pw_allocator/split_free_list_allocator.h
PUBLIC_INCLUDES
public
PUBLIC_DEPS
pw_allocator.allocator
+ pw_allocator.block
+ pw_bytes
+ pw_result
+ pw_status
+ PRIVATE_DEPS
+ pw_assert
+ SOURCES
+ split_free_list_allocator.cc
)
pw_add_library(pw_allocator.allocator_testing STATIC
HEADERS
- pw_allocator_private/allocator_testing.h
+ public/pw_allocator/allocator_testing.h
+ PUBLIC_INCLUDES
+ public
PUBLIC_DEPS
pw_allocator.allocator
pw_allocator.block
- pw_assert
+ pw_allocator.simple_allocator
pw_bytes
+ PRIVATE_DEPS
+ pw_assert
+ pw_unit_test
SOURCES
allocator_testing.cc
)
@@ -245,10 +259,19 @@ pw_add_test(pw_allocator.null_allocator_test
pw_allocator
)
+pw_add_test(pw_allocator.simple_allocator_test
+ SOURCES
+ simple_allocator_test.cc
+ PRIVATE_DEPS
+ pw_allocator.allocator_testing
+ pw_allocator.simple_allocator
+)
+
pw_add_test(pw_allocator.split_free_list_allocator_test
SOURCES
split_free_list_allocator_test.cc
PRIVATE_DEPS
+ pw_allocator.allocator_testing
pw_allocator.split_free_list_allocator
pw_containers.vector
pw_bytes
diff --git a/pw_allocator/allocator_metric_proxy_test.cc b/pw_allocator/allocator_metric_proxy_test.cc
index e426349a9..cfc94b2f0 100644
--- a/pw_allocator/allocator_metric_proxy_test.cc
+++ b/pw_allocator/allocator_metric_proxy_test.cc
@@ -15,27 +15,23 @@
#include "pw_allocator/allocator_metric_proxy.h"
#include "gtest/gtest.h"
-#include "pw_allocator_private/allocator_testing.h"
+#include "pw_allocator/allocator_testing.h"
namespace pw::allocator {
namespace {
// Test fixtures.
-struct AllocatorMetricProxyTest : ::testing::Test {
- private:
- std::array<std::byte, 256> buffer = {};
- test::FakeAllocator wrapped;
+class AllocatorMetricProxyTest : public ::testing::Test {
+ protected:
+ AllocatorMetricProxyTest() : allocator(0) {}
- public:
- AllocatorMetricProxy allocator;
+ void SetUp() override { allocator.Initialize(*wrapped); }
- AllocatorMetricProxyTest() : allocator(0) {}
+ AllocatorMetricProxy allocator;
- void SetUp() override {
- EXPECT_TRUE(wrapped.Initialize(buffer).ok());
- allocator.Initialize(wrapped);
- }
+ private:
+ test::AllocatorForTestWithBuffer<256> wrapped;
};
// Unit tests.
diff --git a/pw_allocator/allocator_test.cc b/pw_allocator/allocator_test.cc
index bbd3f6c77..f0bd1981d 100644
--- a/pw_allocator/allocator_test.cc
+++ b/pw_allocator/allocator_test.cc
@@ -17,7 +17,7 @@
#include <cstddef>
#include "gtest/gtest.h"
-#include "pw_allocator_private/allocator_testing.h"
+#include "pw_allocator/allocator_testing.h"
#include "pw_bytes/alignment.h"
namespace pw::allocator {
@@ -25,14 +25,15 @@ namespace {
// Test fixtures.
-struct AllocatorTest : ::testing::Test {
- private:
- std::array<std::byte, 256> buffer = {};
+class AllocatorTest : public ::testing::Test {
+ protected:
+ void SetUp() override { EXPECT_EQ(allocator.Init(buffer), OkStatus()); }
+ void TearDown() override { allocator.DeallocateAll(); }
- public:
- test::FakeAllocator allocator;
+ test::AllocatorForTest allocator;
- void SetUp() override { EXPECT_EQ(allocator.Initialize(buffer), OkStatus()); }
+ private:
+ std::array<std::byte, 256> buffer = {};
};
// Unit tests
diff --git a/pw_allocator/allocator_testing.cc b/pw_allocator/allocator_testing.cc
index bc67f330b..47d046aae 100644
--- a/pw_allocator/allocator_testing.cc
+++ b/pw_allocator/allocator_testing.cc
@@ -12,31 +12,35 @@
// License for the specific language governing permissions and limitations under
// the License.
-#include "pw_allocator_private/allocator_testing.h"
+#include "pw_allocator/allocator_testing.h"
#include "pw_assert/check.h"
-#include "pw_bytes/alignment.h"
namespace pw::allocator::test {
-Status FakeAllocator::Initialize(ByteSpan buffer) {
- auto result = BlockType::Init(buffer);
- if (!result.ok()) {
- return result.status();
+AllocatorForTest::~AllocatorForTest() {
+ for (auto* block : allocator_.blocks()) {
+ PW_DCHECK(
+ !block->Used(),
+ "The block at %p was still in use when its allocator was "
+ "destroyed. All memory allocated by an allocator must be released "
+ "before the allocator goes out of scope.",
+ static_cast<void*>(block));
}
- begin_ = *result;
- end_ = begin_->Next();
+}
+
+Status AllocatorForTest::Init(ByteSpan bytes) {
ResetParameters();
- return OkStatus();
+ return allocator_.Init(bytes);
}
-void FakeAllocator::Exhaust() {
- for (BlockType* block = begin_; block != end_; block = block->Next()) {
+void AllocatorForTest::Exhaust() {
+ for (auto* block : allocator_.blocks()) {
block->MarkUsed();
}
}
-void FakeAllocator::ResetParameters() {
+void AllocatorForTest::ResetParameters() {
allocate_size_ = 0;
deallocate_ptr_ = nullptr;
deallocate_size_ = 0;
@@ -45,82 +49,38 @@ void FakeAllocator::ResetParameters() {
resize_new_size_ = 0;
}
-Status FakeAllocator::DoQuery(const void* ptr, size_t size, size_t) const {
- PW_CHECK(begin_ != nullptr);
- if (size == 0 || ptr == nullptr) {
- return Status::OutOfRange();
- }
- const auto* bytes = static_cast<const std::byte*>(ptr);
- BlockType* target = BlockType::FromUsableSpace(const_cast<std::byte*>(bytes));
- if (!target->IsValid()) {
- return Status::OutOfRange();
- }
- size = AlignUp(size, BlockType::kAlignment);
- for (BlockType* block = begin_; block != end_; block = block->Next()) {
- if (block == target && block->InnerSize() == size) {
- return OkStatus();
- }
+void AllocatorForTest::DeallocateAll() {
+ for (auto* block : allocator_.blocks()) {
+ BlockType::Free(block);
}
- return Status::OutOfRange();
+ ResetParameters();
+}
+
+Status AllocatorForTest::DoQuery(const void* ptr,
+ size_t size,
+ size_t alignment) const {
+ return allocator_.QueryUnchecked(ptr, size, alignment);
}
-void* FakeAllocator::DoAllocate(size_t size, size_t) {
- PW_CHECK(begin_ != nullptr);
+void* AllocatorForTest::DoAllocate(size_t size, size_t alignment) {
allocate_size_ = size;
- for (BlockType* block = begin_; block != end_; block = block->Next()) {
- if (block->Used() || block->InnerSize() < size) {
- continue;
- }
- Result<BlockType*> result = BlockType::Split(block, size);
- if (result.ok()) {
- BlockType* next = *result;
- BlockType::MergeNext(next).IgnoreError();
- }
- block->MarkUsed();
- return block->UsableSpace();
- }
- return nullptr;
+ return allocator_.AllocateUnchecked(size, alignment);
}
-void FakeAllocator::DoDeallocate(void* ptr, size_t size, size_t alignment) {
- PW_CHECK(begin_ != nullptr);
+void AllocatorForTest::DoDeallocate(void* ptr, size_t size, size_t alignment) {
deallocate_ptr_ = ptr;
deallocate_size_ = size;
- if (!DoQuery(ptr, size, alignment).ok()) {
- return;
- }
- BlockType* block = BlockType::FromUsableSpace(static_cast<std::byte*>(ptr));
- block->MarkFree();
- BlockType::MergeNext(block).IgnoreError();
- BlockType* prev = block->Prev();
- BlockType::MergeNext(prev).IgnoreError();
+ return allocator_.DeallocateUnchecked(ptr, size, alignment);
}
-bool FakeAllocator::DoResize(void* ptr,
- size_t old_size,
- size_t old_alignment,
- size_t new_size) {
- PW_CHECK(begin_ != nullptr);
+bool AllocatorForTest::DoResize(void* ptr,
+ size_t old_size,
+ size_t old_alignment,
+ size_t new_size) {
resize_ptr_ = ptr;
resize_old_size_ = old_size;
resize_new_size_ = new_size;
- if (!DoQuery(ptr, old_size, old_alignment).ok() || old_size == 0 ||
- new_size == 0) {
- return false;
- }
- if (old_size == new_size) {
- return true;
- }
- BlockType* block = BlockType::FromUsableSpace(static_cast<std::byte*>(ptr));
- block->MarkFree();
- BlockType::MergeNext(block).IgnoreError();
-
- Result<BlockType*> result = BlockType::Split(block, new_size);
- if (!result.ok()) {
- BlockType::Split(block, old_size).IgnoreError();
- }
- block->MarkUsed();
- return block->InnerSize() >= new_size;
+ return allocator_.ResizeUnchecked(ptr, old_size, old_alignment, new_size);
}
} // namespace pw::allocator::test
diff --git a/pw_allocator/docs.rst b/pw_allocator/docs.rst
index decaf7e16..0d5545ccf 100644
--- a/pw_allocator/docs.rst
+++ b/pw_allocator/docs.rst
@@ -29,6 +29,19 @@ Allocator
.. doxygenclass:: pw::allocator::Allocator
:members:
+Example
+-------
+As an example, the following implements a simple allocator that tracks memory
+using ``Block``.
+
+.. literalinclude:: public/pw_allocator/simple_allocator.h
+ :language: cpp
+ :linenos:
+ :start-after: [pw_allocator_examples_simple_allocator]
+ :end-before: [pw_allocator_examples_simple_allocator]
+
+Other Implemetations
+--------------------
Provided implementations of the ``Allocator`` interface include:
- ``AllocatorMetricProxy``: Wraps another allocator and records its usage.
diff --git a/pw_allocator/fallback_allocator_test.cc b/pw_allocator/fallback_allocator_test.cc
index 982c6cc7d..c623fa652 100644
--- a/pw_allocator/fallback_allocator_test.cc
+++ b/pw_allocator/fallback_allocator_test.cc
@@ -15,7 +15,7 @@
#include "pw_allocator/fallback_allocator.h"
#include "gtest/gtest.h"
-#include "pw_allocator_private/allocator_testing.h"
+#include "pw_allocator/allocator_testing.h"
#include "pw_status/status.h"
namespace pw::allocator {
@@ -23,75 +23,73 @@ namespace {
// Test fixtures.
-struct FallbackAllocatorTest : ::testing::Test {
- private:
- std::array<std::byte, 128> buffer1 = {};
- std::array<std::byte, 128> buffer2 = {};
+class FallbackAllocatorTest : public ::testing::Test {
+ protected:
+ void SetUp() override { allocator.Initialize(*primary, *secondary); }
- public:
- test::FakeAllocator primary;
- test::FakeAllocator secondary;
- FallbackAllocator allocator;
-
- void SetUp() override {
- EXPECT_EQ(primary.Initialize(buffer1), OkStatus());
- EXPECT_EQ(secondary.Initialize(buffer2), OkStatus());
- allocator.Initialize(primary, secondary);
+ void TearDown() override {
+ primary->DeallocateAll();
+ secondary->DeallocateAll();
}
+
+ test::AllocatorForTestWithBuffer<128> primary;
+ test::AllocatorForTestWithBuffer<128> secondary;
+ FallbackAllocator allocator;
};
// Unit tests.
TEST_F(FallbackAllocatorTest, QueryValidPrimary) {
Layout layout = Layout::Of<uint32_t>();
- void* ptr = primary.Allocate(layout);
- EXPECT_TRUE(primary.Query(ptr, layout).ok());
- EXPECT_EQ(secondary.Query(ptr, layout), Status::OutOfRange());
+ void* ptr = primary->Allocate(layout);
+ EXPECT_TRUE(primary->Query(ptr, layout).ok());
+ EXPECT_EQ(secondary->Query(ptr, layout), Status::OutOfRange());
EXPECT_TRUE(allocator.Query(ptr, layout).ok());
}
TEST_F(FallbackAllocatorTest, QueryValidSecondary) {
Layout layout = Layout::Of<uint32_t>();
- void* ptr = secondary.Allocate(layout);
- EXPECT_FALSE(primary.Query(ptr, layout).ok());
- EXPECT_TRUE(secondary.Query(ptr, layout).ok());
+ void* ptr = secondary->Allocate(layout);
+ EXPECT_FALSE(primary->Query(ptr, layout).ok());
+ EXPECT_TRUE(secondary->Query(ptr, layout).ok());
EXPECT_TRUE(allocator.Query(ptr, layout).ok());
}
TEST_F(FallbackAllocatorTest, QueryInvalidPtr) {
std::array<std::byte, 128> buffer = {};
- test::FakeAllocator other;
- ASSERT_TRUE(other.Initialize(buffer).ok());
+ test::AllocatorForTest other;
+ ASSERT_EQ(other.Init(buffer), OkStatus());
Layout layout = Layout::Of<uint32_t>();
void* ptr = other.Allocate(layout);
- EXPECT_FALSE(primary.Query(ptr, layout).ok());
- EXPECT_FALSE(secondary.Query(ptr, layout).ok());
+ EXPECT_FALSE(primary->Query(ptr, layout).ok());
+ EXPECT_FALSE(secondary->Query(ptr, layout).ok());
EXPECT_FALSE(allocator.Query(ptr, layout).ok());
+ other.DeallocateAll();
}
TEST_F(FallbackAllocatorTest, AllocateFromPrimary) {
Layout layout = Layout::Of<uint32_t>();
void* ptr = allocator.Allocate(layout);
EXPECT_NE(ptr, nullptr);
- EXPECT_EQ(primary.allocate_size(), layout.size());
- EXPECT_EQ(secondary.allocate_size(), 0U);
+ EXPECT_EQ(primary->allocate_size(), layout.size());
+ EXPECT_EQ(secondary->allocate_size(), 0U);
}
TEST_F(FallbackAllocatorTest, AllocateFromSecondary) {
- primary.Exhaust();
+ primary->Exhaust();
Layout layout = Layout::Of<uint32_t>();
void* ptr = allocator.Allocate(layout);
EXPECT_NE(ptr, nullptr);
- EXPECT_EQ(primary.allocate_size(), layout.size());
- EXPECT_EQ(secondary.allocate_size(), layout.size());
+ EXPECT_EQ(primary->allocate_size(), layout.size());
+ EXPECT_EQ(secondary->allocate_size(), layout.size());
}
TEST_F(FallbackAllocatorTest, AllocateFailure) {
Layout layout = Layout::Of<uint32_t[0x10000]>();
void* ptr = allocator.Allocate(layout);
EXPECT_EQ(ptr, nullptr);
- EXPECT_EQ(primary.allocate_size(), layout.size());
- EXPECT_EQ(secondary.allocate_size(), layout.size());
+ EXPECT_EQ(primary->allocate_size(), layout.size());
+ EXPECT_EQ(secondary->allocate_size(), layout.size());
}
TEST_F(FallbackAllocatorTest, DeallocateUsingPrimary) {
@@ -99,22 +97,22 @@ TEST_F(FallbackAllocatorTest, DeallocateUsingPrimary) {
void* ptr = allocator.Allocate(layout);
ASSERT_NE(ptr, nullptr);
allocator.Deallocate(ptr, layout);
- EXPECT_EQ(primary.deallocate_ptr(), ptr);
- EXPECT_EQ(primary.deallocate_size(), layout.size());
- EXPECT_EQ(secondary.deallocate_ptr(), nullptr);
- EXPECT_EQ(secondary.deallocate_size(), 0U);
+ EXPECT_EQ(primary->deallocate_ptr(), ptr);
+ EXPECT_EQ(primary->deallocate_size(), layout.size());
+ EXPECT_EQ(secondary->deallocate_ptr(), nullptr);
+ EXPECT_EQ(secondary->deallocate_size(), 0U);
}
TEST_F(FallbackAllocatorTest, DeallocateUsingSecondary) {
- primary.Exhaust();
+ primary->Exhaust();
Layout layout = Layout::Of<uint32_t>();
void* ptr = allocator.Allocate(layout);
ASSERT_NE(ptr, nullptr);
allocator.Deallocate(ptr, layout);
- EXPECT_EQ(primary.deallocate_ptr(), nullptr);
- EXPECT_EQ(primary.deallocate_size(), 0U);
- EXPECT_EQ(secondary.deallocate_ptr(), ptr);
- EXPECT_EQ(secondary.deallocate_size(), layout.size());
+ EXPECT_EQ(primary->deallocate_ptr(), nullptr);
+ EXPECT_EQ(primary->deallocate_size(), 0U);
+ EXPECT_EQ(secondary->deallocate_ptr(), ptr);
+ EXPECT_EQ(secondary->deallocate_size(), layout.size());
}
TEST_F(FallbackAllocatorTest, ResizePrimary) {
@@ -124,69 +122,69 @@ TEST_F(FallbackAllocatorTest, ResizePrimary) {
size_t new_size = sizeof(uint32_t[3]);
EXPECT_TRUE(allocator.Resize(ptr, old_layout, new_size));
- EXPECT_EQ(primary.resize_ptr(), ptr);
- EXPECT_EQ(primary.resize_old_size(), old_layout.size());
- EXPECT_EQ(primary.resize_new_size(), new_size);
+ EXPECT_EQ(primary->resize_ptr(), ptr);
+ EXPECT_EQ(primary->resize_old_size(), old_layout.size());
+ EXPECT_EQ(primary->resize_new_size(), new_size);
// Secondary should not be touched.
- EXPECT_EQ(secondary.resize_ptr(), nullptr);
- EXPECT_EQ(secondary.resize_old_size(), 0U);
- EXPECT_EQ(secondary.resize_new_size(), 0U);
+ EXPECT_EQ(secondary->resize_ptr(), nullptr);
+ EXPECT_EQ(secondary->resize_old_size(), 0U);
+ EXPECT_EQ(secondary->resize_new_size(), 0U);
}
TEST_F(FallbackAllocatorTest, ResizePrimaryFailure) {
Layout old_layout = Layout::Of<uint32_t>();
void* ptr = allocator.Allocate(old_layout);
ASSERT_NE(ptr, nullptr);
- primary.Exhaust();
+ primary->Exhaust();
size_t new_size = sizeof(uint32_t[3]);
EXPECT_FALSE(allocator.Resize(ptr, old_layout, new_size));
- EXPECT_EQ(primary.resize_ptr(), ptr);
- EXPECT_EQ(primary.resize_old_size(), old_layout.size());
- EXPECT_EQ(primary.resize_new_size(), new_size);
+ EXPECT_EQ(primary->resize_ptr(), ptr);
+ EXPECT_EQ(primary->resize_old_size(), old_layout.size());
+ EXPECT_EQ(primary->resize_new_size(), new_size);
// Secondary should not be touched.
- EXPECT_EQ(secondary.resize_ptr(), nullptr);
- EXPECT_EQ(secondary.resize_old_size(), 0U);
- EXPECT_EQ(secondary.resize_new_size(), 0U);
+ EXPECT_EQ(secondary->resize_ptr(), nullptr);
+ EXPECT_EQ(secondary->resize_old_size(), 0U);
+ EXPECT_EQ(secondary->resize_new_size(), 0U);
}
TEST_F(FallbackAllocatorTest, ResizeSecondary) {
- primary.Exhaust();
+ primary->Exhaust();
Layout old_layout = Layout::Of<uint32_t>();
void* ptr = allocator.Allocate(old_layout);
ASSERT_NE(ptr, nullptr);
size_t new_size = sizeof(uint32_t[3]);
EXPECT_TRUE(allocator.Resize(ptr, old_layout, new_size));
- EXPECT_EQ(secondary.resize_ptr(), ptr);
- EXPECT_EQ(secondary.resize_old_size(), old_layout.size());
- EXPECT_EQ(secondary.resize_new_size(), new_size);
+ EXPECT_EQ(secondary->resize_ptr(), ptr);
+ EXPECT_EQ(secondary->resize_old_size(), old_layout.size());
+ EXPECT_EQ(secondary->resize_new_size(), new_size);
// Primary should not be touched.
- EXPECT_EQ(primary.resize_ptr(), nullptr);
- EXPECT_EQ(primary.resize_old_size(), 0U);
- EXPECT_EQ(primary.resize_new_size(), 0U);
+ EXPECT_EQ(primary->resize_ptr(), nullptr);
+ EXPECT_EQ(primary->resize_old_size(), 0U);
+ EXPECT_EQ(primary->resize_new_size(), 0U);
}
TEST_F(FallbackAllocatorTest, ResizeSecondaryFailure) {
- primary.Exhaust();
+ primary->Exhaust();
Layout old_layout = Layout::Of<uint32_t>();
void* ptr = allocator.Allocate(old_layout);
ASSERT_NE(ptr, nullptr);
- secondary.Exhaust();
+ secondary->Exhaust();
size_t new_size = sizeof(uint32_t[3]);
EXPECT_FALSE(allocator.Resize(ptr, old_layout, new_size));
- EXPECT_EQ(secondary.resize_ptr(), ptr);
- EXPECT_EQ(secondary.resize_old_size(), old_layout.size());
- EXPECT_EQ(secondary.resize_new_size(), new_size);
+ EXPECT_EQ(secondary->resize_ptr(), ptr);
+ EXPECT_EQ(secondary->resize_old_size(), old_layout.size());
+ EXPECT_EQ(secondary->resize_new_size(), new_size);
// Primary should not be touched.
- EXPECT_EQ(primary.resize_ptr(), nullptr);
- EXPECT_EQ(primary.resize_old_size(), 0U);
- EXPECT_EQ(primary.resize_new_size(), 0U);
+ EXPECT_EQ(primary->resize_ptr(), nullptr);
+ EXPECT_EQ(primary->resize_old_size(), 0U);
+ EXPECT_EQ(primary->resize_new_size(), 0U);
}
TEST_F(FallbackAllocatorTest, ReallocateSameAllocator) {
@@ -201,22 +199,22 @@ TEST_F(FallbackAllocatorTest, ReallocateSameAllocator) {
size_t new_size = sizeof(uint32_t[3]);
void* new_ptr = allocator.Reallocate(ptr1, old_layout, new_size);
EXPECT_NE(new_ptr, nullptr);
- EXPECT_EQ(primary.deallocate_ptr(), ptr1);
- EXPECT_EQ(primary.deallocate_size(), old_layout.size());
- EXPECT_EQ(primary.allocate_size(), new_size);
+ EXPECT_EQ(primary->deallocate_ptr(), ptr1);
+ EXPECT_EQ(primary->deallocate_size(), old_layout.size());
+ EXPECT_EQ(primary->allocate_size(), new_size);
}
TEST_F(FallbackAllocatorTest, ReallocateDifferentAllocator) {
Layout old_layout = Layout::Of<uint32_t>();
void* ptr = allocator.Allocate(old_layout);
- primary.Exhaust();
+ primary->Exhaust();
size_t new_size = sizeof(uint32_t[3]);
void* new_ptr = allocator.Reallocate(ptr, old_layout, new_size);
EXPECT_NE(new_ptr, nullptr);
- EXPECT_EQ(primary.deallocate_ptr(), ptr);
- EXPECT_EQ(primary.deallocate_size(), old_layout.size());
- EXPECT_EQ(secondary.allocate_size(), new_size);
+ EXPECT_EQ(primary->deallocate_ptr(), ptr);
+ EXPECT_EQ(primary->deallocate_size(), old_layout.size());
+ EXPECT_EQ(secondary->allocate_size(), new_size);
}
} // namespace
diff --git a/pw_allocator/libc_allocator_test.cc b/pw_allocator/libc_allocator_test.cc
index d69dcb072..967a1069d 100644
--- a/pw_allocator/libc_allocator_test.cc
+++ b/pw_allocator/libc_allocator_test.cc
@@ -22,7 +22,8 @@ namespace pw::allocator {
// Test fixtures.
-struct LibCAllocatorTest : ::testing::Test {
+class LibCAllocatorTest : public ::testing::Test {
+ protected:
LibCAllocator allocator;
};
diff --git a/pw_allocator/public/pw_allocator/allocator_testing.h b/pw_allocator/public/pw_allocator/allocator_testing.h
new file mode 100644
index 000000000..41077522d
--- /dev/null
+++ b/pw_allocator/public/pw_allocator/allocator_testing.h
@@ -0,0 +1,134 @@
+// Copyright 2023 The Pigweed Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+#pragma once
+
+#include <array>
+#include <cstddef>
+
+#include "gtest/gtest.h"
+#include "pw_allocator/allocator.h"
+#include "pw_allocator/block.h"
+#include "pw_allocator/simple_allocator.h"
+#include "pw_bytes/span.h"
+
+namespace pw::allocator::test {
+
+/// Simple memory allocator for testing.
+///
+/// This allocator records the most recent parameters passed to the `Allocator`
+/// interface methods, and returns them via accessors.
+class AllocatorForTest : public Allocator {
+ public:
+ constexpr AllocatorForTest() = default;
+ ~AllocatorForTest() override;
+
+ size_t allocate_size() const { return allocate_size_; }
+ void* deallocate_ptr() const { return deallocate_ptr_; }
+ size_t deallocate_size() const { return deallocate_size_; }
+ void* resize_ptr() const { return resize_ptr_; }
+ size_t resize_old_size() const { return resize_old_size_; }
+ size_t resize_new_size() const { return resize_new_size_; }
+
+ /// Provides memory for the allocator to allocate from.
+ Status Init(ByteSpan bytes);
+
+ /// Allocates all the memory from this object.
+ void Exhaust();
+
+ /// Resets the recorded parameters to an initial state.
+ void ResetParameters();
+
+ /// This frees all memory associated with this allocator.
+ void DeallocateAll();
+
+ private:
+ using BlockType = Block<>;
+
+ /// @copydoc Allocator::Query
+ Status DoQuery(const void* ptr, size_t size, size_t alignment) const override;
+
+ /// @copydoc Allocator::Allocate
+ void* DoAllocate(size_t size, size_t alignment) override;
+
+ /// @copydoc Allocator::Deallocate
+ void DoDeallocate(void* ptr, size_t size, size_t alignment) override;
+
+ /// @copydoc Allocator::Resize
+ bool DoResize(void* ptr,
+ size_t old_size,
+ size_t old_alignment,
+ size_t new_size) override;
+
+ SimpleAllocator allocator_;
+ size_t allocate_size_ = 0;
+ void* deallocate_ptr_ = nullptr;
+ size_t deallocate_size_ = 0;
+ void* resize_ptr_ = nullptr;
+ size_t resize_old_size_ = 0;
+ size_t resize_new_size_ = 0;
+};
+
+/// Wraps a default-constructed type a buffer holding a region of memory.
+///
+/// Although the type is arbitrary, the intended purpose of of this class is to
+/// provide allocators with memory to use when testing.
+///
+/// This class uses composition instead of inheritance in order to allow the
+/// wrapped type's destructor to reference the memory without risk of a
+/// use-after-free. As a result, the specific methods of the wrapped type
+/// are not directly accesible. Instead, they can be accessed using the `*` and
+/// `->` operators, e.g.
+///
+/// @code{.cpp}
+/// WithBuffer<MyAllocator, 256> allocator;
+/// allocator->MethodSpecificToMyAllocator();
+/// @endcode
+///
+/// Note that this class does NOT initialize the allocator, since initialization
+/// is not specified as part of the `Allocator` interface and may vary from
+/// allocator to allocator. As a result, typical usgae includes deriving a class
+/// that initializes the wrapped allocator with the buffer in a constructor. See
+/// `AllocatorForTestWithBuffer` below for an example.
+///
+/// @tparam T The wrapped object.
+/// @tparam kBufferSize The size of the backing memory, in bytes.
+/// @tparam AlignType Buffer memory will be aligned to this type's
+/// alignment boundary.
+template <typename T, size_t kBufferSize, typename AlignType = uint8_t>
+class WithBuffer {
+ public:
+ static constexpr size_t kCapacity = kBufferSize;
+
+ std::byte* data() { return buffer_.data(); }
+ size_t size() const { return buffer_.size(); }
+
+ T& operator*() { return obj_; }
+ T* operator->() { return &obj_; }
+
+ private:
+ alignas(AlignType) std::array<std::byte, kBufferSize> buffer_;
+ T obj_;
+};
+
+/// An `AllocatorForTest` that is automatically initialized on construction.
+template <size_t kBufferSize>
+class AllocatorForTestWithBuffer
+ : public WithBuffer<AllocatorForTest, kBufferSize> {
+ public:
+ AllocatorForTestWithBuffer() {
+ EXPECT_EQ((*this)->Init(ByteSpan(this->data(), this->size())), OkStatus());
+ }
+};
+
+} // namespace pw::allocator::test
diff --git a/pw_allocator/public/pw_allocator/block.h b/pw_allocator/public/pw_allocator/block.h
index d8d505fa5..4b5e7e594 100644
--- a/pw_allocator/public/pw_allocator/block.h
+++ b/pw_allocator/public/pw_allocator/block.h
@@ -151,53 +151,8 @@ class BaseBlock {
/// contiguous region of memory returned from a call to `Init`. This block can
/// be split into smaller blocks that refer to their neighbors. Neighboring
/// blocks can be merged. These behaviors allows ``Allocator``s to track
-/// allocated memory with a small amount of overhead.
-///
-/// For example, the following is a simple but functional ``Allocator`` using
-/// ``Block``:
-///
-/// @code{.cpp}
-/// // TODO(b/306686936): Consider moving this to a standalone example.
-/// class SimpleAllocator {
-/// public:
-/// Status Init(ByteSpan region) {
-/// auto result = Block<>::Init(region);
-/// if (!result.ok()) { return result.status(); }
-/// begin_ = *result;
-/// end_ = begin_->Next();
-/// return OkStatus();
-/// }
-///
-/// private:
-/// void* DoAllocate(Layout layout) override {
-/// for (auto* block = begin_; block != end_; block = block->Next()) {
-/// if (block->InnerSize() >= layout.size()) {
-/// if (auto result=Block<>::Split(block, layout.size()); result.ok()) {
-/// // Try to merge the leftovers with the next block.
-/// Block<>::MergeNext(*result).IgnoreError();
-/// }
-/// block->MarkUsed();
-/// return block->UsableSpace();
-/// }
-/// }
-/// return nullptr;
-/// }
-///
-/// void DoDeallocate(void* ptr, Layout) override {
-/// Block<>* block = Block<>::FromUsableSpace(ptr);
-/// block->MarkFree();
-/// // Try to merge the released block with its neighbors.
-/// Block<>::MergeNext(block).IgnoreError();
-/// block = block->Prev();
-/// Block<>::MergeNext(block).IgnoreError();
-/// }
-///
-/// bool DoResize(void*, Layout, size_t) {
-/// // Always reallocate.
-/// return false;
-/// }
-/// };
-/// @endcode
+/// allocated memory with a small amount of overhead. See
+/// pw_allocator_private/simple_allocator.h for an example.
///
/// Blocks will always be aligned to a `kAlignment boundary. Block sizes will
/// always be rounded up to a multiple of `kAlignment`.
diff --git a/pw_allocator/public/pw_allocator/simple_allocator.h b/pw_allocator/public/pw_allocator/simple_allocator.h
new file mode 100644
index 000000000..98513426e
--- /dev/null
+++ b/pw_allocator/public/pw_allocator/simple_allocator.h
@@ -0,0 +1,87 @@
+// Copyright 2023 The Pigweed Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+#pragma once
+
+#include "pw_allocator/allocator.h"
+#include "pw_allocator/block.h"
+
+namespace pw::allocator {
+
+// DOCSTAG: [pw_allocator_examples_simple_allocator]
+/// Simple allocator that uses a list of `Block`s.
+class SimpleAllocator : public Allocator {
+ public:
+ using Block = pw::allocator::Block<>;
+ using Range = typename Block::Range;
+
+ constexpr SimpleAllocator() = default;
+
+ /// Initialize this allocator to allocate memory from `region`.
+ Status Init(ByteSpan region) {
+ auto result = Block::Init(region);
+ if (result.ok()) {
+ blocks_ = *result;
+ }
+ return result.status();
+ }
+
+ /// Return the range of blocks for this allocator.
+ Range blocks() { return Range(blocks_); }
+
+ private:
+ /// @copydoc Allocator::Query
+ Status DoQuery(const void* ptr, size_t, size_t) const override {
+ for (auto* block : Range(blocks_)) {
+ if (block->UsableSpace() == ptr) {
+ return OkStatus();
+ }
+ }
+ return Status::OutOfRange();
+ }
+
+ /// @copydoc Allocator::Allocate
+ void* DoAllocate(size_t size, size_t alignment) override {
+ for (auto* block : Range(blocks_)) {
+ if (Block::AllocFirst(block, size, alignment).ok()) {
+ return block->UsableSpace();
+ }
+ }
+ return nullptr;
+ }
+
+ /// @copydoc Allocator::Deallocate
+ void DoDeallocate(void* ptr, size_t, size_t) override {
+ if (ptr == nullptr) {
+ return;
+ }
+ auto* bytes = static_cast<std::byte*>(ptr);
+ Block* block = Block::FromUsableSpace(bytes);
+ Block::Free(block);
+ }
+
+ /// @copydoc Allocator::Resize
+ bool DoResize(void* ptr, size_t, size_t, size_t new_size) override {
+ if (ptr == nullptr) {
+ return false;
+ }
+ auto* bytes = static_cast<std::byte*>(ptr);
+ Block* block = Block::FromUsableSpace(bytes);
+ return Block::Resize(block, new_size).ok();
+ }
+
+ Block* blocks_ = nullptr;
+};
+// DOCSTAG: [pw_allocator_examples_simple_allocator]
+
+} // namespace pw::allocator
diff --git a/pw_allocator/pw_allocator_private/allocator_testing.h b/pw_allocator/pw_allocator_private/allocator_testing.h
deleted file mode 100644
index 030492836..000000000
--- a/pw_allocator/pw_allocator_private/allocator_testing.h
+++ /dev/null
@@ -1,78 +0,0 @@
-// Copyright 2023 The Pigweed Authors
-//
-// Licensed under the Apache License, Version 2.0 (the "License"); you may not
-// use this file except in compliance with the License. You may obtain a copy of
-// the License at
-//
-// https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-// License for the specific language governing permissions and limitations under
-// the License.
-#pragma once
-
-#include <array>
-#include <cstddef>
-
-#include "pw_allocator/allocator.h"
-#include "pw_allocator/block.h"
-#include "pw_bytes/span.h"
-
-namespace pw::allocator::test {
-
-/// Fake memory allocator for testing.
-///
-/// This allocator can return a fixed number of allocations made using an
-/// internal buffer. It records the most recent parameters passed to the
-/// `Allocator` interface methods, and returns them via accessors.
-class FakeAllocator : public Allocator {
- public:
- constexpr FakeAllocator() = default;
-
- size_t allocate_size() const { return allocate_size_; }
- void* deallocate_ptr() const { return deallocate_ptr_; }
- size_t deallocate_size() const { return deallocate_size_; }
- void* resize_ptr() const { return resize_ptr_; }
- size_t resize_old_size() const { return resize_old_size_; }
- size_t resize_new_size() const { return resize_new_size_; }
-
- /// Provides memory for the allocator to allocate from.
- Status Initialize(ByteSpan buffer);
-
- /// Allocates all the memory from this object.
- void Exhaust();
-
- /// Resets the recorded parameters to an initial state.
- void ResetParameters();
-
- private:
- using BlockType = Block<>;
-
- /// @copydoc Allocator::Query
- Status DoQuery(const void* ptr, size_t size, size_t alignment) const override;
-
- /// @copydoc Allocator::Allocate
- void* DoAllocate(size_t size, size_t alignment) override;
-
- /// @copydoc Allocator::Deallocate
- void DoDeallocate(void* ptr, size_t size, size_t alignment) override;
-
- /// @copydoc Allocator::Resize
- bool DoResize(void* ptr,
- size_t old_size,
- size_t old_alignment,
- size_t new_size) override;
-
- BlockType* begin_ = nullptr;
- BlockType* end_ = nullptr;
- size_t allocate_size_ = 0;
- void* deallocate_ptr_ = nullptr;
- size_t deallocate_size_ = 0;
- void* resize_ptr_ = nullptr;
- size_t resize_old_size_ = 0;
- size_t resize_new_size_ = 0;
-};
-
-} // namespace pw::allocator::test
diff --git a/pw_allocator/simple_allocator_test.cc b/pw_allocator/simple_allocator_test.cc
new file mode 100644
index 000000000..4904bfe3b
--- /dev/null
+++ b/pw_allocator/simple_allocator_test.cc
@@ -0,0 +1,66 @@
+// Copyright 2023 The Pigweed Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+
+#include "pw_allocator/simple_allocator.h"
+
+#include "gtest/gtest.h"
+#include "pw_allocator/allocator_testing.h"
+
+namespace pw::allocator {
+
+// Size of the memory region to use in the tests below.
+constexpr size_t kCapacity = 256;
+
+// An `SimpleAllocator` that is automatically initialized on construction.
+class SimpleAllocatorWithBuffer
+ : public test::WithBuffer<SimpleAllocator, kCapacity> {
+ public:
+ SimpleAllocatorWithBuffer() {
+ EXPECT_EQ((*this)->Init(ByteSpan(this->data(), this->size())), OkStatus());
+ }
+};
+
+// This is not meant to be a rigorous unit test of individual behaviors, as much
+// as simply a way to demonstrate and exercise the simple allocator.
+TEST(SimpleAllocatorTest, AllocateResizeDeallocate) {
+ SimpleAllocatorWithBuffer allocator;
+
+ // Can allocate usable memory.
+ constexpr size_t kSize1 = kCapacity / 4;
+ constexpr Layout layout1 = Layout::Of<std::byte[kSize1]>();
+ auto* ptr = static_cast<std::byte*>(allocator->Allocate(layout1));
+ ASSERT_NE(ptr, nullptr);
+ memset(ptr, 0x5A, kSize1);
+
+ // Can shrink memory. Contents are preserved.
+ constexpr size_t kSize2 = kCapacity / 8;
+ constexpr Layout layout2 = Layout::Of<std::byte[kSize2]>();
+ EXPECT_TRUE(allocator->Resize(ptr, layout1, layout2.size()));
+ for (size_t i = 0; i < kSize2; ++i) {
+ EXPECT_EQ(size_t(ptr[i]), 0x5Au);
+ }
+
+ // Can grow memory. Contents are preserved.
+ constexpr size_t kSize3 = kCapacity / 2;
+ constexpr Layout layout3 = Layout::Of<std::byte[kSize3]>();
+ EXPECT_TRUE(allocator->Resize(ptr, layout2, layout3.size()));
+ for (size_t i = 0; i < kSize2; ++i) {
+ EXPECT_EQ(size_t(ptr[i]), 0x5Au);
+ }
+
+ // Can free memory.
+ allocator->Deallocate(ptr, layout3);
+}
+
+} // namespace pw::allocator
diff --git a/pw_allocator/split_free_list_allocator_test.cc b/pw_allocator/split_free_list_allocator_test.cc
index a3d7fab0c..ab4bab42b 100644
--- a/pw_allocator/split_free_list_allocator_test.cc
+++ b/pw_allocator/split_free_list_allocator_test.cc
@@ -15,6 +15,7 @@
#include "pw_allocator/split_free_list_allocator.h"
#include "gtest/gtest.h"
+#include "pw_allocator/allocator_testing.h"
#include "pw_allocator/block.h"
#include "pw_bytes/alignment.h"
#include "pw_bytes/span.h"
@@ -23,24 +24,39 @@
namespace pw::allocator {
namespace {
-// Test fixture.
+// Test fixtures.
+
+// Size of the memory region to use in the tests below.
+static constexpr size_t kCapacity = 256;
+
+// Minimum size of a "large" allocation; allocation less than this size are
+// considered "small".
+static constexpr size_t kThreshold = 64;
+
+// An `SplitFreeListAllocator` that is automatically initialized on
+// construction.
+using BlockType = Block<uint16_t, kCapacity>;
+class SplitFreeListAllocatorWithBuffer
+ : public test::
+ WithBuffer<SplitFreeListAllocator<BlockType>, kCapacity, BlockType> {
+ public:
+ SplitFreeListAllocatorWithBuffer() {
+ EXPECT_EQ((*this)->Init(ByteSpan(this->data(), this->size()), kThreshold),
+ OkStatus());
+ }
+};
+// Test case fixture that allows individual tests to cache allocations and
+// release them automatically on tear-down.
class SplitFreeListAllocatorTest : public ::testing::Test {
protected:
- static constexpr size_t kCapacity = 256;
-
- using BlockType = Block<uint16_t, kCapacity>;
-
static constexpr size_t kMaxSize = kCapacity - BlockType::kBlockOverhead;
- static constexpr size_t kThreshold = 64;
static constexpr size_t kNumPtrs = 16;
void SetUp() override {
for (size_t i = 0; i < kNumPtrs; ++i) {
ptrs_[i] = nullptr;
}
- span_ = ByteSpan(buffer_.data(), buffer_.size());
- ASSERT_EQ(allocator_.Init(span_, kThreshold), OkStatus());
}
// This method simply ensures the memory is usable by writing to it.
@@ -51,14 +67,12 @@ class SplitFreeListAllocatorTest : public ::testing::Test {
if (ptrs_[i] != nullptr) {
// `SplitFreeListAllocator::Deallocate` doesn't actually use the layout,
// as the information it needs is encoded in the blocks.
- allocator_.Deallocate(ptrs_[i], Layout::Of<void*>());
+ allocator_->Deallocate(ptrs_[i], Layout::Of<void*>());
}
}
}
- alignas(BlockType) std::array<std::byte, kCapacity> buffer_;
- ByteSpan span_;
- SplitFreeListAllocator<BlockType> allocator_;
+ SplitFreeListAllocatorWithBuffer allocator_;
// Tests can store allocations in this array to have them automatically
// freed in `TearDown`, including on ASSERT failure. If pointers are manually
@@ -72,15 +86,16 @@ TEST_F(SplitFreeListAllocatorTest, InitUnaligned) {
// The test fixture uses aligned memory to make it easier to reason about
// allocations, but that isn't strictly required.
SplitFreeListAllocator<Block<>> unaligned;
- EXPECT_EQ(unaligned.Init(span_.subspan(1), kThreshold), OkStatus());
+ ByteSpan bytes(allocator_.data(), allocator_.size());
+ EXPECT_EQ(unaligned.Init(bytes.subspan(1), kThreshold), OkStatus());
}
TEST_F(SplitFreeListAllocatorTest, AllocateLarge) {
constexpr Layout layout = Layout::Of<std::byte[kThreshold]>();
- ptrs_[0] = allocator_.Allocate(layout);
+ ptrs_[0] = allocator_->Allocate(layout);
ASSERT_NE(ptrs_[0], nullptr);
- EXPECT_GE(ptrs_[0], buffer_.data());
- EXPECT_LT(ptrs_[0], buffer_.data() + buffer_.size());
+ EXPECT_GE(ptrs_[0], allocator_.data());
+ EXPECT_LT(ptrs_[0], allocator_.data() + allocator_.size());
UseMemory(ptrs_[0], layout.size());
}
@@ -88,27 +103,27 @@ TEST_F(SplitFreeListAllocatorTest, AllocateSmall) {
// Returned pointer should not be from the beginning, but should still be in
// range. Exact pointer depends on allocator's minimum allocation size.
constexpr Layout layout = Layout::Of<uint8_t>();
- ptrs_[0] = allocator_.Allocate(layout);
+ ptrs_[0] = allocator_->Allocate(layout);
ASSERT_NE(ptrs_[0], nullptr);
- EXPECT_GT(ptrs_[0], buffer_.data());
- EXPECT_LT(ptrs_[0], buffer_.data() + buffer_.size());
+ EXPECT_GT(ptrs_[0], allocator_.data());
+ EXPECT_LT(ptrs_[0], allocator_.data() + allocator_.size());
UseMemory(ptrs_[0], layout.size());
}
TEST_F(SplitFreeListAllocatorTest, AllocateTooLarge) {
- ptrs_[0] = allocator_.Allocate(Layout::Of<std::byte[kCapacity * 2]>());
+ ptrs_[0] = allocator_->Allocate(Layout::Of<std::byte[kCapacity * 2]>());
EXPECT_EQ(ptrs_[0], nullptr);
}
TEST_F(SplitFreeListAllocatorTest, AllocateLargeAlignment) {
constexpr size_t kSize = sizeof(uint32_t);
constexpr size_t kAlignment = 64;
- ptrs_[0] = allocator_.AllocateUnchecked(kSize, kAlignment);
+ ptrs_[0] = allocator_->AllocateUnchecked(kSize, kAlignment);
ASSERT_NE(ptrs_[0], nullptr);
EXPECT_EQ(reinterpret_cast<uintptr_t>(ptrs_[0]) % kAlignment, 0U);
UseMemory(ptrs_[0], kSize);
- ptrs_[1] = allocator_.AllocateUnchecked(kSize, kAlignment);
+ ptrs_[1] = allocator_->AllocateUnchecked(kSize, kAlignment);
ASSERT_NE(ptrs_[1], nullptr);
EXPECT_EQ(reinterpret_cast<uintptr_t>(ptrs_[1]) % kAlignment, 0U);
UseMemory(ptrs_[0], kSize);
@@ -116,7 +131,8 @@ TEST_F(SplitFreeListAllocatorTest, AllocateLargeAlignment) {
TEST_F(SplitFreeListAllocatorTest, AllocateFromUnaligned) {
SplitFreeListAllocator<Block<>> unaligned;
- ASSERT_EQ(unaligned.Init(span_.subspan(1), kThreshold), OkStatus());
+ ByteSpan bytes(allocator_.data(), allocator_.size());
+ ASSERT_EQ(unaligned.Init(bytes.subspan(1), kThreshold), OkStatus());
constexpr Layout layout = Layout::Of<std::byte[kThreshold + 8]>();
void* ptr = unaligned.Allocate(layout);
@@ -127,9 +143,9 @@ TEST_F(SplitFreeListAllocatorTest, AllocateFromUnaligned) {
TEST_F(SplitFreeListAllocatorTest, AllocateAlignmentFailure) {
// Determine the total number of available bytes.
- auto base = reinterpret_cast<uintptr_t>(buffer_.data());
+ auto base = reinterpret_cast<uintptr_t>(allocator_.data());
uintptr_t addr = AlignUp(base, BlockType::kAlignment);
- size_t outer_size = buffer_.size() - (addr - base);
+ size_t outer_size = allocator_.size() - (addr - base);
// The first block is large....
addr += BlockType::kBlockOverhead + kThreshold;
@@ -142,43 +158,43 @@ TEST_F(SplitFreeListAllocatorTest, AllocateAlignmentFailure) {
}
// And the last block consumes the remaining space.
- // size_t outer_size = allocator_.begin()->OuterSize();
+ // size_t outer_size = allocator_->begin()->OuterSize();
size_t inner_size1 = next - addr;
size_t inner_size2 = kThreshold * 2;
size_t inner_size3 =
outer_size - (BlockType::kBlockOverhead * 3 + inner_size1 + inner_size2);
// Allocate all the blocks.
- ptrs_[0] = allocator_.AllocateUnchecked(inner_size1, 1);
+ ptrs_[0] = allocator_->AllocateUnchecked(inner_size1, 1);
ASSERT_NE(ptrs_[0], nullptr);
- ptrs_[1] = allocator_.AllocateUnchecked(inner_size2, 1);
+ ptrs_[1] = allocator_->AllocateUnchecked(inner_size2, 1);
ASSERT_NE(ptrs_[1], nullptr);
- ptrs_[2] = allocator_.AllocateUnchecked(inner_size3, 1);
+ ptrs_[2] = allocator_->AllocateUnchecked(inner_size3, 1);
ASSERT_NE(ptrs_[2], nullptr);
// If done correctly, the second block's usable space should be unaligned.
EXPECT_NE(reinterpret_cast<uintptr_t>(ptrs_[1]) % kAlignment, 0U);
// Free the second region. This leaves an unaligned region available.
- allocator_.DeallocateUnchecked(ptrs_[1], inner_size2, 1);
+ allocator_->DeallocateUnchecked(ptrs_[1], inner_size2, 1);
ptrs_[1] = nullptr;
// The allocator should be unable to create an aligned region..
- ptrs_[3] = allocator_.AllocateUnchecked(inner_size2, kAlignment);
+ ptrs_[3] = allocator_->AllocateUnchecked(inner_size2, kAlignment);
EXPECT_EQ(ptrs_[3], nullptr);
}
TEST_F(SplitFreeListAllocatorTest, DeallocateNull) {
constexpr Layout layout = Layout::Of<uint8_t>();
- allocator_.Deallocate(nullptr, layout);
+ allocator_->Deallocate(nullptr, layout);
}
TEST_F(SplitFreeListAllocatorTest, DeallocateShuffled) {
constexpr Layout layout = Layout::Of<std::byte[32]>();
// Allocate until the pool is exhausted.
for (size_t i = 0; i < kNumPtrs; ++i) {
- ptrs_[i] = allocator_.Allocate(layout);
+ ptrs_[i] = allocator_->Allocate(layout);
if (ptrs_[i] == nullptr) {
break;
}
@@ -194,7 +210,7 @@ TEST_F(SplitFreeListAllocatorTest, DeallocateShuffled) {
}
// Deallocate everything.
for (size_t i = 0; i < kNumPtrs; ++i) {
- allocator_.Deallocate(ptrs_[i], layout);
+ allocator_->Deallocate(ptrs_[i], layout);
ptrs_[i] = nullptr;
}
}
@@ -206,23 +222,23 @@ TEST_F(SplitFreeListAllocatorTest, IterateOverBlocks) {
// Allocate eight blocks of alternating sizes. After this, the will also be a
// ninth, unallocated block of the remaining memory.
for (size_t i = 0; i < 4; ++i) {
- ptrs_[i] = allocator_.Allocate(layout1);
+ ptrs_[i] = allocator_->Allocate(layout1);
ASSERT_NE(ptrs_[i], nullptr);
- ptrs_[i + 4] = allocator_.Allocate(layout2);
+ ptrs_[i + 4] = allocator_->Allocate(layout2);
ASSERT_NE(ptrs_[i + 4], nullptr);
}
// Deallocate every other block. After this there will be four more
// unallocated blocks, for a total of five.
for (size_t i = 0; i < 4; ++i) {
- allocator_.Deallocate(ptrs_[i], layout1);
+ allocator_->Deallocate(ptrs_[i], layout1);
}
// Count the blocks. The unallocated ones vary in size, but the allocated ones
// should all be the same.
size_t free_count = 0;
size_t used_count = 0;
- for (auto* block : allocator_.blocks()) {
+ for (auto* block : allocator_->blocks()) {
if (block->Used()) {
EXPECT_GE(block->InnerSize(), layout2.size());
++used_count;
@@ -236,116 +252,116 @@ TEST_F(SplitFreeListAllocatorTest, IterateOverBlocks) {
TEST_F(SplitFreeListAllocatorTest, QueryLargeValid) {
constexpr Layout layout = Layout::Of<std::byte[kThreshold * 2]>();
- ptrs_[0] = allocator_.Allocate(layout);
- EXPECT_EQ(allocator_.Query(ptrs_[0], layout), OkStatus());
+ ptrs_[0] = allocator_->Allocate(layout);
+ EXPECT_EQ(allocator_->Query(ptrs_[0], layout), OkStatus());
}
TEST_F(SplitFreeListAllocatorTest, QuerySmallValid) {
constexpr Layout layout = Layout::Of<uint8_t>();
- ptrs_[0] = allocator_.Allocate(layout);
- EXPECT_EQ(allocator_.Query(ptrs_[0], layout), OkStatus());
+ ptrs_[0] = allocator_->Allocate(layout);
+ EXPECT_EQ(allocator_->Query(ptrs_[0], layout), OkStatus());
}
TEST_F(SplitFreeListAllocatorTest, QueryInvalidPtr) {
constexpr Layout layout = Layout::Of<SplitFreeListAllocatorTest>();
- EXPECT_EQ(allocator_.Query(this, layout), Status::OutOfRange());
+ EXPECT_EQ(allocator_->Query(this, layout), Status::OutOfRange());
}
TEST_F(SplitFreeListAllocatorTest, ResizeNull) {
constexpr Layout old_layout = Layout::Of<uint8_t>();
size_t new_size = 1;
- EXPECT_FALSE(allocator_.Resize(nullptr, old_layout, new_size));
+ EXPECT_FALSE(allocator_->Resize(nullptr, old_layout, new_size));
}
TEST_F(SplitFreeListAllocatorTest, ResizeSame) {
constexpr Layout old_layout = Layout::Of<uint32_t>();
- ptrs_[0] = allocator_.Allocate(old_layout);
+ ptrs_[0] = allocator_->Allocate(old_layout);
ASSERT_NE(ptrs_[0], nullptr);
constexpr Layout new_layout = Layout::Of<uint32_t>();
- EXPECT_TRUE(allocator_.Resize(ptrs_[0], old_layout, new_layout.size()));
+ EXPECT_TRUE(allocator_->Resize(ptrs_[0], old_layout, new_layout.size()));
ASSERT_NE(ptrs_[0], nullptr);
UseMemory(ptrs_[0], new_layout.size());
}
TEST_F(SplitFreeListAllocatorTest, ResizeLargeSmaller) {
constexpr Layout old_layout = Layout::Of<std::byte[kMaxSize]>();
- ptrs_[0] = allocator_.Allocate(old_layout);
+ ptrs_[0] = allocator_->Allocate(old_layout);
ASSERT_NE(ptrs_[0], nullptr);
// Shrinking always succeeds.
constexpr Layout new_layout = Layout::Of<std::byte[kThreshold]>();
- EXPECT_TRUE(allocator_.Resize(ptrs_[0], old_layout, new_layout.size()));
+ EXPECT_TRUE(allocator_->Resize(ptrs_[0], old_layout, new_layout.size()));
ASSERT_NE(ptrs_[0], nullptr);
UseMemory(ptrs_[0], new_layout.size());
}
TEST_F(SplitFreeListAllocatorTest, ResizeLargeLarger) {
constexpr Layout old_layout = Layout::Of<std::byte[kThreshold]>();
- ptrs_[0] = allocator_.Allocate(old_layout);
+ ptrs_[0] = allocator_->Allocate(old_layout);
ASSERT_NE(ptrs_[0], nullptr);
// Nothing after ptr, so `Resize` should succeed.
constexpr Layout new_layout = Layout::Of<std::byte[kMaxSize]>();
- EXPECT_TRUE(allocator_.Resize(ptrs_[0], old_layout, new_layout.size()));
+ EXPECT_TRUE(allocator_->Resize(ptrs_[0], old_layout, new_layout.size()));
ASSERT_NE(ptrs_[0], nullptr);
UseMemory(ptrs_[0], new_layout.size());
}
TEST_F(SplitFreeListAllocatorTest, ResizeLargeLargerFailure) {
constexpr Layout old_layout = Layout::Of<std::byte[kThreshold]>();
- ptrs_[0] = allocator_.Allocate(old_layout);
+ ptrs_[0] = allocator_->Allocate(old_layout);
ASSERT_NE(ptrs_[0], nullptr);
- ptrs_[1] = allocator_.Allocate(old_layout);
+ ptrs_[1] = allocator_->Allocate(old_layout);
ASSERT_NE(ptrs_[1], nullptr);
// Memory after ptr is already allocated, so `Resize` should fail.
- EXPECT_FALSE(allocator_.Resize(ptrs_[0], old_layout, kMaxSize));
+ EXPECT_FALSE(allocator_->Resize(ptrs_[0], old_layout, kMaxSize));
}
TEST_F(SplitFreeListAllocatorTest, ResizeLargeSmallerAcrossThreshold) {
constexpr Layout old_layout = Layout::Of<std::byte[kThreshold]>();
- ptrs_[0] = allocator_.Allocate(old_layout);
+ ptrs_[0] = allocator_->Allocate(old_layout);
ASSERT_NE(ptrs_[0], nullptr);
// Shrinking succeeds, and the pointer is unchanged even though it is now
// below the threshold.
constexpr Layout new_layout = Layout::Of<std::byte[kThreshold / 4]>();
- EXPECT_TRUE(allocator_.Resize(ptrs_[0], old_layout, new_layout.size()));
+ EXPECT_TRUE(allocator_->Resize(ptrs_[0], old_layout, new_layout.size()));
ASSERT_NE(ptrs_[0], nullptr);
UseMemory(ptrs_[0], new_layout.size());
}
TEST_F(SplitFreeListAllocatorTest, ResizeSmallSmaller) {
constexpr Layout old_layout = Layout::Of<uint32_t>();
- ptrs_[0] = allocator_.Allocate(old_layout);
+ ptrs_[0] = allocator_->Allocate(old_layout);
ASSERT_NE(ptrs_[0], nullptr);
// Shrinking always succeeds.
constexpr Layout new_layout = Layout::Of<uint8_t>();
- EXPECT_TRUE(allocator_.Resize(ptrs_[0], old_layout, new_layout.size()));
+ EXPECT_TRUE(allocator_->Resize(ptrs_[0], old_layout, new_layout.size()));
}
TEST_F(SplitFreeListAllocatorTest, ResizeSmallLarger) {
// First, allocate a trailing block.
constexpr Layout layout1 = Layout::Of<std::byte[kThreshold / 4]>();
- ptrs_[0] = allocator_.Allocate(layout1);
+ ptrs_[0] = allocator_->Allocate(layout1);
ASSERT_NE(ptrs_[0], nullptr);
// Next allocate the memory to be resized.
constexpr Layout old_layout = Layout::Of<std::byte[kThreshold / 4]>();
- ptrs_[1] = allocator_.Allocate(old_layout);
+ ptrs_[1] = allocator_->Allocate(old_layout);
ASSERT_NE(ptrs_[1], nullptr);
// Now free the trailing block.
- allocator_.Deallocate(ptrs_[0], layout1);
+ allocator_->Deallocate(ptrs_[0], layout1);
ptrs_[0] = nullptr;
// And finally, resize. Since the memory after the block is available and big
// enough, `Resize` should succeed.
constexpr Layout new_layout = Layout::Of<std::byte[kThreshold / 2]>();
- EXPECT_TRUE(allocator_.Resize(ptrs_[1], old_layout, new_layout.size()));
+ EXPECT_TRUE(allocator_->Resize(ptrs_[1], old_layout, new_layout.size()));
ASSERT_NE(ptrs_[1], nullptr);
UseMemory(ptrs_[1], new_layout.size());
}
@@ -353,48 +369,48 @@ TEST_F(SplitFreeListAllocatorTest, ResizeSmallLarger) {
TEST_F(SplitFreeListAllocatorTest, ResizeSmallLargerFailure) {
// First, allocate a trailing block.
constexpr Layout layout1 = Layout::Of<std::byte[kThreshold / 4]>();
- ptrs_[0] = allocator_.Allocate(layout1);
+ ptrs_[0] = allocator_->Allocate(layout1);
ASSERT_NE(ptrs_[0], nullptr);
// Next allocate the memory to be resized.
constexpr Layout old_layout = Layout::Of<std::byte[kThreshold / 4]>();
- ptrs_[1] = allocator_.Allocate(old_layout);
+ ptrs_[1] = allocator_->Allocate(old_layout);
ASSERT_NE(ptrs_[1], nullptr);
// Now free the trailing block.
- allocator_.Deallocate(ptrs_[0], layout1);
+ allocator_->Deallocate(ptrs_[0], layout1);
ptrs_[0] = nullptr;
// And finally, resize. Since the memory after the block is available but not
// big enough, `Resize` should fail.
size_t new_size = 48;
- EXPECT_FALSE(allocator_.Resize(ptrs_[1], old_layout, new_size));
+ EXPECT_FALSE(allocator_->Resize(ptrs_[1], old_layout, new_size));
}
TEST_F(SplitFreeListAllocatorTest, ResizeSmallLargerAcrossThreshold) {
// First, allocate several trailing block.
constexpr Layout layout1 = Layout::Of<std::byte[kThreshold / 2]>();
- ptrs_[0] = allocator_.Allocate(layout1);
+ ptrs_[0] = allocator_->Allocate(layout1);
ASSERT_NE(ptrs_[0], nullptr);
- ptrs_[1] = allocator_.Allocate(layout1);
+ ptrs_[1] = allocator_->Allocate(layout1);
ASSERT_NE(ptrs_[1], nullptr);
// Next allocate the memory to be resized.
constexpr Layout old_layout = Layout::Of<std::byte[kThreshold / 4]>();
- ptrs_[2] = allocator_.Allocate(old_layout);
+ ptrs_[2] = allocator_->Allocate(old_layout);
EXPECT_NE(ptrs_[2], nullptr);
// Now free the trailing blocks.
- allocator_.Deallocate(ptrs_[0], layout1);
+ allocator_->Deallocate(ptrs_[0], layout1);
ptrs_[0] = nullptr;
- allocator_.Deallocate(ptrs_[1], layout1);
+ allocator_->Deallocate(ptrs_[1], layout1);
ptrs_[1] = nullptr;
// Growing succeeds, and the pointer is unchanged even though it is now
// above the threshold.
constexpr Layout new_layout = Layout::Of<std::byte[kThreshold]>();
- EXPECT_TRUE(allocator_.Resize(ptrs_[2], old_layout, new_layout.size()));
+ EXPECT_TRUE(allocator_->Resize(ptrs_[2], old_layout, new_layout.size()));
ASSERT_NE(ptrs_[2], nullptr);
UseMemory(ptrs_[2], new_layout.size());
}
diff --git a/pw_allocator/unique_ptr_test.cc b/pw_allocator/unique_ptr_test.cc
index 9382a2bf5..c0aa57cce 100644
--- a/pw_allocator/unique_ptr_test.cc
+++ b/pw_allocator/unique_ptr_test.cc
@@ -16,18 +16,12 @@
#include "gtest/gtest.h"
#include "pw_allocator/allocator.h"
-#include "pw_allocator_private/allocator_testing.h"
+#include "pw_allocator/allocator_testing.h"
namespace pw::allocator {
namespace {
-class FakeAllocWithBuffer : public test::FakeAllocator {
- public:
- FakeAllocWithBuffer() { EXPECT_EQ(Initialize(buffer_), OkStatus()); }
-
- private:
- std::array<std::byte, 256> buffer_ = {};
-};
+using FakeAllocWithBuffer = test::AllocatorForTestWithBuffer<256>;
TEST(UniquePtr, DefaultInitializationIsNullptr) {
UniquePtr<int> empty;
@@ -42,7 +36,7 @@ TEST(UniquePtr, OperatorEqNullptrOnEmptyUniquePtrSucceeds) {
TEST(UniquePtr, OperatorEqNullptrAfterMakeUniqueFails) {
FakeAllocWithBuffer alloc;
- std::optional<UniquePtr<int>> ptr_opt = alloc.MakeUnique<int>(5);
+ std::optional<UniquePtr<int>> ptr_opt = alloc->MakeUnique<int>(5);
ASSERT_TRUE(ptr_opt.has_value());
UniquePtr<int> ptr = std::move(*ptr_opt);
EXPECT_TRUE(ptr != nullptr);
@@ -52,7 +46,7 @@ TEST(UniquePtr, OperatorEqNullptrAfterMakeUniqueFails) {
TEST(UniquePtr, OperatorEqNullptrAfterMakeUniqueNullptrTypeFails) {
FakeAllocWithBuffer alloc;
std::optional<UniquePtr<std::nullptr_t>> ptr_opt =
- alloc.MakeUnique<std::nullptr_t>(nullptr);
+ alloc->MakeUnique<std::nullptr_t>(nullptr);
ASSERT_TRUE(ptr_opt.has_value());
UniquePtr<std::nullptr_t> ptr = std::move(*ptr_opt);
EXPECT_TRUE(ptr != nullptr);
@@ -86,7 +80,7 @@ TEST(UniquePtr, MakeUniqueForwardsConstructorArguments) {
FakeAllocWithBuffer alloc;
MoveOnly mo(6);
std::optional<UniquePtr<BuiltWithMoveOnly>> ptr =
- alloc.MakeUnique<BuiltWithMoveOnly>(std::move(mo));
+ alloc->MakeUnique<BuiltWithMoveOnly>(std::move(mo));
ASSERT_TRUE(ptr.has_value());
EXPECT_EQ((*ptr)->Value(), 6);
}
@@ -97,17 +91,17 @@ TEST(UniquePtr, MoveConstructsFromSubClassAndFreesTotalSize) {
std::array<std::byte, 128> mem;
};
FakeAllocWithBuffer alloc;
- std::optional<UniquePtr<LargerSub>> ptr_opt = alloc.MakeUnique<LargerSub>();
+ std::optional<UniquePtr<LargerSub>> ptr_opt = alloc->MakeUnique<LargerSub>();
ASSERT_TRUE(ptr_opt.has_value());
- EXPECT_EQ(alloc.allocate_size(), 128ul);
+ EXPECT_EQ(alloc->allocate_size(), 128ul);
UniquePtr<LargerSub> ptr = std::move(*ptr_opt);
UniquePtr<Base> base_ptr(std::move(ptr));
- EXPECT_EQ(alloc.deallocate_size(), 0ul);
+ EXPECT_EQ(alloc->deallocate_size(), 0ul);
// The size that is deallocated here should be the size of the larger
// subclass, not the size of the smaller base class.
base_ptr.Reset();
- EXPECT_EQ(alloc.deallocate_size(), 128ul);
+ EXPECT_EQ(alloc->deallocate_size(), 128ul);
}
TEST(UniquePtr, MoveAssignsFromSubClassAndFreesTotalSize) {
@@ -116,17 +110,17 @@ TEST(UniquePtr, MoveAssignsFromSubClassAndFreesTotalSize) {
std::array<std::byte, 128> mem;
};
FakeAllocWithBuffer alloc;
- std::optional<UniquePtr<LargerSub>> ptr_opt = alloc.MakeUnique<LargerSub>();
+ std::optional<UniquePtr<LargerSub>> ptr_opt = alloc->MakeUnique<LargerSub>();
ASSERT_TRUE(ptr_opt.has_value());
- EXPECT_EQ(alloc.allocate_size(), 128ul);
+ EXPECT_EQ(alloc->allocate_size(), 128ul);
UniquePtr<LargerSub> ptr = std::move(*ptr_opt);
UniquePtr<Base> base_ptr = std::move(ptr);
- EXPECT_EQ(alloc.deallocate_size(), 0ul);
+ EXPECT_EQ(alloc->deallocate_size(), 0ul);
// The size that is deallocated here should be the size of the larger
// subclass, not the size of the smaller base class.
base_ptr.Reset();
- EXPECT_EQ(alloc.deallocate_size(), 128ul);
+ EXPECT_EQ(alloc->deallocate_size(), 128ul);
}
TEST(UniquePtr, DestructorDestroysAndFrees) {
@@ -141,15 +135,15 @@ TEST(UniquePtr, DestructorDestroysAndFrees) {
};
FakeAllocWithBuffer alloc;
std::optional<UniquePtr<DestructorCounter>> ptr_opt =
- alloc.MakeUnique<DestructorCounter>(count);
+ alloc->MakeUnique<DestructorCounter>(count);
ASSERT_TRUE(ptr_opt.has_value());
EXPECT_EQ(count, 0);
- EXPECT_EQ(alloc.deallocate_size(), 0ul);
+ EXPECT_EQ(alloc->deallocate_size(), 0ul);
ptr_opt.reset(); // clear the optional, destroying the UniquePtr
EXPECT_EQ(count, 1);
- EXPECT_EQ(alloc.deallocate_size(), sizeof(DestructorCounter));
+ EXPECT_EQ(alloc->deallocate_size(), sizeof(DestructorCounter));
}
} // namespace
-} // namespace pw::allocator \ No newline at end of file
+} // namespace pw::allocator