• 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/first_fit_block_allocator.h"
16 
17 #include "pw_allocator/block_allocator_testing.h"
18 #include "pw_allocator/buffer.h"
19 #include "pw_unit_test/framework.h"
20 
21 namespace {
22 
23 using ::pw::allocator::Layout;
24 using ::pw::allocator::test::Preallocation;
25 using FirstFitBlockAllocator =
26     ::pw::allocator::FirstFitBlockAllocator<uint16_t>;
27 using BlockAllocatorTest =
28     ::pw::allocator::test::BlockAllocatorTest<FirstFitBlockAllocator>;
29 
30 class FirstFitBlockAllocatorTest : public BlockAllocatorTest {
31  public:
FirstFitBlockAllocatorTest()32   FirstFitBlockAllocatorTest() : BlockAllocatorTest(allocator_) {}
33 
34  private:
35   FirstFitBlockAllocator allocator_;
36 };
37 
TEST_F(FirstFitBlockAllocatorTest,CanAutomaticallyInit)38 TEST_F(FirstFitBlockAllocatorTest, CanAutomaticallyInit) {
39   FirstFitBlockAllocator allocator(GetBytes());
40   CanAutomaticallyInit(allocator);
41 }
42 
TEST_F(FirstFitBlockAllocatorTest,CanExplicitlyInit)43 TEST_F(FirstFitBlockAllocatorTest, CanExplicitlyInit) {
44   FirstFitBlockAllocator allocator;
45   CanExplicitlyInit(allocator);
46 }
47 
TEST_F(FirstFitBlockAllocatorTest,GetCapacity)48 TEST_F(FirstFitBlockAllocatorTest, GetCapacity) { GetCapacity(); }
49 
TEST_F(FirstFitBlockAllocatorTest,AllocateLarge)50 TEST_F(FirstFitBlockAllocatorTest, AllocateLarge) { AllocateLarge(); }
51 
TEST_F(FirstFitBlockAllocatorTest,AllocateSmall)52 TEST_F(FirstFitBlockAllocatorTest, AllocateSmall) { AllocateSmall(); }
53 
TEST_F(FirstFitBlockAllocatorTest,AllocateLargeAlignment)54 TEST_F(FirstFitBlockAllocatorTest, AllocateLargeAlignment) {
55   AllocateLargeAlignment();
56 }
57 
TEST_F(FirstFitBlockAllocatorTest,AllocateAlignmentFailure)58 TEST_F(FirstFitBlockAllocatorTest, AllocateAlignmentFailure) {
59   AllocateAlignmentFailure();
60 }
61 
TEST_F(FirstFitBlockAllocatorTest,AllocatesFirstCompatible)62 TEST_F(FirstFitBlockAllocatorTest, AllocatesFirstCompatible) {
63   auto& allocator = GetAllocator({
64       {kSmallOuterSize, Preallocation::kIndexFree},
65       {kSmallerOuterSize, 1},
66       {kSmallOuterSize, Preallocation::kIndexFree},
67       {kSmallerOuterSize, 3},
68       {kLargeOuterSize, Preallocation::kIndexFree},
69       {Preallocation::kSizeRemaining, 5},
70   });
71 
72   Store(0, allocator.Allocate(Layout(kSmallInnerSize, 1)));
73   EXPECT_EQ(NextAfter(0), Fetch(1));
74   Store(4, allocator.Allocate(Layout(kLargeInnerSize, 1)));
75   EXPECT_EQ(NextAfter(3), Fetch(4));
76   EXPECT_EQ(NextAfter(4), Fetch(5));
77 }
78 
TEST_F(FirstFitBlockAllocatorTest,DeallocateNull)79 TEST_F(FirstFitBlockAllocatorTest, DeallocateNull) { DeallocateNull(); }
80 
TEST_F(FirstFitBlockAllocatorTest,DeallocateShuffled)81 TEST_F(FirstFitBlockAllocatorTest, DeallocateShuffled) { DeallocateShuffled(); }
82 
TEST_F(FirstFitBlockAllocatorTest,IterateOverBlocks)83 TEST_F(FirstFitBlockAllocatorTest, IterateOverBlocks) { IterateOverBlocks(); }
84 
TEST_F(FirstFitBlockAllocatorTest,ResizeNull)85 TEST_F(FirstFitBlockAllocatorTest, ResizeNull) { ResizeNull(); }
86 
TEST_F(FirstFitBlockAllocatorTest,ResizeLargeSame)87 TEST_F(FirstFitBlockAllocatorTest, ResizeLargeSame) { ResizeLargeSame(); }
88 
TEST_F(FirstFitBlockAllocatorTest,ResizeLargeSmaller)89 TEST_F(FirstFitBlockAllocatorTest, ResizeLargeSmaller) { ResizeLargeSmaller(); }
90 
TEST_F(FirstFitBlockAllocatorTest,ResizeLargeLarger)91 TEST_F(FirstFitBlockAllocatorTest, ResizeLargeLarger) { ResizeLargeLarger(); }
92 
TEST_F(FirstFitBlockAllocatorTest,ResizeLargeLargerFailure)93 TEST_F(FirstFitBlockAllocatorTest, ResizeLargeLargerFailure) {
94   ResizeLargeLargerFailure();
95 }
96 
TEST_F(FirstFitBlockAllocatorTest,ResizeSmallSame)97 TEST_F(FirstFitBlockAllocatorTest, ResizeSmallSame) { ResizeSmallSame(); }
98 
TEST_F(FirstFitBlockAllocatorTest,ResizeSmallSmaller)99 TEST_F(FirstFitBlockAllocatorTest, ResizeSmallSmaller) { ResizeSmallSmaller(); }
100 
TEST_F(FirstFitBlockAllocatorTest,ResizeSmallLarger)101 TEST_F(FirstFitBlockAllocatorTest, ResizeSmallLarger) { ResizeSmallLarger(); }
102 
TEST_F(FirstFitBlockAllocatorTest,ResizeSmallLargerFailure)103 TEST_F(FirstFitBlockAllocatorTest, ResizeSmallLargerFailure) {
104   ResizeSmallLargerFailure();
105 }
106 
TEST_F(FirstFitBlockAllocatorTest,CanMeasureFragmentation)107 TEST_F(FirstFitBlockAllocatorTest, CanMeasureFragmentation) {
108   CanMeasureFragmentation();
109 }
110 
TEST_F(FirstFitBlockAllocatorTest,DisablePoisoning)111 TEST_F(FirstFitBlockAllocatorTest, DisablePoisoning) {
112   auto& allocator = GetAllocator();
113   constexpr Layout layout = Layout::Of<std::byte[kSmallInnerSize]>();
114 
115   // Allocate 3 blocks to prevent the middle one from being merged when freed.
116   std::array<void*, 3> ptrs;
117   for (auto& ptr : ptrs) {
118     ptr = allocator.Allocate(layout);
119     ASSERT_NE(ptr, nullptr);
120   }
121 
122   // Modify the contents of the block and check if it is still valid.
123   auto* bytes = std::launder(reinterpret_cast<uint8_t*>(ptrs[1]));
124   auto* block = BlockType::FromUsableSpace(bytes);
125   allocator.Deallocate(bytes);
126   EXPECT_FALSE(block->Used());
127   EXPECT_TRUE(block->IsValid());
128   bytes[0] = ~bytes[0];
129   EXPECT_TRUE(block->IsValid());
130 
131   allocator.Deallocate(ptrs[0]);
132   allocator.Deallocate(ptrs[2]);
133 }
134 
TEST(PoisonedFirstFitBlockAllocatorTest,PoisonEveryFreeBlock)135 TEST(PoisonedFirstFitBlockAllocatorTest, PoisonEveryFreeBlock) {
136   using PoisonedFirstFitBlockAllocator =
137       ::pw::allocator::FirstFitBlockAllocator<uintptr_t, 1>;
138   using BlockType = PoisonedFirstFitBlockAllocator::BlockType;
139 
140   pw::allocator::WithBuffer<PoisonedFirstFitBlockAllocator,
141                             FirstFitBlockAllocatorTest::kCapacity>
142       allocator;
143   allocator->Init(allocator.as_bytes());
144   constexpr Layout layout =
145       Layout::Of<std::byte[FirstFitBlockAllocatorTest::kSmallInnerSize]>();
146 
147   // Allocate 3 blocks to prevent the middle one from being merged when freed.
148   std::array<void*, 3> ptrs;
149   for (auto& ptr : ptrs) {
150     ptr = allocator->Allocate(layout);
151     ASSERT_NE(ptr, nullptr);
152   }
153   // Modify the contents of the block and check if it is still valid.
154   auto* bytes = std::launder(reinterpret_cast<uint8_t*>(ptrs[1]));
155   auto* block = BlockType::FromUsableSpace(bytes);
156   allocator->Deallocate(bytes);
157   EXPECT_FALSE(block->Used());
158   EXPECT_TRUE(block->IsValid());
159   bytes[0] = ~bytes[0];
160   EXPECT_FALSE(block->IsValid());
161 
162   allocator->Deallocate(ptrs[0]);
163   allocator->Deallocate(ptrs[2]);
164 }
165 
TEST(PoisonedFirstFitBlockAllocatorTest,PoisonPeriodically)166 TEST(PoisonedFirstFitBlockAllocatorTest, PoisonPeriodically) {
167   using PoisonedFirstFitBlockAllocator =
168       ::pw::allocator::FirstFitBlockAllocator<uintptr_t, 4>;
169   using BlockType = PoisonedFirstFitBlockAllocator::BlockType;
170 
171   pw::allocator::WithBuffer<PoisonedFirstFitBlockAllocator,
172                             FirstFitBlockAllocatorTest::kCapacity>
173       allocator;
174   allocator->Init(allocator.as_bytes());
175   constexpr Layout layout =
176       Layout::Of<std::byte[FirstFitBlockAllocatorTest::kSmallInnerSize]>();
177 
178   // Allocate 9 blocks to prevent every other from being merged when freed.
179   std::array<void*, 9> ptrs;
180   for (auto& ptr : ptrs) {
181     ptr = allocator->Allocate(layout);
182     ASSERT_NE(ptr, nullptr);
183   }
184 
185   for (size_t i = 1; i < ptrs.size(); i += 2) {
186     auto* bytes = std::launder(reinterpret_cast<uint8_t*>(ptrs[i]));
187     auto* block = BlockType::FromUsableSpace(bytes);
188     allocator->Deallocate(bytes);
189     EXPECT_FALSE(block->Used());
190     EXPECT_TRUE(block->IsValid());
191     bytes[0] = ~bytes[0];
192 
193     // Corruption is only detected on the fourth freed block.
194     if (i == 7) {
195       EXPECT_FALSE(block->IsValid());
196     } else {
197       EXPECT_TRUE(block->IsValid());
198     }
199   }
200 
201   for (size_t i = 0; i < ptrs.size(); i += 2) {
202     allocator->Deallocate(ptrs[i]);
203   }
204 }
205 
206 }  // namespace
207