blob: cdb427da70c0e64744c5cde70748e2b406ac0e19 [file] [log] [blame]
// Copyright 2024 The Pigweed Authors
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may not
// use this file except in compliance with the License. You may obtain a copy of
// the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations under
// the License.
#pragma once
#include <cstddef>
#include <limits>
#include "pw_allocator/block/detailed_block.h"
#include "pw_allocator/block_allocator.h"
#include "pw_allocator/bucket/fast_sorted.h"
#include "pw_allocator/bucket/sorted.h"
#include "pw_allocator/config.h"
namespace pw::allocator {
/// Alias for a default block type that is compatible with `FirstFitAllocator`.
template <typename OffsetType>
using WorstFitBlock = DetailedBlock<OffsetType, GenericFastSortedItem>;
/// Block allocator that uses a "worst-fit" allocation strategy.
///
/// In this strategy, the allocator handles an allocation request by looking at
/// all unused blocks and finding the smallest one which can satisfy the
/// request.
///
/// This algorithm may make better use of available memory by wasting less on
/// unused fragments, but may also lead to worse fragmentation as those
/// fragments are more likely to be too small to be useful to other requests.
template <typename BlockType = WorstFitBlock<uintptr_t>>
class WorstFitAllocator : public BlockAllocator<BlockType> {
public:
using Base = BlockAllocator<BlockType>;
/// Constexpr constructor. Callers must explicitly call `Init`.
constexpr WorstFitAllocator() = default;
/// Non-constexpr constructor that automatically calls `Init`.
///
/// @param[in] region Region of memory to use when satisfying allocation
/// requests. The region MUST be valid as an argument
/// to `BlockType::Init`.
WorstFitAllocator(ByteSpan region) { Base::Init(region); }
private:
/// @copydoc BlockAllocator::ChooseBlock
BlockResult<BlockType> ChooseBlock(Layout layout) override {
BlockType* block = large_bucket_.RemoveCompatible(layout);
if (block != nullptr) {
return BlockType::AllocFirst(std::move(block), layout);
}
block = small_bucket_.RemoveCompatible(layout);
if (block != nullptr) {
return BlockType::AllocFirst(std::move(block), layout);
}
return BlockResult<BlockType>(nullptr, Status::NotFound());
}
/// @copydoc BlockAllocator::ReserveBlock
void ReserveBlock(BlockType& block) override {
// The small bucket is slower; skip it if we can.
if (!large_bucket_.Remove(block)) {
std::ignore = small_bucket_.Remove(block);
}
}
/// @copydoc BlockAllocator::RecycleBlock
void RecycleBlock(BlockType& block) override {
if (block.InnerSize() <= sizeof(SortedItem)) {
std::ignore = small_bucket_.Add(block);
} else {
std::ignore = large_bucket_.Add(block);
}
}
ReverseSortedBucket<BlockType> small_bucket_;
ReverseFastSortedBucket<BlockType> large_bucket_;
};
} // namespace pw::allocator