1 // Copyright 2020 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/detailed_block.h"
16
17 #include <array>
18 #include <cstdint>
19 #include <cstring>
20
21 #include "lib/stdcompat/bit.h"
22 #include "pw_allocator/block/testing.h"
23 #include "pw_assert/check.h"
24 #include "pw_assert/internal/check_impl.h"
25 #include "pw_bytes/alignment.h"
26 #include "pw_bytes/span.h"
27 #include "pw_span/span.h"
28 #include "pw_status/status.h"
29 #include "pw_unit_test/framework.h"
30
31 namespace {
32
33 // Test fixtures.
34 using ::pw::allocator::Layout;
35 using ::pw::allocator::test::GetAlignedOffsetAfter;
36 using ::pw::allocator::test::GetOuterSize;
37 using ::pw::allocator::test::Preallocate;
38 using ::pw::allocator::test::Preallocation;
39
40 using BlockResultPrev = pw::allocator::internal::GenericBlockResult::Prev;
41 using BlockResultNext = pw::allocator::internal::GenericBlockResult::Next;
42
43 // The size of the memory region used in tests.
44 constexpr size_t kN = 1024;
45
46 // The large alignment used in alignment-related tests.
47 constexpr size_t kAlign = 64;
48
49 template <typename BlockType>
50 class BlockTest : public ::testing::Test {
51 protected:
52 // Some tests below need a block with a nonzero inner size to fit within
53 // alignment boundaries.
54 static_assert(kAlign > BlockType::kBlockOverhead + BlockType::kAlignment);
55
56 alignas(BlockType::kAlignment) std::array<std::byte, kN> bytes_;
57 };
58
59 // A block type with more overhead.
60 using LargeOffsetBlock = ::pw::allocator::DetailedBlock<uint64_t>;
61
62 class LargeOffsetBlockTest : public BlockTest<LargeOffsetBlock> {
63 protected:
64 using BlockType = LargeOffsetBlock;
65 };
66
67 // A block type with less overhead.
68 using SmallOffsetBlock = ::pw::allocator::DetailedBlock<uint16_t>;
69
70 class SmallOffsetBlockTest : public BlockTest<SmallOffsetBlock> {
71 protected:
72 using BlockType = SmallOffsetBlock;
73 };
74
75 /// Returns the smallest offset into the given memory region which can be
76 /// preceded by a valid block, and at which a block would have properly aligned
77 /// usable space of the given size.
78 ///
79 /// @pre ``bytes`` must not be smaller than the calculated offset plus
80 /// ``layout.size()``.
81 template <typename BlockType>
GetFirstAlignedOffset(pw::ByteSpan bytes,Layout layout)82 size_t GetFirstAlignedOffset(pw::ByteSpan bytes, Layout layout) {
83 size_t min_block = BlockType::kBlockOverhead + 1;
84 size_t offset = GetAlignedOffsetAfter(
85 bytes.data(), layout.alignment(), min_block + BlockType::kBlockOverhead);
86 return min_block + offset;
87 }
88
89 /// Returns the largest offset into the given memory region at which a block
90 /// would have properly aligned usable space of the given size.
91 ///
92 /// @pre ``bytes`` must not be smaller than the calculated offset plus
93 /// ``layout.size()``.
94 template <typename BlockType>
GetLastAlignedOffset(pw::ByteSpan bytes,Layout layout)95 size_t GetLastAlignedOffset(pw::ByteSpan bytes, Layout layout) {
96 size_t min_offset = GetFirstAlignedOffset<BlockType>(bytes, layout);
97 return min_offset +
98 pw::AlignDown(bytes.subspan(min_offset).size() - layout.size(),
99 layout.alignment());
100 }
101
102 /// Iterates to each block reachable from the given one and asserts that it is
103 /// valid.
104 template <typename BlockType>
CheckAllReachableBlock(BlockType * block)105 void CheckAllReachableBlock(BlockType* block) {
106 // Rewind to start.
107 for (BlockType* prev = block->Prev(); prev != nullptr; prev = block->Prev()) {
108 block = prev;
109 }
110 for (; block != nullptr; block = block->Next()) {
111 ASSERT_TRUE(block->IsValid());
112 }
113 }
114
115 // Macro to provide type-parameterized tests for the various block types above.
116 //
117 // Ideally, the unit tests below could just use `TYPED_TEST_P` and its
118 // asscoiated macros from GoogleTest, see
119 // https://github.com/google/googletest/blob/main/docs/advanced.md#type-parameterized-tests
120 //
121 // These macros are not supported by the light framework however, so this macro
122 // provides a custom implementation that works just for these types.
123 #define TEST_FOR_EACH_BLOCK_TYPE(TestCase) \
124 template <typename BlockType> \
125 void TestCase(pw::ByteSpan bytes_); \
126 TEST_F(LargeOffsetBlockTest, TestCase) { \
127 TestCase<LargeOffsetBlock>(bytes_); \
128 } \
129 TEST_F(SmallOffsetBlockTest, TestCase) { \
130 TestCase<SmallOffsetBlock>(bytes_); \
131 } \
132 template <typename BlockType> \
133 void TestCase([[maybe_unused]] pw::ByteSpan bytes_)
134
135 // Unit tests.
136
TEST_FOR_EACH_BLOCK_TYPE(CanCreateSingleAlignedBlock)137 TEST_FOR_EACH_BLOCK_TYPE(CanCreateSingleAlignedBlock) {
138 auto result = BlockType::Init(bytes_);
139 ASSERT_EQ(result.status(), pw::OkStatus());
140 BlockType* block = *result;
141
142 EXPECT_EQ(block->OuterSize(), kN);
143 EXPECT_EQ(block->InnerSize(), kN - BlockType::kBlockOverhead);
144 EXPECT_EQ(block->Prev(), nullptr);
145 EXPECT_EQ(block->Next(), nullptr);
146 EXPECT_TRUE(block->IsFree());
147 EXPECT_EQ(block->Next(), nullptr);
148 CheckAllReachableBlock(block);
149 }
150
TEST_FOR_EACH_BLOCK_TYPE(CanCreateUnalignedSingleBlock)151 TEST_FOR_EACH_BLOCK_TYPE(CanCreateUnalignedSingleBlock) {
152 pw::ByteSpan aligned(bytes_);
153
154 auto result = BlockType::Init(aligned.subspan(1));
155 EXPECT_EQ(result.status(), pw::OkStatus());
156 }
157
TEST_FOR_EACH_BLOCK_TYPE(CannotCreateTooSmallBlock)158 TEST_FOR_EACH_BLOCK_TYPE(CannotCreateTooSmallBlock) {
159 std::array<std::byte, 2> bytes;
160 auto result = BlockType::Init(bytes);
161 EXPECT_EQ(result.status(), pw::Status::ResourceExhausted());
162 }
163
TEST(BlockTest,CannotCreateTooLargeBlock)164 TEST(BlockTest, CannotCreateTooLargeBlock) {
165 std::array<std::byte, kN> bytes;
166 auto result = pw::allocator::DetailedBlock<uint8_t>::Init(bytes);
167 EXPECT_EQ(result.status(), pw::Status::OutOfRange());
168 }
169
TEST_FOR_EACH_BLOCK_TYPE(CannotAllocFirst_Null)170 TEST_FOR_EACH_BLOCK_TYPE(CannotAllocFirst_Null) {
171 constexpr Layout kLayout(1, 1);
172
173 BlockType* block = nullptr;
174
175 auto result = BlockType::AllocFirst(std::move(block), kLayout);
176 EXPECT_EQ(result.status(), pw::Status::InvalidArgument());
177 EXPECT_EQ(result.block(), nullptr);
178 }
179
TEST_FOR_EACH_BLOCK_TYPE(CannotAllocFirst_ZeroSize)180 TEST_FOR_EACH_BLOCK_TYPE(CannotAllocFirst_ZeroSize) {
181 constexpr Layout kLayout(0, 1);
182
183 auto* block = Preallocate<BlockType>(
184 bytes_,
185 {
186 {Preallocation::kSizeRemaining, Preallocation::kFree},
187 });
188
189 auto result = BlockType::AllocFirst(std::move(block), kLayout);
190 EXPECT_EQ(result.status(), pw::Status::InvalidArgument());
191 CheckAllReachableBlock(result.block());
192 }
193
TEST_FOR_EACH_BLOCK_TYPE(CannotAllocFirst_Used)194 TEST_FOR_EACH_BLOCK_TYPE(CannotAllocFirst_Used) {
195 constexpr Layout kLayout(1, 1);
196
197 auto* block = Preallocate<BlockType>(
198 bytes_,
199 {
200 {Preallocation::kSizeRemaining, Preallocation::kUsed},
201 });
202
203 auto result = BlockType::AllocFirst(std::move(block), kLayout);
204 EXPECT_EQ(result.status(), pw::Status::FailedPrecondition());
205 CheckAllReachableBlock(result.block());
206 }
207
TEST_FOR_EACH_BLOCK_TYPE(CannotAllocFirst_TooSmall)208 TEST_FOR_EACH_BLOCK_TYPE(CannotAllocFirst_TooSmall) {
209 constexpr Layout kLayout(256, kAlign);
210
211 // Trim the buffer so that the layout does not fit.
212 pw::ByteSpan bytes = bytes_.subspan(
213 0, GetOuterSize<BlockType>(kLayout.size()) - BlockType::kAlignment);
214
215 auto* block = Preallocate<BlockType>(
216 bytes,
217 {
218 {Preallocation::kSizeRemaining, Preallocation::kFree},
219 });
220 auto result = BlockType::AllocFirst(std::move(block), kLayout);
221 EXPECT_EQ(result.status(), pw::Status::ResourceExhausted());
222 CheckAllReachableBlock(result.block());
223 }
224
TEST_FOR_EACH_BLOCK_TYPE(CanAllocFirst_Exact_FirstBlock)225 TEST_FOR_EACH_BLOCK_TYPE(CanAllocFirst_Exact_FirstBlock) {
226 constexpr Layout kLayout(256, kAlign);
227
228 // Trim the front of the buffer so that the first block is aligned.
229 size_t trim =
230 GetAlignedOffsetAfter(bytes_.data(), kAlign, BlockType::kBlockOverhead);
231 pw::ByteSpan bytes = bytes_.subspan(trim);
232
233 // Leave enough space free for the requested block.
234 size_t available = GetOuterSize<BlockType>(kLayout.size());
235 auto* block = Preallocate<BlockType>(
236 bytes,
237 {
238 {available, Preallocation::kFree},
239 {Preallocation::kSizeRemaining, Preallocation::kUsed},
240 });
241
242 // Allocate from the front of the block.
243 auto result = BlockType::AllocFirst(std::move(block), kLayout);
244 ASSERT_EQ(result.status(), pw::OkStatus());
245 EXPECT_EQ(result.prev(), BlockResultPrev::kUnchanged);
246 EXPECT_EQ(result.next(), BlockResultNext::kUnchanged);
247 block = result.block();
248
249 EXPECT_GE(block->InnerSize(), kLayout.size());
250 auto addr = cpp20::bit_cast<uintptr_t>(block->UsableSpace());
251 EXPECT_EQ(addr % kAlign, 0U);
252 EXPECT_FALSE(block->IsFree());
253 CheckAllReachableBlock(block);
254 }
255
TEST_FOR_EACH_BLOCK_TYPE(CanAllocFirst_Exact_SubsequentBlock)256 TEST_FOR_EACH_BLOCK_TYPE(CanAllocFirst_Exact_SubsequentBlock) {
257 constexpr Layout kLayout(256, kAlign);
258
259 // Preallocate a first block so that the next block is aligned.
260 size_t leading = GetFirstAlignedOffset<BlockType>(bytes_, kLayout);
261
262 // Leave enough space free for the requested block.
263 size_t available = GetOuterSize<BlockType>(kLayout.size());
264
265 auto* block = Preallocate<BlockType>(
266 bytes_,
267 {
268 {leading, Preallocation::kUsed},
269 {available, Preallocation::kFree},
270 {Preallocation::kSizeRemaining, Preallocation::kUsed},
271 });
272 block = block->Next();
273
274 // Allocate from the front of the block.
275 auto result = BlockType::AllocFirst(std::move(block), kLayout);
276 ASSERT_EQ(result.status(), pw::OkStatus());
277 EXPECT_EQ(result.prev(), BlockResultPrev::kUnchanged);
278 EXPECT_EQ(result.next(), BlockResultNext::kUnchanged);
279 block = result.block();
280
281 EXPECT_GE(block->InnerSize(), kLayout.size());
282 auto addr = cpp20::bit_cast<uintptr_t>(block->UsableSpace());
283 EXPECT_EQ(addr % kAlign, 0U);
284 EXPECT_FALSE(block->IsFree());
285 CheckAllReachableBlock(block);
286 }
287
TEST_FOR_EACH_BLOCK_TYPE(CanAllocFirst_NewNext_FirstBlock)288 TEST_FOR_EACH_BLOCK_TYPE(CanAllocFirst_NewNext_FirstBlock) {
289 constexpr Layout kLayout(256, kAlign);
290
291 // Trim the front of the buffer so that the first block is aligned.
292 size_t trim =
293 GetAlignedOffsetAfter(bytes_.data(), kAlign, BlockType::kBlockOverhead);
294 pw::ByteSpan bytes = bytes_.subspan(trim);
295
296 // Leave enough space free for the requested block and one more block.
297 size_t available =
298 GetOuterSize<BlockType>(kLayout.size()) + GetOuterSize<BlockType>(1);
299
300 auto* block = Preallocate<BlockType>(
301 bytes,
302 {
303 {available, Preallocation::kFree},
304 {Preallocation::kSizeRemaining, Preallocation::kUsed},
305 });
306
307 // Allocate from the front of the block.
308 auto result = BlockType::AllocFirst(std::move(block), kLayout);
309 ASSERT_EQ(result.status(), pw::OkStatus());
310 EXPECT_EQ(result.prev(), BlockResultPrev::kUnchanged);
311 EXPECT_EQ(result.next(), BlockResultNext::kSplitNew);
312 block = result.block();
313
314 EXPECT_GE(block->InnerSize(), kLayout.size());
315 auto addr = cpp20::bit_cast<uintptr_t>(block->UsableSpace());
316 EXPECT_EQ(addr % kAlign, 0U);
317 EXPECT_FALSE(block->IsFree());
318 CheckAllReachableBlock(block);
319 }
320
TEST_FOR_EACH_BLOCK_TYPE(CanAllocFirst_NewNext_SubsequentBlock)321 TEST_FOR_EACH_BLOCK_TYPE(CanAllocFirst_NewNext_SubsequentBlock) {
322 constexpr Layout kLayout(256, kAlign);
323
324 // Preallocate a first block so that the next block is aligned.
325 size_t leading = GetFirstAlignedOffset<BlockType>(bytes_, kLayout);
326
327 // Leave enough space free for the requested block and one more block.
328 size_t available =
329 GetOuterSize<BlockType>(kLayout.size()) + GetOuterSize<BlockType>(1);
330
331 auto* block = Preallocate<BlockType>(
332 bytes_,
333 {
334 {leading, Preallocation::kUsed},
335 {available, Preallocation::kFree},
336 {Preallocation::kSizeRemaining, Preallocation::kUsed},
337 });
338 block = block->Next();
339
340 // Allocate from the front of the block.
341 auto result = BlockType::AllocFirst(std::move(block), kLayout);
342 ASSERT_EQ(result.status(), pw::OkStatus());
343 EXPECT_EQ(result.prev(), BlockResultPrev::kUnchanged);
344 EXPECT_EQ(result.next(), BlockResultNext::kSplitNew);
345 block = result.block();
346
347 EXPECT_GE(block->InnerSize(), kLayout.size());
348 auto addr = cpp20::bit_cast<uintptr_t>(block->UsableSpace());
349 EXPECT_EQ(addr % kAlign, 0U);
350 EXPECT_FALSE(block->IsFree());
351 CheckAllReachableBlock(block);
352 }
353
TEST_FOR_EACH_BLOCK_TYPE(CanAllocFirst_NewPrev_FirstBlock)354 TEST_FOR_EACH_BLOCK_TYPE(CanAllocFirst_NewPrev_FirstBlock) {
355 constexpr Layout kLayout(256, kAlign);
356
357 // Trim the front of the buffer so that there is room for a block before the
358 // first alignment boundary.
359 size_t trim =
360 GetAlignedOffsetAfter(bytes_.data(), kAlign, BlockType::kBlockOverhead) +
361 kAlign - GetOuterSize<BlockType>(1);
362 pw::ByteSpan bytes = bytes_.subspan(trim);
363
364 // Leave enough space free for a block and the requested block.
365 size_t available =
366 GetOuterSize<BlockType>(1) + GetOuterSize<BlockType>(kLayout.size());
367
368 auto* block = Preallocate<BlockType>(
369 bytes,
370 {
371 {available, Preallocation::kFree},
372 {Preallocation::kSizeRemaining, Preallocation::kUsed},
373 });
374
375 // Allocate from the front of the block.
376 auto result = BlockType::AllocFirst(std::move(block), kLayout);
377 ASSERT_EQ(result.status(), pw::OkStatus());
378 EXPECT_EQ(result.prev(), BlockResultPrev::kSplitNew);
379 EXPECT_EQ(result.next(), BlockResultNext::kUnchanged);
380 block = result.block();
381
382 EXPECT_GE(block->InnerSize(), kLayout.size());
383 auto addr = cpp20::bit_cast<uintptr_t>(block->UsableSpace());
384 EXPECT_EQ(addr % kAlign, 0U);
385 EXPECT_FALSE(block->IsFree());
386 CheckAllReachableBlock(block);
387 }
388
TEST_FOR_EACH_BLOCK_TYPE(CanAllocFirst_NewPrev_SubsequentBlock)389 TEST_FOR_EACH_BLOCK_TYPE(CanAllocFirst_NewPrev_SubsequentBlock) {
390 constexpr Layout kLayout(256, kAlign);
391
392 // Preallocate a first block with room for another block before the next
393 // alignment boundary.
394 size_t leading = GetFirstAlignedOffset<BlockType>(bytes_, kLayout) + kAlign -
395 GetOuterSize<BlockType>(1);
396
397 // Leave enough space free for a block and the requested block.
398 size_t available =
399 GetOuterSize<BlockType>(1) + GetOuterSize<BlockType>(kLayout.size());
400
401 auto* block = Preallocate<BlockType>(
402 bytes_,
403 {
404 {leading, Preallocation::kUsed},
405 {available, Preallocation::kFree},
406 {Preallocation::kSizeRemaining, Preallocation::kUsed},
407 });
408 block = block->Next();
409
410 // Allocate from the front of the block.
411 auto result = BlockType::AllocFirst(std::move(block), kLayout);
412 ASSERT_EQ(result.status(), pw::OkStatus());
413 EXPECT_EQ(result.prev(), BlockResultPrev::kSplitNew);
414 EXPECT_EQ(result.next(), BlockResultNext::kUnchanged);
415 block = result.block();
416
417 EXPECT_GE(block->InnerSize(), kLayout.size());
418 auto addr = cpp20::bit_cast<uintptr_t>(block->UsableSpace());
419 EXPECT_EQ(addr % kAlign, 0U);
420 EXPECT_FALSE(block->IsFree());
421 CheckAllReachableBlock(block);
422 }
423
TEST_FOR_EACH_BLOCK_TYPE(CanAllocFirst_NewPrevAndNewNext_FirstBlock)424 TEST_FOR_EACH_BLOCK_TYPE(CanAllocFirst_NewPrevAndNewNext_FirstBlock) {
425 constexpr Layout kLayout(256, kAlign);
426
427 // Trim the front of the buffer so that there is room for a block before the
428 // first alignment boundary.
429 size_t trim =
430 GetAlignedOffsetAfter(bytes_.data(), kAlign, BlockType::kBlockOverhead) +
431 kAlign - GetOuterSize<BlockType>(1);
432 pw::ByteSpan bytes = bytes_.subspan(trim);
433
434 // Leave enough space free for a block, the requested block, and one more
435 // block.
436 size_t available = GetOuterSize<BlockType>(1) +
437 GetOuterSize<BlockType>(kLayout.size()) +
438 GetOuterSize<BlockType>(1);
439
440 auto* block = Preallocate<BlockType>(
441 bytes,
442 {
443 {available, Preallocation::kFree},
444 {Preallocation::kSizeRemaining, Preallocation::kUsed},
445 });
446
447 // Allocate from the front of the block.
448 auto result = BlockType::AllocFirst(std::move(block), kLayout);
449 ASSERT_EQ(result.status(), pw::OkStatus());
450 EXPECT_EQ(result.prev(), BlockResultPrev::kSplitNew);
451 EXPECT_EQ(result.next(), BlockResultNext::kSplitNew);
452 block = result.block();
453
454 EXPECT_GE(block->InnerSize(), kLayout.size());
455 auto addr = cpp20::bit_cast<uintptr_t>(block->UsableSpace());
456 EXPECT_EQ(addr % kAlign, 0U);
457 EXPECT_FALSE(block->IsFree());
458 CheckAllReachableBlock(block);
459 }
460
TEST_FOR_EACH_BLOCK_TYPE(CanAllocFirst_NewPrevAndNewNext_SubsequentBlock)461 TEST_FOR_EACH_BLOCK_TYPE(CanAllocFirst_NewPrevAndNewNext_SubsequentBlock) {
462 constexpr Layout kLayout(256, kAlign);
463
464 // Preallocate a first block with room for another block before the next
465 // alignment boundary.
466 size_t leading = GetFirstAlignedOffset<BlockType>(bytes_, kLayout) + kAlign -
467 GetOuterSize<BlockType>(1);
468
469 // Leave enough space free for a block and the requested block and one more
470 // block.
471 size_t available = kAlign + GetOuterSize<BlockType>(kLayout.size());
472
473 auto* block = Preallocate<BlockType>(
474 bytes_,
475 {
476 {leading, Preallocation::kUsed},
477 {available, Preallocation::kFree},
478 {Preallocation::kSizeRemaining, Preallocation::kUsed},
479 });
480 block = block->Next();
481
482 auto result = BlockType::AllocFirst(std::move(block), kLayout);
483 ASSERT_EQ(result.status(), pw::OkStatus());
484 EXPECT_EQ(result.prev(), BlockResultPrev::kSplitNew);
485 EXPECT_EQ(result.next(), BlockResultNext::kSplitNew);
486 block = result.block();
487
488 EXPECT_GE(block->InnerSize(), kLayout.size());
489 auto addr = cpp20::bit_cast<uintptr_t>(block->UsableSpace());
490 EXPECT_EQ(addr % kAlign, 0U);
491 EXPECT_FALSE(block->IsFree());
492 CheckAllReachableBlock(block);
493 }
494
TEST_FOR_EACH_BLOCK_TYPE(CannotAllocFirst_ShiftToPrev_FirstBlock)495 TEST_FOR_EACH_BLOCK_TYPE(CannotAllocFirst_ShiftToPrev_FirstBlock) {
496 constexpr Layout kLayout(256, kAlign);
497
498 // Trim the front of the buffer so that there is `kAlignment` bytes before
499 // where the aligned block would start.
500 size_t trim =
501 GetAlignedOffsetAfter(bytes_.data(), kAlign, BlockType::kBlockOverhead) +
502 kAlign - BlockType::kAlignment;
503 pw::ByteSpan bytes = bytes_.subspan(trim);
504
505 // Leave enough space free for the `kAlignment` bytes and the requested block.
506 size_t available =
507 BlockType::kAlignment + GetOuterSize<BlockType>(kLayout.size());
508
509 auto* block = Preallocate<BlockType>(
510 bytes,
511 {
512 {available, Preallocation::kFree},
513 {Preallocation::kSizeRemaining, Preallocation::kUsed},
514 });
515
516 // Attempt and fail to allocate from the front of the block.
517 auto result = BlockType::AllocFirst(std::move(block), kLayout);
518 EXPECT_EQ(result.status(), pw::Status::ResourceExhausted());
519 CheckAllReachableBlock(result.block());
520 }
521
TEST_FOR_EACH_BLOCK_TYPE(CanAllocFirst_ShiftToPrev_SubsequentBlock)522 TEST_FOR_EACH_BLOCK_TYPE(CanAllocFirst_ShiftToPrev_SubsequentBlock) {
523 constexpr Layout kLayout(256, kAlign);
524
525 // Preallocate a first block so that there is `kAlignment` bytes before
526 // where the aligned block would start.
527 size_t leading = GetFirstAlignedOffset<BlockType>(bytes_, kLayout) + kAlign -
528 BlockType::kAlignment;
529
530 // Leave enough space free for the `kAlignment` bytes and the requested block.
531 size_t available =
532 BlockType::kAlignment + GetOuterSize<BlockType>(kLayout.size());
533
534 auto* first = Preallocate<BlockType>(
535 bytes_,
536 {
537 {leading, Preallocation::kUsed},
538 {available, Preallocation::kFree},
539 {Preallocation::kSizeRemaining, Preallocation::kUsed},
540 });
541 BlockType* block = first->Next();
542
543 // Allocate from the front of the block.
544 auto result = BlockType::AllocFirst(std::move(block), kLayout);
545 ASSERT_EQ(result.status(), pw::OkStatus());
546 EXPECT_EQ(result.prev(), BlockResultPrev::kResizedLarger);
547 EXPECT_EQ(result.next(), BlockResultNext::kUnchanged);
548 block = result.block();
549
550 // Verify the previous block was padded.
551 size_t old_requested_size = leading - BlockType::kBlockOverhead;
552 auto old_layout = first->RequestedLayout();
553 EXPECT_EQ(old_layout->size(), old_requested_size);
554
555 // Resize the first block, and verify the padding is updated.
556 size_t new_requested_size = old_requested_size + 1;
557 result = first->Resize(new_requested_size);
558 ASSERT_EQ(result.status(), pw::OkStatus());
559 EXPECT_EQ(result.prev(), BlockResultPrev::kUnchanged);
560 EXPECT_EQ(result.next(), BlockResultNext::kUnchanged);
561 auto new_layout = first->RequestedLayout();
562 EXPECT_EQ(new_layout->size(), new_requested_size);
563
564 EXPECT_GE(block->InnerSize(), kLayout.size());
565 auto addr = cpp20::bit_cast<uintptr_t>(block->UsableSpace());
566 EXPECT_EQ(addr % kAlign, 0U);
567 EXPECT_FALSE(block->IsFree());
568
569 // Verify that freeing the subsequent block does not reclaim bytes that were
570 // resized.
571 result = BlockType::Free(std::move(block));
572 ASSERT_EQ(result.status(), pw::OkStatus());
573 EXPECT_EQ(result.prev(), BlockResultPrev::kUnchanged);
574 EXPECT_EQ(result.next(), BlockResultNext::kUnchanged);
575 CheckAllReachableBlock(first);
576 }
577
TEST_FOR_EACH_BLOCK_TYPE(CannotAllocFirst_ShiftToPrevAndNewNext_FirstBlock)578 TEST_FOR_EACH_BLOCK_TYPE(CannotAllocFirst_ShiftToPrevAndNewNext_FirstBlock) {
579 constexpr Layout kLayout(256, kAlign);
580
581 // Trim the front of the buffer so that there is `kAlignment` bytes before
582 // where the aligned block would start.
583 size_t trim =
584 GetAlignedOffsetAfter(bytes_.data(), kAlign, BlockType::kBlockOverhead) +
585 kAlign - BlockType::kAlignment;
586 pw::ByteSpan bytes = bytes_.subspan(trim);
587
588 // Leave enough space free for the `kAlignment` bytes, the requested block,
589 // and one more block.
590 size_t available = BlockType::kAlignment +
591 GetOuterSize<BlockType>(kLayout.size()) +
592 GetOuterSize<BlockType>(1);
593
594 auto* block = Preallocate<BlockType>(
595 bytes,
596 {
597 {available, Preallocation::kFree},
598 {Preallocation::kSizeRemaining, Preallocation::kUsed},
599 });
600
601 // Attempt and fail to allocate from the front of the block.
602 auto result = BlockType::AllocFirst(std::move(block), kLayout);
603 EXPECT_EQ(result.status(), pw::Status::ResourceExhausted());
604 CheckAllReachableBlock(result.block());
605 }
606
TEST_FOR_EACH_BLOCK_TYPE(CanAllocFirst_ShiftToPrevAndNewNext_SubsequentBlock)607 TEST_FOR_EACH_BLOCK_TYPE(CanAllocFirst_ShiftToPrevAndNewNext_SubsequentBlock) {
608 constexpr Layout kLayout(256, kAlign);
609
610 // Preallocate a first block so that there is `kAlignment` bytes before
611 // where the aligned block would start.
612 size_t leading = GetFirstAlignedOffset<BlockType>(bytes_, kLayout) + kAlign -
613 BlockType::kAlignment;
614
615 // Leave enough space free for the `kAlignment` bytes, the requested block,
616 // and one more block.
617 size_t available = BlockType::kAlignment +
618 GetOuterSize<BlockType>(kLayout.size()) +
619 GetOuterSize<BlockType>(1);
620
621 auto* block = Preallocate<BlockType>(
622 bytes_,
623 {
624 {leading, Preallocation::kUsed},
625 {available, Preallocation::kFree},
626 {Preallocation::kSizeRemaining, Preallocation::kUsed},
627 });
628 block = block->Next();
629
630 // Allocate from the front of the block.
631 auto result = BlockType::AllocFirst(std::move(block), kLayout);
632 ASSERT_EQ(result.status(), pw::OkStatus());
633 EXPECT_EQ(result.prev(), BlockResultPrev::kResizedLarger);
634 EXPECT_EQ(result.next(), BlockResultNext::kSplitNew);
635 block = result.block();
636
637 EXPECT_GE(block->InnerSize(), kLayout.size());
638 auto addr = cpp20::bit_cast<uintptr_t>(block->UsableSpace());
639 EXPECT_EQ(addr % kAlign, 0U);
640 EXPECT_FALSE(block->IsFree());
641 CheckAllReachableBlock(block);
642 }
643
TEST_FOR_EACH_BLOCK_TYPE(CannotAllocLast_Null)644 TEST_FOR_EACH_BLOCK_TYPE(CannotAllocLast_Null) {
645 constexpr Layout kLayout(1, 1);
646
647 BlockType* block = nullptr;
648
649 // Attempt and fail to allocate from the front of the block.
650 auto result = BlockType::AllocLast(std::move(block), kLayout);
651 EXPECT_EQ(result.status(), pw::Status::InvalidArgument());
652 EXPECT_EQ(result.block(), nullptr);
653 }
654
TEST_FOR_EACH_BLOCK_TYPE(CannotAllocLast_ZeroSize)655 TEST_FOR_EACH_BLOCK_TYPE(CannotAllocLast_ZeroSize) {
656 constexpr Layout kLayout(0, 1);
657
658 auto* block = Preallocate<BlockType>(
659 bytes_,
660 {
661 {Preallocation::kSizeRemaining, Preallocation::kFree},
662 });
663
664 // Check if we expect this to succeed.
665 auto can_alloc_last = block->CanAlloc(kLayout);
666 EXPECT_EQ(can_alloc_last.status(), pw::Status::InvalidArgument());
667
668 // Attempt and fail to allocate from the front of the block.
669 auto result = BlockType::AllocLast(std::move(block), kLayout);
670 EXPECT_EQ(result.status(), pw::Status::InvalidArgument());
671 CheckAllReachableBlock(result.block());
672 }
673
TEST_FOR_EACH_BLOCK_TYPE(CannotAllocLast_Used)674 TEST_FOR_EACH_BLOCK_TYPE(CannotAllocLast_Used) {
675 constexpr Layout kLayout(1, 1);
676
677 auto* block = Preallocate<BlockType>(
678 bytes_,
679 {
680 {Preallocation::kSizeRemaining, Preallocation::kUsed},
681 });
682
683 // Check if we expect this to succeed.
684 auto can_alloc_last = block->CanAlloc(kLayout);
685 EXPECT_EQ(can_alloc_last.status(), pw::Status::FailedPrecondition());
686
687 // Attempt and fail to allocate from the front of the block.
688 auto result = BlockType::AllocLast(std::move(block), kLayout);
689 EXPECT_EQ(result.status(), pw::Status::FailedPrecondition());
690 CheckAllReachableBlock(result.block());
691 }
692
TEST_FOR_EACH_BLOCK_TYPE(CannotAllocLast_TooSmall)693 TEST_FOR_EACH_BLOCK_TYPE(CannotAllocLast_TooSmall) {
694 constexpr Layout kLayout(256, kAlign);
695
696 // Trim the buffer so that the layout does not fit.
697 pw::ByteSpan bytes = bytes_.subspan(
698 0, GetOuterSize<BlockType>(kLayout.size()) - BlockType::kAlignment);
699
700 auto* block = Preallocate<BlockType>(
701 bytes,
702 {
703 {Preallocation::kSizeRemaining, Preallocation::kFree},
704 });
705
706 // Check if we expect this to succeed.
707 auto can_alloc_last = block->CanAlloc(kLayout);
708 EXPECT_EQ(can_alloc_last.status(), pw::Status::ResourceExhausted());
709
710 // Attempt and fail to allocate from the front of the block.
711 auto result = BlockType::AllocLast(std::move(block), kLayout);
712 EXPECT_EQ(result.status(), pw::Status::ResourceExhausted());
713 CheckAllReachableBlock(result.block());
714 }
715
TEST_FOR_EACH_BLOCK_TYPE(CanAllocLast_Exact_FirstBlock)716 TEST_FOR_EACH_BLOCK_TYPE(CanAllocLast_Exact_FirstBlock) {
717 constexpr Layout kLayout(256, kAlign);
718
719 // Trim the front of the buffer so that the first block is aligned.
720 size_t trim =
721 GetAlignedOffsetAfter(bytes_.data(), kAlign, BlockType::kBlockOverhead);
722 pw::ByteSpan bytes = bytes_.subspan(trim);
723
724 // Leave enough space free for the requested block.
725 size_t available = GetOuterSize<BlockType>(kLayout.size());
726
727 auto* block = Preallocate<BlockType>(
728 bytes,
729 {
730 {available, Preallocation::kFree},
731 {Preallocation::kSizeRemaining, Preallocation::kUsed},
732 });
733
734 // Check if we expect this to succeed.
735 auto can_alloc_last = block->CanAlloc(kLayout);
736 ASSERT_EQ(can_alloc_last.status(), pw::OkStatus());
737 EXPECT_EQ(can_alloc_last.size(), 0U);
738
739 // Allocate from the back of the block.
740 auto result = BlockType::AllocLast(std::move(block), kLayout);
741 ASSERT_EQ(result.status(), pw::OkStatus());
742 EXPECT_EQ(result.prev(), BlockResultPrev::kUnchanged);
743 EXPECT_EQ(result.next(), BlockResultNext::kUnchanged);
744 block = result.block();
745
746 EXPECT_GE(block->InnerSize(), kLayout.size());
747 auto addr = cpp20::bit_cast<uintptr_t>(block->UsableSpace());
748 EXPECT_EQ(addr % kAlign, 0U);
749 EXPECT_FALSE(block->IsFree());
750 CheckAllReachableBlock(block);
751 }
752
TEST_FOR_EACH_BLOCK_TYPE(CanAllocLast_Exact_SubsequentBlock)753 TEST_FOR_EACH_BLOCK_TYPE(CanAllocLast_Exact_SubsequentBlock) {
754 constexpr Layout kLayout(256, kAlign);
755
756 // Preallocate a first block so that the next block is aligned.
757 size_t leading = GetFirstAlignedOffset<BlockType>(bytes_, kLayout);
758
759 // Leave enough space free for the requested block.
760 size_t available = GetOuterSize<BlockType>(kLayout.size());
761
762 auto* block = Preallocate<BlockType>(
763 bytes_,
764 {
765 {leading, Preallocation::kUsed},
766 {available, Preallocation::kFree},
767 {Preallocation::kSizeRemaining, Preallocation::kUsed},
768 });
769 block = block->Next();
770
771 // Check if we expect this to succeed.
772 auto can_alloc_last = block->CanAlloc(kLayout);
773 ASSERT_EQ(can_alloc_last.status(), pw::OkStatus());
774 EXPECT_EQ(can_alloc_last.size(), 0U);
775
776 // Allocate from the back of the block.
777 auto result = BlockType::AllocLast(std::move(block), kLayout);
778 ASSERT_EQ(result.status(), pw::OkStatus());
779 EXPECT_EQ(result.prev(), BlockResultPrev::kUnchanged);
780 EXPECT_EQ(result.next(), BlockResultNext::kUnchanged);
781 block = result.block();
782
783 EXPECT_GE(block->InnerSize(), kLayout.size());
784 auto addr = cpp20::bit_cast<uintptr_t>(block->UsableSpace());
785 EXPECT_EQ(addr % kAlign, 0U);
786 EXPECT_FALSE(block->IsFree());
787 CheckAllReachableBlock(block);
788 }
789
TEST_FOR_EACH_BLOCK_TYPE(CanAllocLast_NewPrev_FirstBlock)790 TEST_FOR_EACH_BLOCK_TYPE(CanAllocLast_NewPrev_FirstBlock) {
791 constexpr Layout kLayout(256, kAlign);
792
793 // Trim the front of the buffer so that there is room for a block before the
794 // first alignment boundary.
795 size_t trim =
796 GetAlignedOffsetAfter(bytes_.data(), kAlign, BlockType::kBlockOverhead) +
797 kAlign - GetOuterSize<BlockType>(1);
798 pw::ByteSpan bytes = bytes_.subspan(trim);
799
800 // Leave enough space free for a block and the requested block.
801 size_t available =
802 GetOuterSize<BlockType>(1) + GetOuterSize<BlockType>(kLayout.size());
803
804 auto* block = Preallocate<BlockType>(
805 bytes,
806 {
807 {available, Preallocation::kFree},
808 {Preallocation::kSizeRemaining, Preallocation::kUsed},
809 });
810
811 // Check if we expect this to succeed.
812 auto can_alloc_last = block->CanAlloc(kLayout);
813 ASSERT_EQ(can_alloc_last.status(), pw::OkStatus());
814 EXPECT_EQ(can_alloc_last.size(), GetOuterSize<BlockType>(1));
815
816 // Allocate from the back of the block.
817 auto result = BlockType::AllocLast(std::move(block), kLayout);
818 ASSERT_EQ(result.status(), pw::OkStatus());
819 EXPECT_EQ(result.prev(), BlockResultPrev::kSplitNew);
820 EXPECT_EQ(result.next(), BlockResultNext::kUnchanged);
821 block = result.block();
822
823 EXPECT_GE(block->InnerSize(), kLayout.size());
824 auto addr = cpp20::bit_cast<uintptr_t>(block->UsableSpace());
825 EXPECT_EQ(addr % kAlign, 0U);
826 EXPECT_FALSE(block->IsFree());
827 CheckAllReachableBlock(block);
828 }
829
TEST_FOR_EACH_BLOCK_TYPE(CanAllocLast_NewPrev_SubsequentBlock)830 TEST_FOR_EACH_BLOCK_TYPE(CanAllocLast_NewPrev_SubsequentBlock) {
831 constexpr Layout kLayout(256, kAlign);
832
833 // Preallocate a first block with room for another block before the next
834 // alignment boundary.
835 size_t leading = GetFirstAlignedOffset<BlockType>(bytes_, kLayout) + kAlign -
836 GetOuterSize<BlockType>(1);
837
838 // Leave enough space free for a block and the requested block.
839 size_t available =
840 GetOuterSize<BlockType>(1) + GetOuterSize<BlockType>(kLayout.size());
841
842 auto* block = Preallocate<BlockType>(
843 bytes_,
844 {
845 {leading, Preallocation::kUsed},
846 {available, Preallocation::kFree},
847 {Preallocation::kSizeRemaining, Preallocation::kUsed},
848 });
849 block = block->Next();
850
851 // Check if we expect this to succeed.
852 auto can_alloc_last = block->CanAlloc(kLayout);
853 ASSERT_EQ(can_alloc_last.status(), pw::OkStatus());
854 EXPECT_EQ(can_alloc_last.size(), GetOuterSize<BlockType>(1));
855
856 // Allocate from the back of the block.
857 auto result = BlockType::AllocLast(std::move(block), kLayout);
858 ASSERT_EQ(result.status(), pw::OkStatus());
859 EXPECT_EQ(result.prev(), BlockResultPrev::kSplitNew);
860 EXPECT_EQ(result.next(), BlockResultNext::kUnchanged);
861 block = result.block();
862
863 EXPECT_GE(block->InnerSize(), kLayout.size());
864 auto addr = cpp20::bit_cast<uintptr_t>(block->UsableSpace());
865 EXPECT_EQ(addr % kAlign, 0U);
866 EXPECT_FALSE(block->IsFree());
867 CheckAllReachableBlock(block);
868 }
869
TEST_FOR_EACH_BLOCK_TYPE(CannotAllocLast_ShiftToPrev_FirstBlock)870 TEST_FOR_EACH_BLOCK_TYPE(CannotAllocLast_ShiftToPrev_FirstBlock) {
871 constexpr Layout kLayout(256, kAlign);
872
873 // Trim the front of the buffer so that there is `kAlignment` bytes before
874 // where the aligned block would start.
875 size_t trim =
876 GetAlignedOffsetAfter(bytes_.data(), kAlign, BlockType::kBlockOverhead) +
877 kAlign - BlockType::kAlignment;
878 pw::ByteSpan bytes = bytes_.subspan(trim);
879
880 // Leave enough space free for the `kAlignment` bytes and the requested block.
881 size_t available =
882 BlockType::kAlignment + GetOuterSize<BlockType>(kLayout.size());
883
884 auto* block = Preallocate<BlockType>(
885 bytes,
886 {
887 {available, Preallocation::kFree},
888 {Preallocation::kSizeRemaining, Preallocation::kUsed},
889 });
890
891 // Check if we expect this to succeed.
892 auto can_alloc_last = block->CanAlloc(kLayout);
893 EXPECT_EQ(can_alloc_last.status(), pw::Status::ResourceExhausted());
894
895 // Attempt and fail to allocate from the back of the block.
896 auto result = BlockType::AllocFirst(std::move(block), kLayout);
897 EXPECT_EQ(result.status(), pw::Status::ResourceExhausted());
898 CheckAllReachableBlock(result.block());
899 }
900
TEST_FOR_EACH_BLOCK_TYPE(CanAllocLast_ShiftToPrev_SubsequentBlock)901 TEST_FOR_EACH_BLOCK_TYPE(CanAllocLast_ShiftToPrev_SubsequentBlock) {
902 constexpr Layout kLayout(256, kAlign);
903
904 // Preallocate a first block so that there is `kAlignment` bytes before
905 // where the aligned block would start.
906 size_t leading = GetFirstAlignedOffset<BlockType>(bytes_, kLayout) + kAlign -
907 BlockType::kAlignment;
908
909 // Leave enough space free for the `kAlignment` bytes and the requested block.
910 size_t available =
911 BlockType::kAlignment + GetOuterSize<BlockType>(kLayout.size());
912
913 auto* block = Preallocate<BlockType>(
914 bytes_,
915 {
916 {leading, Preallocation::kUsed},
917 {available, Preallocation::kFree},
918 {Preallocation::kSizeRemaining, Preallocation::kUsed},
919 });
920 block = block->Next();
921
922 // Check if we expect this to succeed.
923 auto can_alloc_last = block->CanAlloc(kLayout);
924 ASSERT_EQ(can_alloc_last.status(), pw::OkStatus());
925 EXPECT_EQ(can_alloc_last.size(), BlockType::kAlignment);
926
927 // Allocate from the back of the block.
928 auto result = BlockType::AllocLast(std::move(block), kLayout);
929 ASSERT_EQ(result.status(), pw::OkStatus());
930 EXPECT_EQ(result.prev(), BlockResultPrev::kResizedLarger);
931 EXPECT_EQ(result.next(), BlockResultNext::kUnchanged);
932 EXPECT_EQ(result.size(), BlockType::kAlignment);
933 block = result.block();
934
935 EXPECT_GE(block->InnerSize(), kLayout.size());
936 auto addr = cpp20::bit_cast<uintptr_t>(block->UsableSpace());
937 EXPECT_EQ(addr % kAlign, 0U);
938 EXPECT_FALSE(block->IsFree());
939
940 // Deallocate the block, and verify the bytes are reclaimed.
941 result = BlockType::Free(std::move(block));
942 ASSERT_EQ(result.status(), pw::OkStatus());
943 EXPECT_EQ(result.prev(), BlockResultPrev::kResizedSmaller);
944 EXPECT_EQ(result.next(), BlockResultNext::kUnchanged);
945 EXPECT_EQ(result.size(), BlockType::kAlignment);
946
947 CheckAllReachableBlock(result.block());
948 }
949
TEST_FOR_EACH_BLOCK_TYPE(CannotAllocLastIfTooSmallForAlignment)950 TEST_FOR_EACH_BLOCK_TYPE(CannotAllocLastIfTooSmallForAlignment) {
951 constexpr Layout kLayout(256, kAlign);
952 constexpr size_t kOuterSize = BlockType::kBlockOverhead + kLayout.size();
953
954 // Make sure the block's usable space is not aligned.
955 size_t outer_size = GetFirstAlignedOffset<BlockType>(bytes_, kLayout) + 1;
956 auto* block = Preallocate<BlockType>(
957 bytes_,
958 {
959 {outer_size, Preallocation::kUsed},
960 {kOuterSize, Preallocation::kFree},
961 {Preallocation::kSizeRemaining, Preallocation::kUsed},
962 });
963 block = block->Next();
964
965 // Cannot allocate without room to a split a block for alignment.
966 auto result = BlockType::AllocLast(std::move(block), kLayout);
967 EXPECT_EQ(result.status(), pw::Status::ResourceExhausted());
968 CheckAllReachableBlock(result.block());
969 }
970
TEST_FOR_EACH_BLOCK_TYPE(CannotAllocLastFromNull)971 TEST_FOR_EACH_BLOCK_TYPE(CannotAllocLastFromNull) {
972 BlockType* block = nullptr;
973 constexpr Layout kLayout(1, 1);
974 auto result = BlockType::AllocLast(std::move(block), kLayout);
975 EXPECT_EQ(result.status(), pw::Status::InvalidArgument());
976 EXPECT_EQ(result.block(), nullptr);
977 }
978
TEST_FOR_EACH_BLOCK_TYPE(CannotAllocLastZeroSize)979 TEST_FOR_EACH_BLOCK_TYPE(CannotAllocLastZeroSize) {
980 auto* block = Preallocate<BlockType>(
981 bytes_,
982 {
983 {Preallocation::kSizeRemaining, Preallocation::kFree},
984 });
985 constexpr Layout kLayout(0, 1);
986 auto result = BlockType::AllocLast(std::move(block), kLayout);
987 EXPECT_EQ(result.status(), pw::Status::InvalidArgument());
988 CheckAllReachableBlock(result.block());
989 }
990
TEST_FOR_EACH_BLOCK_TYPE(CannotAllocLastFromUsed)991 TEST_FOR_EACH_BLOCK_TYPE(CannotAllocLastFromUsed) {
992 auto* block = Preallocate<BlockType>(
993 bytes_,
994 {
995 {Preallocation::kSizeRemaining, Preallocation::kUsed},
996 });
997 constexpr Layout kLayout(1, 1);
998 auto result = BlockType::AllocLast(std::move(block), kLayout);
999 EXPECT_EQ(result.status(), pw::Status::FailedPrecondition());
1000 CheckAllReachableBlock(result.block());
1001 }
1002
TEST_FOR_EACH_BLOCK_TYPE(FreeingNullDoesNothing)1003 TEST_FOR_EACH_BLOCK_TYPE(FreeingNullDoesNothing) {
1004 BlockType* block = nullptr;
1005 auto result = BlockType::Free(std::move(block));
1006 EXPECT_EQ(result.status(), pw::Status::InvalidArgument());
1007 }
1008
TEST_FOR_EACH_BLOCK_TYPE(FreeingFreeBlockDoesNothing)1009 TEST_FOR_EACH_BLOCK_TYPE(FreeingFreeBlockDoesNothing) {
1010 auto* block = Preallocate<BlockType>(
1011 bytes_,
1012 {
1013 {Preallocation::kSizeRemaining, Preallocation::kFree},
1014 });
1015 auto result = BlockType::Free(std::move(block));
1016 ASSERT_EQ(result.status(), pw::OkStatus());
1017 CheckAllReachableBlock(result.block());
1018 }
1019
TEST_FOR_EACH_BLOCK_TYPE(CanFree)1020 TEST_FOR_EACH_BLOCK_TYPE(CanFree) {
1021 auto* block = Preallocate<BlockType>(
1022 bytes_,
1023 {
1024 {Preallocation::kSizeRemaining, Preallocation::kUsed},
1025 });
1026
1027 auto result = BlockType::Free(std::move(block));
1028 ASSERT_EQ(result.status(), pw::OkStatus());
1029 EXPECT_EQ(result.prev(), BlockResultPrev::kUnchanged);
1030 EXPECT_EQ(result.next(), BlockResultNext::kUnchanged);
1031
1032 block = result.block();
1033 EXPECT_TRUE(block->IsFree());
1034 EXPECT_EQ(block->OuterSize(), kN);
1035 CheckAllReachableBlock(block);
1036 }
1037
TEST_FOR_EACH_BLOCK_TYPE(CanFreeBlockWithoutMerging)1038 TEST_FOR_EACH_BLOCK_TYPE(CanFreeBlockWithoutMerging) {
1039 constexpr size_t kOuterSize = 256;
1040
1041 auto* block = Preallocate<BlockType>(
1042 bytes_,
1043 {
1044 {kOuterSize, Preallocation::kUsed},
1045 {kOuterSize, Preallocation::kUsed},
1046 {Preallocation::kSizeRemaining, Preallocation::kUsed},
1047 });
1048 block = block->Next();
1049 BlockType* next = block->Next();
1050 BlockType* prev = block->Prev();
1051
1052 auto result = BlockType::Free(std::move(block));
1053 ASSERT_EQ(result.status(), pw::OkStatus());
1054 EXPECT_EQ(result.prev(), BlockResultPrev::kUnchanged);
1055 EXPECT_EQ(result.next(), BlockResultNext::kUnchanged);
1056
1057 block = result.block();
1058 EXPECT_TRUE(block->IsFree());
1059 EXPECT_EQ(next, block->Next());
1060 EXPECT_EQ(prev, block->Prev());
1061 CheckAllReachableBlock(block);
1062 }
1063
TEST_FOR_EACH_BLOCK_TYPE(CanFreeBlockAndMergeWithPrev)1064 TEST_FOR_EACH_BLOCK_TYPE(CanFreeBlockAndMergeWithPrev) {
1065 constexpr size_t kOuterSize = 256;
1066
1067 auto* first = Preallocate<BlockType>(
1068 bytes_,
1069 {
1070 {kOuterSize, Preallocation::kFree},
1071 {kOuterSize, Preallocation::kUsed},
1072 {Preallocation::kSizeRemaining, Preallocation::kUsed},
1073 });
1074 BlockType* block = first->Next();
1075 BlockType* next = block->Next();
1076
1077 // Note that when merging with the previous free block, it is that previous
1078 // free block which is returned, and only the 'next' field indicates a merge.
1079 auto result = BlockType::Free(std::move(block));
1080 ASSERT_EQ(result.status(), pw::OkStatus());
1081 EXPECT_EQ(result.prev(), BlockResultPrev::kUnchanged);
1082 EXPECT_EQ(result.next(), BlockResultNext::kMerged);
1083
1084 block = result.block();
1085 EXPECT_EQ(block->Prev(), nullptr);
1086 EXPECT_EQ(block->Next(), next);
1087 CheckAllReachableBlock(block);
1088 }
1089
TEST_FOR_EACH_BLOCK_TYPE(CanFreeBlockAndMergeWithNext)1090 TEST_FOR_EACH_BLOCK_TYPE(CanFreeBlockAndMergeWithNext) {
1091 constexpr size_t kOuterSize = 256;
1092
1093 auto* first = Preallocate<BlockType>(
1094 bytes_,
1095 {
1096 {kOuterSize, Preallocation::kUsed},
1097 {kOuterSize, Preallocation::kUsed},
1098 {Preallocation::kSizeRemaining, Preallocation::kFree},
1099 });
1100 BlockType* block = first->Next();
1101 BlockType* prev = block->Prev();
1102
1103 auto result = BlockType::Free(std::move(block));
1104 ASSERT_EQ(result.status(), pw::OkStatus());
1105 EXPECT_EQ(result.prev(), BlockResultPrev::kUnchanged);
1106 EXPECT_EQ(result.next(), BlockResultNext::kMerged);
1107
1108 block = result.block();
1109 EXPECT_TRUE(block->IsFree());
1110 EXPECT_EQ(block->Prev(), prev);
1111 EXPECT_EQ(block->Next(), nullptr);
1112 CheckAllReachableBlock(block);
1113 }
1114
TEST_FOR_EACH_BLOCK_TYPE(CanFreeBlockAndMergeWithBoth)1115 TEST_FOR_EACH_BLOCK_TYPE(CanFreeBlockAndMergeWithBoth) {
1116 constexpr size_t kOuterSize = 128;
1117
1118 auto* first = Preallocate<BlockType>(
1119 bytes_,
1120 {
1121 {kOuterSize, Preallocation::kFree},
1122 {kOuterSize, Preallocation::kUsed},
1123 {Preallocation::kSizeRemaining, Preallocation::kFree},
1124 });
1125 BlockType* block = first->Next();
1126
1127 auto result = BlockType::Free(std::move(block));
1128 ASSERT_EQ(result.status(), pw::OkStatus());
1129 EXPECT_EQ(result.prev(), BlockResultPrev::kUnchanged);
1130 EXPECT_EQ(result.next(), BlockResultNext::kMerged);
1131
1132 block = result.block();
1133 EXPECT_EQ(block->Prev(), nullptr);
1134 EXPECT_EQ(block->Next(), nullptr);
1135 CheckAllReachableBlock(block);
1136 }
1137
TEST_FOR_EACH_BLOCK_TYPE(CanResizeBlockSameSize)1138 TEST_FOR_EACH_BLOCK_TYPE(CanResizeBlockSameSize) {
1139 constexpr size_t kOuterSize = 256;
1140
1141 auto* block = Preallocate<BlockType>(
1142 bytes_,
1143 {
1144 {kOuterSize, Preallocation::kUsed},
1145 {kOuterSize, Preallocation::kUsed},
1146 {Preallocation::kSizeRemaining, Preallocation::kUsed},
1147 });
1148 block = block->Next();
1149
1150 auto result = block->Resize(block->InnerSize());
1151 EXPECT_EQ(result.status(), pw::OkStatus());
1152 EXPECT_EQ(result.prev(), BlockResultPrev::kUnchanged);
1153 EXPECT_EQ(result.next(), BlockResultNext::kUnchanged);
1154 CheckAllReachableBlock(block);
1155 }
1156
TEST_FOR_EACH_BLOCK_TYPE(CannotResizeFreeBlock)1157 TEST_FOR_EACH_BLOCK_TYPE(CannotResizeFreeBlock) {
1158 constexpr size_t kOuterSize = 256;
1159
1160 auto* block = Preallocate<BlockType>(
1161 bytes_,
1162 {
1163 {kOuterSize, Preallocation::kUsed},
1164 {kOuterSize, Preallocation::kFree},
1165 {Preallocation::kSizeRemaining, Preallocation::kUsed},
1166 });
1167 block = block->Next();
1168
1169 auto result = block->Resize(block->InnerSize());
1170 EXPECT_EQ(result.status(), pw::Status::FailedPrecondition());
1171 CheckAllReachableBlock(block);
1172 }
1173
TEST_FOR_EACH_BLOCK_TYPE(CanResizeBlockSmallerWithNextFree)1174 TEST_FOR_EACH_BLOCK_TYPE(CanResizeBlockSmallerWithNextFree) {
1175 constexpr size_t kOuterSize = 256;
1176
1177 auto* block = Preallocate<BlockType>(
1178 bytes_,
1179 {
1180 {kOuterSize, Preallocation::kUsed},
1181 {kOuterSize, Preallocation::kUsed},
1182 {Preallocation::kSizeRemaining, Preallocation::kFree},
1183 });
1184 block = block->Next();
1185 size_t next_inner_size = block->Next()->InnerSize();
1186
1187 // Shrink by less than the minimum needed for a block. The extra should be
1188 // added to the subsequent block.
1189 size_t delta = BlockType::kBlockOverhead - BlockType::kAlignment;
1190 size_t new_inner_size = block->InnerSize() - delta;
1191 auto result = block->Resize(new_inner_size);
1192 EXPECT_EQ(result.status(), pw::OkStatus());
1193 EXPECT_EQ(result.prev(), BlockResultPrev::kUnchanged);
1194 EXPECT_EQ(result.next(), BlockResultNext::kResized);
1195 EXPECT_EQ(block->InnerSize(), new_inner_size);
1196
1197 BlockType* next = block->Next();
1198 EXPECT_TRUE(next->IsFree());
1199 EXPECT_EQ(next->InnerSize(), next_inner_size + delta);
1200 CheckAllReachableBlock(block);
1201 }
1202
TEST_FOR_EACH_BLOCK_TYPE(CanResizeBlockLargerWithNextFree)1203 TEST_FOR_EACH_BLOCK_TYPE(CanResizeBlockLargerWithNextFree) {
1204 constexpr size_t kOuterSize = 256;
1205
1206 auto* block = Preallocate<BlockType>(
1207 bytes_,
1208 {
1209 {kOuterSize, Preallocation::kUsed},
1210 {kOuterSize, Preallocation::kFree},
1211 {Preallocation::kSizeRemaining, Preallocation::kUsed},
1212 });
1213 size_t next_inner_size = block->Next()->InnerSize();
1214
1215 // Grow by less than the minimum needed for a block. The extra should be
1216 // added to the subsequent block.
1217 size_t delta = BlockType::kBlockOverhead;
1218 size_t new_inner_size = block->InnerSize() + delta;
1219 auto result = block->Resize(new_inner_size);
1220 EXPECT_EQ(result.status(), pw::OkStatus());
1221 EXPECT_EQ(result.prev(), BlockResultPrev::kUnchanged);
1222 EXPECT_EQ(result.next(), BlockResultNext::kResized);
1223 EXPECT_EQ(block->InnerSize(), new_inner_size);
1224
1225 BlockType* next = block->Next();
1226 EXPECT_TRUE(next->IsFree());
1227 EXPECT_EQ(next->InnerSize(), next_inner_size - delta);
1228 CheckAllReachableBlock(block);
1229 }
1230
TEST_FOR_EACH_BLOCK_TYPE(CannotResizeBlockMuchLargerWithNextFree)1231 TEST_FOR_EACH_BLOCK_TYPE(CannotResizeBlockMuchLargerWithNextFree) {
1232 constexpr size_t kOuterSize = 256;
1233
1234 auto* block = Preallocate<BlockType>(
1235 bytes_,
1236 {
1237 {kOuterSize, Preallocation::kUsed},
1238 {kOuterSize, Preallocation::kFree},
1239 {Preallocation::kSizeRemaining, Preallocation::kUsed},
1240 });
1241
1242 size_t new_inner_size = block->InnerSize() + kOuterSize + 1;
1243 auto result = block->Resize(new_inner_size);
1244 EXPECT_EQ(result.status(), pw::Status::ResourceExhausted());
1245 CheckAllReachableBlock(block);
1246 }
1247
TEST_FOR_EACH_BLOCK_TYPE(CanResizeBlockSmallerWithNextUsed)1248 TEST_FOR_EACH_BLOCK_TYPE(CanResizeBlockSmallerWithNextUsed) {
1249 constexpr Layout kLayout(256, kAlign);
1250 constexpr size_t kOuterSize = BlockType::kBlockOverhead + kLayout.size();
1251
1252 auto* block = Preallocate<BlockType>(
1253 bytes_,
1254 {
1255 {kOuterSize, Preallocation::kUsed},
1256 {Preallocation::kSizeRemaining, Preallocation::kUsed},
1257 });
1258
1259 // Shrink the block.
1260 size_t delta = kLayout.size() / 2;
1261 size_t new_inner_size = block->InnerSize() - delta;
1262 auto result = block->Resize(new_inner_size);
1263 EXPECT_EQ(result.status(), pw::OkStatus());
1264 EXPECT_EQ(result.prev(), BlockResultPrev::kUnchanged);
1265 EXPECT_EQ(result.next(), BlockResultNext::kSplitNew);
1266
1267 BlockType* next = block->Next();
1268 EXPECT_TRUE(next->IsFree());
1269 EXPECT_EQ(next->OuterSize(), delta);
1270 CheckAllReachableBlock(block);
1271 }
1272
TEST_FOR_EACH_BLOCK_TYPE(CannotResizeBlockLargerWithNextUsed)1273 TEST_FOR_EACH_BLOCK_TYPE(CannotResizeBlockLargerWithNextUsed) {
1274 constexpr size_t kOuterSize = 256;
1275
1276 auto* block = Preallocate<BlockType>(
1277 bytes_,
1278 {
1279 {kOuterSize, Preallocation::kUsed},
1280 {Preallocation::kSizeRemaining, Preallocation::kUsed},
1281 });
1282
1283 size_t delta = BlockType::kBlockOverhead / 2;
1284 size_t new_inner_size = block->InnerSize() + delta;
1285 auto result = block->Resize(new_inner_size);
1286 EXPECT_EQ(result.status(), pw::Status::ResourceExhausted());
1287 }
1288
TEST_FOR_EACH_BLOCK_TYPE(CanCheckValidBlock)1289 TEST_FOR_EACH_BLOCK_TYPE(CanCheckValidBlock) {
1290 constexpr size_t kOuterSize1 = 512;
1291 constexpr size_t kOuterSize2 = 256;
1292
1293 auto* block = Preallocate<BlockType>(
1294 bytes_,
1295 {
1296 {kOuterSize1, Preallocation::kUsed},
1297 {kOuterSize2, Preallocation::kFree},
1298 {Preallocation::kSizeRemaining, Preallocation::kUsed},
1299 });
1300 ASSERT_TRUE(block->IsValid());
1301
1302 block = block->Next();
1303 ASSERT_TRUE(block->IsValid());
1304
1305 block = block->Next();
1306 ASSERT_TRUE(block->IsValid());
1307 }
1308
TEST_FOR_EACH_BLOCK_TYPE(CanCheckInvalidBlock)1309 TEST_FOR_EACH_BLOCK_TYPE(CanCheckInvalidBlock) {
1310 constexpr size_t kOuterSize1 = 128;
1311 constexpr size_t kOuterSize2 = 384;
1312
1313 auto* block1 = Preallocate<BlockType>(
1314 bytes_,
1315 {
1316 {kOuterSize1, Preallocation::kUsed},
1317 {kOuterSize2, Preallocation::kFree},
1318 {Preallocation::kSizeRemaining, Preallocation::kUsed},
1319 });
1320 BlockType* block2 = block1->Next();
1321 BlockType* block3 = block2->Next();
1322
1323 // Corrupt a Block header.
1324 // This must not touch memory outside the original region, or the test may
1325 // (correctly) abort when run with address sanitizer.
1326 // To remain as agostic to the internals of `Block` as possible, the test
1327 // copies a smaller block's header to a larger block, and ensure's the
1328 // contents of blocks are blank.
1329 std::memset(block1->UsableSpace(), 0, block1->InnerSize());
1330 std::memset(block2->UsableSpace(), 0, block2->InnerSize());
1331 std::memset(block3->UsableSpace(), 0, block3->InnerSize());
1332 EXPECT_TRUE(block1->IsValid());
1333 EXPECT_TRUE(block2->IsValid());
1334 EXPECT_TRUE(block3->IsValid());
1335 auto* src = cpp20::bit_cast<std::byte*>(block1);
1336 auto* dst = cpp20::bit_cast<std::byte*>(block2);
1337 std::memcpy(dst, src, sizeof(BlockType));
1338 EXPECT_FALSE(block1->IsValid());
1339 EXPECT_FALSE(block2->IsValid());
1340 EXPECT_FALSE(block3->IsValid());
1341 }
1342
TEST_FOR_EACH_BLOCK_TYPE(CanCheckPoison)1343 TEST_FOR_EACH_BLOCK_TYPE(CanCheckPoison) {
1344 auto* block = Preallocate<BlockType>(
1345 bytes_,
1346 {
1347 {Preallocation::kSizeRemaining, Preallocation::kFree},
1348 });
1349
1350 // Modify a byte in the middle of a free block.
1351 // Without poisoning, the modification is undetected.
1352 EXPECT_TRUE(block->IsFree());
1353 bytes_[kN / 2] = std::byte(0x7f);
1354 EXPECT_TRUE(block->IsValid());
1355
1356 // Modify a byte in the middle of a free block.
1357 // With poisoning, the modification is detected.
1358 block->Poison();
1359 bytes_[kN / 2] = std::byte(0x7f);
1360 EXPECT_FALSE(block->IsValid());
1361 }
1362
TEST_FOR_EACH_BLOCK_TYPE(CanGetBlockFromUsableSpace)1363 TEST_FOR_EACH_BLOCK_TYPE(CanGetBlockFromUsableSpace) {
1364 auto* block1 = Preallocate<BlockType>(
1365 bytes_,
1366 {
1367 {Preallocation::kSizeRemaining, Preallocation::kFree},
1368 });
1369
1370 void* ptr = block1->UsableSpace();
1371 BlockType* block2 = BlockType::FromUsableSpace(ptr);
1372 EXPECT_EQ(block1, block2);
1373 }
1374
TEST_FOR_EACH_BLOCK_TYPE(CanGetConstBlockFromUsableSpace)1375 TEST_FOR_EACH_BLOCK_TYPE(CanGetConstBlockFromUsableSpace) {
1376 const auto* block1 = Preallocate<BlockType>(
1377 bytes_,
1378 {
1379 {Preallocation::kSizeRemaining, Preallocation::kFree},
1380 });
1381
1382 const void* ptr = block1->UsableSpace();
1383 const BlockType* block2 = BlockType::FromUsableSpace(ptr);
1384 EXPECT_EQ(block1, block2);
1385 }
1386
TEST_FOR_EACH_BLOCK_TYPE(CanGetAlignmentFromUsedBlock)1387 TEST_FOR_EACH_BLOCK_TYPE(CanGetAlignmentFromUsedBlock) {
1388 constexpr Layout kLayout1(128, kAlign);
1389 constexpr Layout kLayout2(384, kAlign * 2);
1390
1391 auto* block1 = Preallocate<BlockType>(
1392 bytes_,
1393 {
1394 {Preallocation::kSizeRemaining, Preallocation::kFree},
1395 });
1396
1397 auto result = BlockType::AllocLast(std::move(block1), kLayout1);
1398 ASSERT_EQ(result.status(), pw::OkStatus());
1399 block1 = result.block();
1400
1401 BlockType* block2 = block1->Prev();
1402 result = BlockType::AllocLast(std::move(block2), kLayout2);
1403 ASSERT_EQ(result.status(), pw::OkStatus());
1404 block2 = result.block();
1405
1406 auto block1_layout = block1->RequestedLayout();
1407 auto block2_layout = block2->RequestedLayout();
1408 EXPECT_EQ(block1_layout->alignment(), kAlign);
1409 EXPECT_EQ(block2_layout->alignment(), kAlign * 2);
1410 }
1411
TEST_FOR_EACH_BLOCK_TYPE(FreeBlocksHaveDefaultAlignment)1412 TEST_FOR_EACH_BLOCK_TYPE(FreeBlocksHaveDefaultAlignment) {
1413 constexpr Layout kLayout1(128, kAlign);
1414 constexpr Layout kLayout2(384, kAlign * 2);
1415
1416 auto* block1 = Preallocate<BlockType>(
1417 bytes_,
1418 {
1419 {Preallocation::kSizeRemaining, Preallocation::kFree},
1420 });
1421
1422 auto result = BlockType::AllocLast(std::move(block1), kLayout1);
1423 ASSERT_EQ(result.status(), pw::OkStatus());
1424 block1 = result.block();
1425
1426 BlockType* block2 = block1->Prev();
1427 result = BlockType::AllocLast(std::move(block2), kLayout2);
1428 ASSERT_EQ(result.status(), pw::OkStatus());
1429
1430 auto layout = block1->RequestedLayout();
1431 ASSERT_EQ(layout.status(), pw::OkStatus());
1432 EXPECT_EQ(layout->alignment(), kAlign);
1433
1434 result = BlockType::Free(std::move(block1));
1435 ASSERT_EQ(result.status(), pw::OkStatus());
1436 EXPECT_EQ(result.prev(), BlockResultPrev::kUnchanged);
1437 EXPECT_EQ(result.next(), BlockResultNext::kMerged);
1438 block1 = result.block();
1439
1440 layout = block1->RequestedLayout();
1441 EXPECT_EQ(layout.status(), pw::Status::FailedPrecondition());
1442 }
1443
1444 } // namespace
1445