• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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