19#include "pw_allocator/allocator.h"
20#include "pw_allocator/block/basic.h"
21#include "pw_allocator/block/iterable.h"
22#include "pw_allocator/block/poisonable.h"
23#include "pw_allocator/block/result.h"
24#include "pw_allocator/block/with_layout.h"
25#include "pw_allocator/capability.h"
26#include "pw_allocator/config.h"
27#include "pw_allocator/fragmentation.h"
28#include "pw_allocator/hardening.h"
29#include "pw_assert/assert.h"
30#include "pw_bytes/span.h"
31#include "pw_result/result.h"
32#include "pw_status/status.h"
34namespace pw::allocator {
55 template <
typename BlockType>
81template <
typename,
size_t>
97template <
typename BlockType_>
103 using BlockType = BlockType_;
104 using Range =
typename BlockType::Range;
107 Base::GetCapabilities<BlockType>();
113 Range
blocks()
const {
return Range(first_); }
156 bool DoResize(
void* ptr,
size_t new_size)
override;
187 template <
typename Ptr>
201 template <
typename,
size_t>
247 static bool PrevIsFree(
const BlockType* block) {
248 auto* prev = block->Prev();
249 return prev !=
nullptr && prev->IsFree();
253 static bool NextIsFree(
const BlockType* block) {
254 auto* next = block->Next();
255 return next !=
nullptr && next->IsFree();
260 void UpdateLast(BlockType* block);
263 size_t capacity_ = 0;
264 size_t allocated_ = 0;
265 BlockType* first_ =
nullptr;
266 BlockType* last_ =
nullptr;
267 uint16_t unpoisoned_ = 0;
276template <
typename BlockType>
277constexpr Capabilities GenericBlockAllocator::GetCapabilities() {
278 Capabilities common = kImplementsGetUsableLayout |
279 kImplementsGetAllocatedLayout | kImplementsGetCapacity |
280 kImplementsRecognizes;
281 if constexpr (has_layout_v<BlockType>) {
282 return common | kImplementsGetRequestedLayout;
290template <
typename BlockType>
291BlockAllocator<BlockType>::~BlockAllocator() {
292 if constexpr (Hardening::kIncludesRobustChecks) {
293 for (
auto* block : blocks()) {
294 if (!block->IsFree()) {
295 CrashOnAllocated(block);
301template <
typename BlockType>
304 PW_ASSERT(result.
ok());
308template <
typename BlockType>
310 if constexpr (Hardening::kIncludesRobustChecks) {
311 PW_ASSERT(begin !=
nullptr);
312 PW_ASSERT(begin->Prev() ==
nullptr);
315 for (
auto* block : blocks()) {
317 capacity_ += block->OuterSize();
318 if (block->IsFree()) {
319 RecycleBlock(*block);
324template <
typename BlockType>
326 if (capacity_ == 0) {
331 if constexpr (Hardening::kIncludesDebugChecks) {
332 PW_ASSERT(last_->Next() ==
nullptr);
334 auto result = ChooseBlock(layout);
339 BlockType* block = result.block();
340 allocated_ += block->OuterSize();
341 switch (result.prev()) {
342 case BlockResultPrev::kSplitNew:
344 RecycleBlock(*(block->Prev()));
346 case BlockResultPrev::kResizedLarger:
348 allocated_ += result.size();
350 case BlockResultPrev::kUnchanged:
351 case BlockResultPrev::kResizedSmaller:
354 if (result.next() == BlockResultNext::kSplitNew) {
355 RecycleBlock(*(block->Next()));
359 if constexpr (Hardening::kIncludesDebugChecks) {
360 PW_ASSERT(block <= last_);
363 return block->UsableSpace();
366template <
typename BlockType>
368 BlockType* block = FromUsableSpace(ptr);
369 if (block->IsFree()) {
370 if constexpr (Hardening::kIncludesBasicChecks) {
371 CrashOnDoubleFree(block);
376 DeallocateBlock(std::move(block));
379template <
typename BlockType>
382 if (
auto* prev = block->Prev(); prev !=
nullptr && prev->IsFree()) {
385 if (
auto* next = block->Next(); next !=
nullptr && next->IsFree()) {
390 allocated_ -= block->OuterSize();
391 auto free_result = BlockType::Free(std::move(block));
392 block = free_result.block();
395 if (free_result.prev() == BlockResultPrev::kResizedSmaller) {
397 allocated_ -= free_result.size();
400 if constexpr (is_poisonable_v<BlockType> && kPoisonInterval != 0) {
402 if (unpoisoned_ >= kPoisonInterval) {
408 RecycleBlock(*block);
411template <
typename BlockType>
413 BlockType* block = FromUsableSpace(ptr);
416 if (
auto* next = block->Next(); next !=
nullptr && next->IsFree()) {
420 size_t old_size = block->OuterSize();
421 if (!block->Resize(new_size).ok()) {
424 allocated_ -= old_size;
425 allocated_ += block->OuterSize();
428 if (
auto* next = block->Next(); next !=
nullptr && next->IsFree()) {
435template <
typename BlockType>
437 const void* ptr)
const {
439 if (info_type == InfoType::kCapacity) {
443 if (ptr < first_->UsableSpace() || last_->UsableSpace() < ptr) {
446 const auto* block = BlockType::FromUsableSpace(ptr);
447 if (!block->IsValid()) {
450 if (block->IsFree()) {
453 if constexpr (kCapabilities.has(kImplementsGetRequestedLayout)) {
454 if (info_type == InfoType::kRequestedLayoutOf) {
455 return block->RequestedLayout();
459 case InfoType::kUsableLayoutOf:
460 return Layout(block->InnerSize(), BlockType::kAlignment);
461 case InfoType::kAllocatedLayoutOf:
462 return Layout(block->OuterSize(), BlockType::kAlignment);
463 case InfoType::kRecognizes:
465 case InfoType::kCapacity:
466 case InfoType::kRequestedLayoutOf:
472template <
typename BlockType>
476 for (
auto block : blocks()) {
477 if (block->IsFree()) {
478 fragmentation.
AddFragment(block->InnerSize() / BlockType::kAlignment);
481 return fragmentation;
484template <
typename BlockType>
485template <
typename Ptr>
486internal::copy_const_ptr_t<Ptr, BlockType*>
488 if (ptr < first_->UsableSpace() || last_->UsableSpace() < ptr) {
489 if constexpr (Hardening::kIncludesBasicChecks) {
490 CrashOnOutOfRange(ptr);
494 auto* block = BlockType::FromUsableSpace(ptr);
495 if (!block->IsValid()) {
496 if constexpr (Hardening::kIncludesBasicChecks) {
497 block->CheckInvariants();
504template <
typename BlockType>
506 BlockType* next = block->Next();
507 if (next ==
nullptr) {
509 }
else if (next->Next() ==
nullptr) {
Definition: allocator.h:42
InfoType
Definition: deallocator.h:173
constexpr bool ok() const
Definition: result.h:451
static constexpr Status DataLoss()
Definition: status.h:316
static constexpr Status Unimplemented()
Definition: status.h:280
static constexpr Status FailedPrecondition()
Definition: status.h:243
static constexpr Status NotFound()
Definition: status.h:190
Definition: block_allocator.h:98
void * DoAllocate(Layout layout) override
Definition: block_allocator.h:325
Range blocks() const
Returns a Range of blocks tracking the memory of this allocator.
Definition: block_allocator.h:113
virtual void RecycleBlock(BlockType &block)
Definition: block_allocator.h:230
size_t DoGetAllocated() const override
Definition: block_allocator.h:159
virtual size_t DoGetMaxAllocatable()=0
internal::copy_const_ptr_t< Ptr, BlockType * > FromUsableSpace(Ptr ptr) const
Definition: block_allocator.h:487
virtual BlockResult< BlockType > ChooseBlock(Layout layout)=0
std::optional< Fragmentation > DoMeasureFragmentation() const override
Returns fragmentation information for the allocator's memory region.
Definition: block_allocator.h:473
void DoDeallocate(void *ptr) override
Definition: block_allocator.h:367
virtual void DeallocateBlock(BlockType *&&block)
Definition: block_allocator.h:380
bool DoResize(void *ptr, size_t new_size) override
Definition: block_allocator.h:412
void Init(ByteSpan region)
Definition: block_allocator.h:302
void Init(BlockType *begin)
Definition: block_allocator.h:309
virtual void Flush()
Definition: block_allocator.h:244
size_t GetMaxAllocatable()
Definition: block_allocator.h:144
virtual void ReserveBlock(BlockType &block)
Definition: block_allocator.h:222
Result< Layout > DoGetInfo(InfoType info_type, const void *ptr) const override
Definition: block_allocator.h:436
Definition: capability.h:65
Definition: detailed_block.h:88
Definition: block_allocator.h:46
static void CrashOnOutOfRange(const void *freed)
static void CrashOnDoubleFree(const void *freed)
Crashes with an informational message that a given block was freed twice.
static void CrashOnAllocated(const void *allocated)
Next
Definition: result.h:45
Prev
Definition: result.h:36
Definition: block_allocator.h:82
#define PW_ALLOCATOR_BLOCK_POISON_INTERVAL
Definition: config.h:30
Definition: fragmentation.h:47
void AddFragment(size_t inner_size)
Includes a region of free memory in the fragmentation calculation.