1 // Copyright 2024 The Pigweed Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License"); you may not
4 // use this file except in compliance with the License. You may obtain a copy of
5 // the License at
6 //
7 // https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12 // License for the specific language governing permissions and limitations under
13 // the License.
14
15 #include "pw_allocator/block_allocator_testing.h"
16
17 #include "pw_assert/check.h"
18 #include "pw_bytes/alignment.h"
19 #include "pw_status/status.h"
20
21 namespace pw::allocator::test {
22
23 // Test fixtures.
24
SetUp()25 void BlockAllocatorTestBase::SetUp() { ptrs_.fill(nullptr); }
26
Store(size_t index,void * ptr)27 void BlockAllocatorTestBase::Store(size_t index, void* ptr) {
28 ptrs_[index] = ptr;
29 }
30
Fetch(size_t index)31 void* BlockAllocatorTestBase::Fetch(size_t index) {
32 return index < kNumPtrs ? ptrs_[index] : nullptr;
33 }
34
UseMemory(void * ptr,size_t size)35 void BlockAllocatorTestBase::UseMemory(void* ptr, size_t size) {
36 std::memset(ptr, 0x5a, size);
37 }
38
39 // Unit tests.
40
GetCapacity()41 void BlockAllocatorTestBase::GetCapacity() {
42 Allocator& allocator = GetAllocator();
43 StatusWithSize capacity = allocator.GetCapacity();
44 EXPECT_EQ(capacity.status(), OkStatus());
45 EXPECT_EQ(capacity.size(), kCapacity);
46 }
47
AllocateLarge()48 void BlockAllocatorTestBase::AllocateLarge() {
49 Allocator& allocator = GetAllocator();
50 constexpr Layout layout = Layout::Of<std::byte[kLargeInnerSize]>();
51 Store(0, allocator.Allocate(layout));
52 ASSERT_NE(Fetch(0), nullptr);
53 ByteSpan bytes = GetBytes();
54 EXPECT_GE(Fetch(0), bytes.data());
55 EXPECT_LE(Fetch(0), bytes.data() + bytes.size());
56 UseMemory(Fetch(0), layout.size());
57 }
58
AllocateSmall()59 void BlockAllocatorTestBase::AllocateSmall() {
60 Allocator& allocator = GetAllocator();
61 constexpr Layout layout = Layout::Of<std::byte[kSmallInnerSize]>();
62 Store(0, allocator.Allocate(layout));
63 ASSERT_NE(Fetch(0), nullptr);
64 ByteSpan bytes = GetBytes();
65 EXPECT_GE(Fetch(0), bytes.data());
66 EXPECT_LE(Fetch(0), bytes.data() + bytes.size());
67 UseMemory(Fetch(0), layout.size());
68 }
69
AllocateTooLarge()70 void BlockAllocatorTestBase::AllocateTooLarge() {
71 Allocator& allocator = GetAllocator();
72 Store(0, allocator.Allocate(Layout::Of<std::byte[kCapacity * 2]>()));
73 EXPECT_EQ(Fetch(0), nullptr);
74 }
75
AllocateLargeAlignment()76 void BlockAllocatorTestBase::AllocateLargeAlignment() {
77 Allocator& allocator = GetAllocator();
78 constexpr size_t kAlignment = 64;
79 Store(0, allocator.Allocate(Layout(kLargeInnerSize, kAlignment)));
80 ASSERT_NE(Fetch(0), nullptr);
81 EXPECT_EQ(reinterpret_cast<uintptr_t>(Fetch(0)) % kAlignment, 0U);
82 UseMemory(Fetch(0), kLargeInnerSize);
83
84 Store(1, allocator.Allocate(Layout(kLargeInnerSize, kAlignment)));
85 ASSERT_NE(Fetch(1), nullptr);
86 EXPECT_EQ(reinterpret_cast<uintptr_t>(Fetch(1)) % kAlignment, 0U);
87 UseMemory(Fetch(1), kLargeInnerSize);
88 }
89
AllocateAlignmentFailure()90 void BlockAllocatorTestBase::AllocateAlignmentFailure() {
91 // Allocate a two blocks with an unaligned region between them.
92 constexpr size_t kAlignment = 128;
93 ByteSpan bytes = GetBytes();
94 auto addr = reinterpret_cast<uintptr_t>(bytes.data());
95 uintptr_t outer_size =
96 AlignUp(addr + kDefaultBlockOverhead, kAlignment) - addr + 1;
97 Allocator& allocator = GetAllocator({
98 {outer_size, 0},
99 {kLargeOuterSize, Preallocation::kIndexFree},
100 {Preallocation::kSizeRemaining, 2},
101 });
102
103 // The allocator should be unable to create an aligned region..
104 Store(1, allocator.Allocate(Layout(kLargeInnerSize, kAlignment)));
105 EXPECT_EQ(Fetch(1), nullptr);
106 }
107
DeallocateNull()108 void BlockAllocatorTestBase::DeallocateNull() {
109 Allocator& allocator = GetAllocator();
110 allocator.Deallocate(nullptr);
111 }
112
DeallocateShuffled()113 void BlockAllocatorTestBase::DeallocateShuffled() {
114 Allocator& allocator = GetAllocator();
115 constexpr Layout layout = Layout::Of<std::byte[kSmallInnerSize]>();
116 for (size_t i = 0; i < kNumPtrs; ++i) {
117 Store(i, allocator.Allocate(layout));
118 if (Fetch(i) == nullptr) {
119 break;
120 }
121 }
122
123 // Mix up the order of allocations.
124 for (size_t i = 0; i < kNumPtrs; ++i) {
125 if (i % 2 == 0 && i + 1 < kNumPtrs) {
126 void* tmp = Fetch(i);
127 Store(i, Fetch(i + 1));
128 Store(i + 1, tmp);
129 }
130 if (i % 3 == 0 && i + 2 < kNumPtrs) {
131 void* tmp = Fetch(i);
132 Store(i, Fetch(i + 2));
133 Store(i + 2, tmp);
134 }
135 }
136
137 // Deallocate everything.
138 for (size_t i = 0; i < kNumPtrs; ++i) {
139 allocator.Deallocate(Fetch(i));
140 Store(i, nullptr);
141 }
142 }
143
ResizeNull()144 void BlockAllocatorTestBase::ResizeNull() {
145 Allocator& allocator = GetAllocator();
146 size_t new_size = 1;
147 EXPECT_FALSE(allocator.Resize(nullptr, new_size));
148 }
149
ResizeLargeSame()150 void BlockAllocatorTestBase::ResizeLargeSame() {
151 Allocator& allocator = GetAllocator({
152 {kLargeOuterSize, 0},
153 {kLargeOuterSize, 1},
154 });
155 size_t new_size = kLargeInnerSize;
156 ASSERT_TRUE(allocator.Resize(Fetch(0), new_size));
157 UseMemory(Fetch(0), kLargeInnerSize);
158 }
159
ResizeLargeSmaller()160 void BlockAllocatorTestBase::ResizeLargeSmaller() {
161 Allocator& allocator = GetAllocator({
162 {kLargeOuterSize, 0},
163 {kLargeOuterSize, 1},
164 });
165 size_t new_size = kSmallInnerSize;
166 ASSERT_TRUE(allocator.Resize(Fetch(0), new_size));
167 UseMemory(Fetch(0), kSmallInnerSize);
168 }
169
ResizeLargeLarger()170 void BlockAllocatorTestBase::ResizeLargeLarger() {
171 Allocator& allocator = GetAllocator({
172 {kLargeOuterSize, 0},
173 {kLargeOuterSize, Preallocation::kIndexFree},
174 {kSmallOuterSize, 2},
175 });
176 size_t new_size = kLargeInnerSize * 2;
177 ASSERT_TRUE(allocator.Resize(Fetch(0), new_size));
178 UseMemory(Fetch(0), kLargeInnerSize * 2);
179 }
180
ResizeLargeLargerFailure()181 void BlockAllocatorTestBase::ResizeLargeLargerFailure() {
182 Allocator& allocator = GetAllocator({
183 {kLargeOuterSize, 0},
184 {kSmallOuterSize, 12},
185 });
186 // Memory after ptr is already allocated, so `Resize` should fail.
187 size_t new_size = kLargeInnerSize * 2;
188 EXPECT_FALSE(allocator.Resize(Fetch(0), new_size));
189 }
190
ResizeSmallSame()191 void BlockAllocatorTestBase::ResizeSmallSame() {
192 Allocator& allocator = GetAllocator({
193 {kSmallOuterSize, 0},
194 {kSmallOuterSize, 1},
195 });
196 size_t new_size = kSmallInnerSize;
197 ASSERT_TRUE(allocator.Resize(Fetch(0), new_size));
198 UseMemory(Fetch(0), kSmallInnerSize);
199 }
200
ResizeSmallSmaller()201 void BlockAllocatorTestBase::ResizeSmallSmaller() {
202 Allocator& allocator = GetAllocator({
203 {kSmallOuterSize, 0},
204 {kSmallOuterSize, 1},
205 });
206 size_t new_size = kSmallInnerSize / 2;
207 ASSERT_TRUE(allocator.Resize(Fetch(0), new_size));
208 UseMemory(Fetch(0), kSmallInnerSize / 2);
209 }
210
ResizeSmallLarger()211 void BlockAllocatorTestBase::ResizeSmallLarger() {
212 Allocator& allocator = GetAllocator({
213 {kSmallOuterSize, 0},
214 {kSmallOuterSize, Preallocation::kIndexFree},
215 {kSmallOuterSize, 2},
216 });
217 size_t new_size = kSmallInnerSize * 2;
218 ASSERT_TRUE(allocator.Resize(Fetch(0), new_size));
219 UseMemory(Fetch(0), kSmallInnerSize * 2);
220 }
221
ResizeSmallLargerFailure()222 void BlockAllocatorTestBase::ResizeSmallLargerFailure() {
223 Allocator& allocator = GetAllocator({
224 {kSmallOuterSize, 0},
225 {kSmallOuterSize, 1},
226 });
227 // Memory after ptr is already allocated, so `Resize` should fail.
228 size_t new_size = kSmallInnerSize * 2 + kDefaultBlockOverhead;
229 EXPECT_FALSE(allocator.Resize(Fetch(0), new_size));
230 }
231
232 } // namespace pw::allocator::test
233