• 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 <cstdint>
18 
19 #include "lib/stdcompat/bit.h"
20 #include "pw_allocator/layout.h"
21 #include "pw_assert/check.h"
22 #include "pw_bytes/alignment.h"
23 #include "pw_status/status.h"
24 
25 namespace pw::allocator::test {
26 
27 // Test fixtures.
28 
SetUp()29 void BlockAllocatorTestBase::SetUp() { ptrs_.fill(nullptr); }
30 
Store(size_t index,void * ptr)31 void BlockAllocatorTestBase::Store(size_t index, void* ptr) {
32   PW_CHECK_UINT_LT(index, kNumPtrs, "index is out of bounds");
33   PW_CHECK(ptr == nullptr || ptrs_[index] == nullptr,
34            "assigning pointer would clobber existing allocation");
35   ptrs_[index] = ptr;
36 }
37 
Fetch(size_t index)38 void* BlockAllocatorTestBase::Fetch(size_t index) {
39   return index < kNumPtrs ? ptrs_[index] : nullptr;
40 }
41 
Swap(size_t i,size_t j)42 void BlockAllocatorTestBase::Swap(size_t i, size_t j) {
43   std::swap(ptrs_[i], ptrs_[j]);
44 }
45 
UseMemory(void * ptr,size_t size)46 void BlockAllocatorTestBase::UseMemory(void* ptr, size_t size) {
47   std::memset(ptr, 0x5a, size);
48 }
49 
50 // Unit tests.
51 
GetCapacity(size_t expected)52 void BlockAllocatorTestBase::GetCapacity(size_t expected) {
53   Allocator& allocator = GetGenericAllocator();
54   StatusWithSize capacity = allocator.GetCapacity();
55   EXPECT_EQ(capacity.status(), OkStatus());
56   EXPECT_EQ(capacity.size(), expected);
57 }
58 
AllocateLarge()59 void BlockAllocatorTestBase::AllocateLarge() {
60   Allocator& allocator = GetGenericAllocator();
61   constexpr Layout layout = Layout::Of<std::byte[kLargeInnerSize]>();
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 
AllocateSmall()70 void BlockAllocatorTestBase::AllocateSmall() {
71   Allocator& allocator = GetGenericAllocator();
72   constexpr Layout layout = Layout::Of<std::byte[kSmallInnerSize]>();
73   Store(0, allocator.Allocate(layout));
74   ASSERT_NE(Fetch(0), nullptr);
75   ByteSpan bytes = GetBytes();
76   EXPECT_GE(Fetch(0), bytes.data());
77   EXPECT_LE(Fetch(0), bytes.data() + bytes.size());
78   UseMemory(Fetch(0), layout.size());
79 }
80 
AllocateTooLarge()81 void BlockAllocatorTestBase::AllocateTooLarge() {
82   Allocator& allocator = GetGenericAllocator();
83   Store(0, allocator.Allocate(Layout::Of<std::byte[kCapacity * 2]>()));
84   EXPECT_EQ(Fetch(0), nullptr);
85 }
86 
DeallocateNull()87 void BlockAllocatorTestBase::DeallocateNull() {
88   Allocator& allocator = GetGenericAllocator();
89   allocator.Deallocate(nullptr);
90 }
91 
DeallocateShuffled()92 void BlockAllocatorTestBase::DeallocateShuffled() {
93   Allocator& allocator = GetGenericAllocator();
94   constexpr Layout layout = Layout::Of<std::byte[kSmallInnerSize]>();
95   for (size_t i = 0; i < kNumPtrs; ++i) {
96     Store(i, allocator.Allocate(layout));
97     if (Fetch(i) == nullptr) {
98       break;
99     }
100   }
101 
102   // Mix up the order of allocations.
103   for (size_t i = 0; i < kNumPtrs; ++i) {
104     if (i % 2 == 0 && i + 1 < kNumPtrs) {
105       Swap(i, i + 1);
106     }
107     if (i % 3 == 0 && i + 2 < kNumPtrs) {
108       Swap(i, i + 2);
109     }
110   }
111 
112   // Deallocate everything.
113   for (size_t i = 0; i < kNumPtrs; ++i) {
114     allocator.Deallocate(Fetch(i));
115     Store(i, nullptr);
116   }
117 }
118 
ResizeNull()119 void BlockAllocatorTestBase::ResizeNull() {
120   Allocator& allocator = GetGenericAllocator();
121   size_t new_size = 1;
122   EXPECT_FALSE(allocator.Resize(nullptr, new_size));
123 }
124 
ResizeLargeSame()125 void BlockAllocatorTestBase::ResizeLargeSame() {
126   Allocator& allocator = GetGenericAllocator({
127       {kLargeOuterSize, Preallocation::kUsed},
128       {Preallocation::kSizeRemaining, Preallocation::kUsed},
129   });
130   size_t new_size = kLargeInnerSize;
131   ASSERT_TRUE(allocator.Resize(Fetch(0), new_size));
132   UseMemory(Fetch(0), kLargeInnerSize);
133 }
134 
ResizeLargeSmaller()135 void BlockAllocatorTestBase::ResizeLargeSmaller() {
136   Allocator& allocator = GetGenericAllocator({
137       {kLargeOuterSize, Preallocation::kUsed},
138       {Preallocation::kSizeRemaining, Preallocation::kUsed},
139   });
140   size_t new_size = kSmallInnerSize;
141   ASSERT_TRUE(allocator.Resize(Fetch(0), new_size));
142   UseMemory(Fetch(0), kSmallInnerSize);
143 }
144 
ResizeLargeLarger()145 void BlockAllocatorTestBase::ResizeLargeLarger() {
146   Allocator& allocator = GetGenericAllocator({
147       {kLargeOuterSize, Preallocation::kUsed},
148       {kLargeOuterSize, Preallocation::kFree},
149       {Preallocation::kSizeRemaining, Preallocation::kUsed},
150   });
151   size_t new_size = kLargeInnerSize * 2;
152   ASSERT_TRUE(allocator.Resize(Fetch(0), new_size));
153   UseMemory(Fetch(0), kLargeInnerSize * 2);
154 }
155 
ResizeLargeLargerFailure()156 void BlockAllocatorTestBase::ResizeLargeLargerFailure() {
157   Allocator& allocator = GetGenericAllocator({
158       {kLargeOuterSize, Preallocation::kUsed},
159       {Preallocation::kSizeRemaining, Preallocation::kUsed},
160   });
161   // Memory after ptr is already allocated, so `Resize` should fail.
162   size_t new_size = kLargeInnerSize * 2;
163   EXPECT_FALSE(allocator.Resize(Fetch(0), new_size));
164 }
165 
ResizeSmallSame()166 void BlockAllocatorTestBase::ResizeSmallSame() {
167   Allocator& allocator = GetGenericAllocator({
168       {kSmallOuterSize, Preallocation::kUsed},
169       {Preallocation::kSizeRemaining, Preallocation::kUsed},
170   });
171   size_t new_size = kSmallInnerSize;
172   ASSERT_TRUE(allocator.Resize(Fetch(0), new_size));
173   UseMemory(Fetch(0), kSmallInnerSize);
174 }
175 
ResizeSmallSmaller()176 void BlockAllocatorTestBase::ResizeSmallSmaller() {
177   Allocator& allocator = GetGenericAllocator({
178       {kSmallOuterSize, Preallocation::kUsed},
179       {Preallocation::kSizeRemaining, Preallocation::kUsed},
180   });
181   size_t new_size = kSmallInnerSize / 2;
182   ASSERT_TRUE(allocator.Resize(Fetch(0), new_size));
183   UseMemory(Fetch(0), kSmallInnerSize / 2);
184 }
185 
ResizeSmallLarger()186 void BlockAllocatorTestBase::ResizeSmallLarger() {
187   Allocator& allocator = GetGenericAllocator({
188       {kSmallOuterSize, Preallocation::kUsed},
189       {kSmallOuterSize, Preallocation::kFree},
190       {Preallocation::kSizeRemaining, Preallocation::kUsed},
191   });
192   size_t new_size = kSmallInnerSize * 2;
193   ASSERT_TRUE(allocator.Resize(Fetch(0), new_size));
194   UseMemory(Fetch(0), kSmallInnerSize * 2);
195 }
196 
ResizeSmallLargerFailure()197 void BlockAllocatorTestBase::ResizeSmallLargerFailure() {
198   Allocator& allocator = GetGenericAllocator({
199       {kSmallOuterSize, Preallocation::kUsed},
200       {Preallocation::kSizeRemaining, Preallocation::kUsed},
201   });
202   // Memory after ptr is already allocated, so `Resize` should fail.
203   size_t new_size = kSmallInnerSize * 2 + kDefaultBlockOverhead;
204   EXPECT_FALSE(allocator.Resize(Fetch(0), new_size));
205 }
206 
207 }  // namespace pw::allocator::test
208