blob: 0f5411624bf65c789882edc164d6d464640e92cb [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 <array>
#include <cstddef>
#include "pw_alignment/alignment.h"
#include "pw_allocator/allocator.h"
#include "pw_allocator/bucket.h"
#include "pw_bytes/span.h"
#include "pw_containers/vector.h"
namespace pw::allocator {
namespace internal {
/// Size-independent buddy allocator.
///
/// This allocator allocates chunks of memory whose sizes are powers of two.
/// See also https://en.wikipedia.org/wiki/Buddy_memory_allocation.
///
/// Compared to `BuddyAllocator`, this implementation is size-agnostic with
/// respect to the number of buckets.
class GenericBuddyAllocator final {
public:
/// Construct a buddy allocator.
///
/// @param[in] buckets Storage for buckets of free chunks.
/// @param[in] min_chunk_size Size of the chunks in the first bucket.
/// @param[in] region Memory used to allocate chunks.
GenericBuddyAllocator(span<Bucket> buckets,
size_t min_chunk_size,
ByteSpan region);
/// @copydoc Allocator::Allocate
void* Allocate(Layout layout);
/// @copydoc Deallocator::Deallocate
void Deallocate(void* ptr);
/// @copydoc Deallocator::Query
Status Query(const void* ptr) const;
/// Ensures all allocations have been freed. Crashes with a diagnostic message
/// If any allocations remain outstanding.
void CrashIfAllocated();
private:
span<Bucket> buckets_;
ByteSpan region_;
};
} // namespace internal
/// Allocator that uses the buddy memory allocation algorithm.
///
/// This allocator allocates chunks of memory whose sizes are powers of two.
/// This allows the allocator to satisfy requests to acquire and release memory
/// very quickly, at the possible cost of higher internal fragmentation. In
/// particular:
///
/// * The maximum alignment for this allocator is `kMinChunkSize`.
/// * The minimum size of an allocation is `kMinChunkSize`. Less may be
/// requested, but it will be satisfied by a minimal chunk.
/// * The maximum size of an allocation is `kMinChunkSize << (kNumBuckets - 1)`.
///
/// Use this allocator if you know the needed sizes are close to but less than
/// chunk sizes and you need high allocator performance.
///
/// @tparam kMinChunkSize Size of the smallest allocatable chunk. Must be a
/// a power of two. All allocations will use at least
/// this much memory.
/// @tparam kNumBuckets Number of buckets. Must be at least 1. Each
/// additional bucket allows combining chunks into
/// larger chunks.
template <size_t kMinChunkSize, size_t kNumBuckets>
class BuddyAllocator : public Allocator {
public:
static_assert((kMinChunkSize & (kMinChunkSize - 1)) == 0,
"kMinChunkSize must be a power of 2");
/// Construct an allocator.
///
/// @param[in] region Region of memory to use when satisfying allocation
/// requests. The region must be large enough to fit a
/// least one minimally-size chunk aligned to the size of
/// the chunk.
BuddyAllocator(ByteSpan region) : impl_(buckets_, kMinChunkSize, region) {}
~BuddyAllocator() override { impl_.CrashIfAllocated(); }
private:
/// @copydoc Allocator::Allocate
void* DoAllocate(Layout layout) override {
// Reserve one byte to save the bucket index.
return impl_.Allocate(layout.Extend(1));
}
/// @copydoc Deallocator::DoDeallocate
void DoDeallocate(void* ptr) override { impl_.Deallocate(ptr); }
/// @copydoc Deallocator::Query
Status DoQuery(const void* ptr) const override { return impl_.Query(ptr); }
std::array<internal::Bucket, kNumBuckets> buckets_;
internal::GenericBuddyAllocator impl_;
};
} // namespace pw::allocator