xref: /aosp_15_r20/external/pigweed/pw_allocator/first_fit_test.cc (revision 61c4878ac05f98d0ceed94b57d316916de578985)
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/first_fit.h"
16 
17 #include <cstdint>
18 
19 #include "pw_allocator/block/detailed_block.h"
20 #include "pw_allocator/block_allocator_testing.h"
21 #include "pw_allocator/buffer.h"
22 #include "pw_allocator/dual_first_fit_block_allocator.h"
23 #include "pw_allocator/first_fit_block_allocator.h"
24 #include "pw_allocator/last_fit_block_allocator.h"
25 #include "pw_unit_test/framework.h"
26 
27 namespace {
28 
29 using ::pw::allocator::Layout;
30 using ::pw::allocator::test::Preallocation;
31 using BlockType = ::pw::allocator::FirstFitBlock<uint16_t>;
32 using FirstFitAllocator = ::pw::allocator::FirstFitAllocator<BlockType>;
33 using ::pw::allocator::test::BlockAllocatorTest;
34 
35 // Minimum size of a "large" allocation; allocation less than this size are
36 // considered "small" when using the "dual first fit" strategy.
37 static constexpr size_t kThreshold =
38     BlockAllocatorTest<FirstFitAllocator>::kSmallInnerSize * 2;
39 
40 class FirstFitAllocatorTest : public BlockAllocatorTest<FirstFitAllocator> {
41  public:
FirstFitAllocatorTest()42   FirstFitAllocatorTest() : BlockAllocatorTest(allocator_) {}
43 
44  private:
45   FirstFitAllocator allocator_;
46 };
47 
TEST_F(FirstFitAllocatorTest,AutomaticallyInit)48 TEST_F(FirstFitAllocatorTest, AutomaticallyInit) {
49   FirstFitAllocator allocator(GetBytes());
50   AutomaticallyInit(allocator);
51 }
52 
TEST_F(FirstFitAllocatorTest,ExplicitlyInit)53 TEST_F(FirstFitAllocatorTest, ExplicitlyInit) {
54   FirstFitAllocator allocator;
55   ExplicitlyInit(allocator);
56 }
57 
TEST_F(FirstFitAllocatorTest,GetCapacity)58 TEST_F(FirstFitAllocatorTest, GetCapacity) { GetCapacity(); }
59 
TEST_F(FirstFitAllocatorTest,AllocateLarge)60 TEST_F(FirstFitAllocatorTest, AllocateLarge) { AllocateLarge(); }
61 
TEST_F(FirstFitAllocatorTest,AllocateSmall)62 TEST_F(FirstFitAllocatorTest, AllocateSmall) { AllocateSmall(); }
63 
TEST_F(FirstFitAllocatorTest,AllocateLargeAlignment)64 TEST_F(FirstFitAllocatorTest, AllocateLargeAlignment) {
65   AllocateLargeAlignment();
66 }
67 
TEST_F(FirstFitAllocatorTest,AllocateAlignmentFailure)68 TEST_F(FirstFitAllocatorTest, AllocateAlignmentFailure) {
69   AllocateAlignmentFailure();
70 }
71 
TEST_F(FirstFitAllocatorTest,AllocatesZeroThreshold)72 TEST_F(FirstFitAllocatorTest, AllocatesZeroThreshold) {
73   FirstFitAllocator& allocator = GetAllocator({
74       {kSmallOuterSize, Preallocation::kFree},
75       {kSmallerOuterSize, Preallocation::kUsed},
76       {kSmallOuterSize, Preallocation::kFree},
77       {kSmallerOuterSize, Preallocation::kUsed},
78       {kLargeOuterSize, Preallocation::kFree},
79       {Preallocation::kSizeRemaining, Preallocation::kUsed},
80   });
81 
82   Store(0, allocator.Allocate(Layout(kSmallInnerSize, 1)));
83   EXPECT_EQ(NextAfter(0), Fetch(1));
84   Store(4, allocator.Allocate(Layout(kLargeInnerSize, 1)));
85   EXPECT_EQ(NextAfter(3), Fetch(4));
86   EXPECT_EQ(NextAfter(4), Fetch(5));
87 }
88 
TEST_F(FirstFitAllocatorTest,AllocatesMaxThreshold)89 TEST_F(FirstFitAllocatorTest, AllocatesMaxThreshold) {
90   FirstFitAllocator& allocator = GetAllocator({
91       {kLargeOuterSize, Preallocation::kFree},
92       {kSmallerOuterSize, Preallocation::kUsed},
93       {kSmallOuterSize, Preallocation::kFree},
94       {kSmallerOuterSize, Preallocation::kUsed},
95       {kSmallOuterSize, Preallocation::kFree},
96       {Preallocation::kSizeRemaining, Preallocation::kUsed},
97   });
98   allocator.set_threshold(std::numeric_limits<size_t>::max());
99 
100   Store(0, allocator.Allocate(Layout(kLargeInnerSize, 1)));
101   EXPECT_EQ(NextAfter(0), Fetch(1));
102   Store(4, allocator.Allocate(Layout(kSmallInnerSize, 1)));
103   EXPECT_EQ(NextAfter(3), Fetch(4));
104   EXPECT_EQ(NextAfter(4), Fetch(5));
105 }
106 
TEST_F(FirstFitAllocatorTest,AllocatesUsingThreshold)107 TEST_F(FirstFitAllocatorTest, AllocatesUsingThreshold) {
108   FirstFitAllocator& allocator = GetAllocator({
109       {kLargeOuterSize, Preallocation::kFree},
110       {kSmallerOuterSize, Preallocation::kUsed},
111       {kSmallOuterSize, Preallocation::kFree},
112       {Preallocation::kSizeRemaining, Preallocation::kUsed},
113       {kLargeOuterSize, Preallocation::kFree},
114       {kSmallerOuterSize, Preallocation::kUsed},
115       {kSmallOuterSize, Preallocation::kFree},
116   });
117   allocator.set_threshold(kThreshold);
118 
119   Store(0, allocator.Allocate(Layout(kLargeInnerSize, 1)));
120   EXPECT_EQ(NextAfter(0), Fetch(1));
121   Store(4, allocator.Allocate(Layout(kLargeInnerSize, 1)));
122   EXPECT_EQ(NextAfter(3), Fetch(4));
123   EXPECT_EQ(NextAfter(4), Fetch(5));
124   Store(6, allocator.Allocate(Layout(kSmallInnerSize, 1)));
125   EXPECT_EQ(NextAfter(5), Fetch(6));
126   EXPECT_EQ(NextAfter(6), Fetch(7));
127   Store(2, allocator.Allocate(Layout(kSmallInnerSize, 1)));
128   EXPECT_EQ(NextAfter(1), Fetch(2));
129   EXPECT_EQ(NextAfter(2), Fetch(3));
130 }
131 
TEST_F(FirstFitAllocatorTest,DeallocateNull)132 TEST_F(FirstFitAllocatorTest, DeallocateNull) { DeallocateNull(); }
133 
TEST_F(FirstFitAllocatorTest,DeallocateShuffled)134 TEST_F(FirstFitAllocatorTest, DeallocateShuffled) { DeallocateShuffled(); }
135 
TEST_F(FirstFitAllocatorTest,IterateOverBlocks)136 TEST_F(FirstFitAllocatorTest, IterateOverBlocks) { IterateOverBlocks(); }
137 
TEST_F(FirstFitAllocatorTest,ResizeNull)138 TEST_F(FirstFitAllocatorTest, ResizeNull) { ResizeNull(); }
139 
TEST_F(FirstFitAllocatorTest,ResizeLargeSame)140 TEST_F(FirstFitAllocatorTest, ResizeLargeSame) { ResizeLargeSame(); }
141 
TEST_F(FirstFitAllocatorTest,ResizeLargeSmaller)142 TEST_F(FirstFitAllocatorTest, ResizeLargeSmaller) { ResizeLargeSmaller(); }
143 
TEST_F(FirstFitAllocatorTest,ResizeLargeLarger)144 TEST_F(FirstFitAllocatorTest, ResizeLargeLarger) { ResizeLargeLarger(); }
145 
TEST_F(FirstFitAllocatorTest,ResizeLargeLargerFailure)146 TEST_F(FirstFitAllocatorTest, ResizeLargeLargerFailure) {
147   ResizeLargeLargerFailure();
148 }
149 
TEST_F(FirstFitAllocatorTest,ResizeSmallSame)150 TEST_F(FirstFitAllocatorTest, ResizeSmallSame) { ResizeSmallSame(); }
151 
TEST_F(FirstFitAllocatorTest,ResizeSmallSmaller)152 TEST_F(FirstFitAllocatorTest, ResizeSmallSmaller) { ResizeSmallSmaller(); }
153 
TEST_F(FirstFitAllocatorTest,ResizeSmallLarger)154 TEST_F(FirstFitAllocatorTest, ResizeSmallLarger) { ResizeSmallLarger(); }
155 
TEST_F(FirstFitAllocatorTest,ResizeSmallLargerFailure)156 TEST_F(FirstFitAllocatorTest, ResizeSmallLargerFailure) {
157   ResizeSmallLargerFailure();
158 }
159 
TEST_F(FirstFitAllocatorTest,ResizeLargeSmallerAcrossThreshold)160 TEST_F(FirstFitAllocatorTest, ResizeLargeSmallerAcrossThreshold) {
161   FirstFitAllocator& allocator = GetAllocator({
162       {kThreshold * 2, Preallocation::kUsed},
163       {Preallocation::kSizeRemaining, Preallocation::kFree},
164   });
165   allocator.set_threshold(kThreshold);
166 
167   // Shrinking succeeds, and the pointer is unchanged even though it is now
168   // below the threshold.
169   size_t new_size = kThreshold / 2;
170   ASSERT_TRUE(allocator.Resize(Fetch(0), new_size));
171   UseMemory(Fetch(0), kThreshold / 2);
172 }
173 
TEST_F(FirstFitAllocatorTest,ResizeSmallLargerAcrossThreshold)174 TEST_F(FirstFitAllocatorTest, ResizeSmallLargerAcrossThreshold) {
175   FirstFitAllocator& allocator = GetAllocator({
176       {Preallocation::kSizeRemaining, Preallocation::kUsed},
177       {kThreshold / 2, Preallocation::kUsed},
178       {kThreshold * 2, Preallocation::kFree},
179   });
180   allocator.set_threshold(kThreshold);
181 
182   // Growing succeeds, and the pointer is unchanged even though it is now
183   // above the threshold.
184   size_t new_size = kThreshold * 2;
185   ASSERT_TRUE(allocator.Resize(Fetch(1), new_size));
186   UseMemory(Fetch(1), kThreshold * 2);
187 }
188 
TEST_F(FirstFitAllocatorTest,MeasureFragmentation)189 TEST_F(FirstFitAllocatorTest, MeasureFragmentation) { MeasureFragmentation(); }
190 
TEST_F(FirstFitAllocatorTest,PoisonPeriodically)191 TEST_F(FirstFitAllocatorTest, PoisonPeriodically) { PoisonPeriodically(); }
192 
193 // TODO(b/376730645): Remove this test when the legacy alias is deprecated.
194 using DualFirstFitBlockAllocator =
195     ::pw::allocator::DualFirstFitBlockAllocator<uint16_t>;
196 class DualFirstFitBlockAllocatorTest
197     : public BlockAllocatorTest<DualFirstFitBlockAllocator> {
198  public:
DualFirstFitBlockAllocatorTest()199   DualFirstFitBlockAllocatorTest() : BlockAllocatorTest(allocator_) {}
200 
201  private:
202   DualFirstFitBlockAllocator allocator_;
203 };
TEST_F(DualFirstFitBlockAllocatorTest,AllocatesUsingThreshold)204 TEST_F(DualFirstFitBlockAllocatorTest, AllocatesUsingThreshold) {
205   auto& allocator = GetAllocator({
206       {kLargeOuterSize, Preallocation::kFree},
207       {kSmallerOuterSize, Preallocation::kUsed},
208       {kSmallOuterSize, Preallocation::kFree},
209       {Preallocation::kSizeRemaining, Preallocation::kUsed},
210       {kLargeOuterSize, Preallocation::kFree},
211       {kSmallerOuterSize, Preallocation::kUsed},
212       {kSmallOuterSize, Preallocation::kFree},
213   });
214   allocator.set_threshold(kThreshold);
215 
216   Store(0, allocator.Allocate(Layout(kLargeInnerSize, 1)));
217   EXPECT_EQ(NextAfter(0), Fetch(1));
218   Store(4, allocator.Allocate(Layout(kLargeInnerSize, 1)));
219   EXPECT_EQ(NextAfter(3), Fetch(4));
220   EXPECT_EQ(NextAfter(4), Fetch(5));
221   Store(6, allocator.Allocate(Layout(kSmallInnerSize, 1)));
222   EXPECT_EQ(NextAfter(5), Fetch(6));
223   EXPECT_EQ(NextAfter(6), Fetch(7));
224   Store(2, allocator.Allocate(Layout(kSmallInnerSize, 1)));
225   EXPECT_EQ(NextAfter(1), Fetch(2));
226   EXPECT_EQ(NextAfter(2), Fetch(3));
227 }
228 
229 // TODO(b/376730645): Remove this test when the legacy alias is deprecated.
230 using FirstFitBlockAllocator =
231     ::pw::allocator::FirstFitBlockAllocator<uint16_t>;
232 class FirstFitBlockAllocatorTest
233     : public BlockAllocatorTest<FirstFitBlockAllocator> {
234  public:
FirstFitBlockAllocatorTest()235   FirstFitBlockAllocatorTest() : BlockAllocatorTest(allocator_) {}
236 
237  private:
238   FirstFitBlockAllocator allocator_;
239 };
TEST_F(FirstFitBlockAllocatorTest,AllocatesFirstCompatible)240 TEST_F(FirstFitBlockAllocatorTest, AllocatesFirstCompatible) {
241   auto& allocator = GetAllocator({
242       {kSmallOuterSize, Preallocation::kFree},
243       {kSmallerOuterSize, Preallocation::kUsed},
244       {kSmallOuterSize, Preallocation::kFree},
245       {kSmallerOuterSize, Preallocation::kUsed},
246       {kLargeOuterSize, Preallocation::kFree},
247       {Preallocation::kSizeRemaining, Preallocation::kUsed},
248   });
249 
250   Store(0, allocator.Allocate(Layout(kSmallInnerSize, 1)));
251   EXPECT_EQ(NextAfter(0), Fetch(1));
252   Store(4, allocator.Allocate(Layout(kLargeInnerSize, 1)));
253   EXPECT_EQ(NextAfter(3), Fetch(4));
254   EXPECT_EQ(NextAfter(4), Fetch(5));
255 }
256 
257 // TODO(b/376730645): Remove this test when the legacy alias is deprecated.
258 using LastFitBlockAllocator = ::pw::allocator::LastFitBlockAllocator<uint16_t>;
259 class LastFitBlockAllocatorTest
260     : public BlockAllocatorTest<LastFitBlockAllocator> {
261  public:
LastFitBlockAllocatorTest()262   LastFitBlockAllocatorTest() : BlockAllocatorTest(allocator_) {}
263 
264  private:
265   LastFitBlockAllocator allocator_;
266 };
TEST_F(LastFitBlockAllocatorTest,AllocatesLastCompatible)267 TEST_F(LastFitBlockAllocatorTest, AllocatesLastCompatible) {
268   auto& allocator = GetAllocator({
269       {kLargeOuterSize, Preallocation::kFree},
270       {kSmallerOuterSize, Preallocation::kUsed},
271       {kSmallOuterSize, Preallocation::kFree},
272       {kSmallerOuterSize, Preallocation::kUsed},
273       {kSmallOuterSize, Preallocation::kFree},
274       {Preallocation::kSizeRemaining, Preallocation::kUsed},
275   });
276 
277   Store(0, allocator.Allocate(Layout(kLargeInnerSize, 1)));
278   EXPECT_EQ(NextAfter(0), Fetch(1));
279   Store(4, allocator.Allocate(Layout(kSmallInnerSize, 1)));
280   EXPECT_EQ(NextAfter(3), Fetch(4));
281   EXPECT_EQ(NextAfter(4), Fetch(5));
282 }
283 
284 }  // namespace
285