blob: b291634668494796b8622dd7c970755f4ffecbad [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 "pw_allocator/allocator.h"
#include "pw_allocator/chunk_pool.h"
#include "pw_bytes/span.h"
#include "pw_result/result.h"
namespace pw::allocator {
/// Typed pool that can be used for slab allocation.
///
/// This class is a special purpose pool designed to allocate objects of one
/// specific type, ``T``. It is useful when you need a dynamic pool of objects
/// with very low performance and memory overhead costs. For example, a
/// dispatcher might use such an allocator to manage memory for a set of task
/// objects.
///
/// @tparam T The type of object to allocate memory for.
template <typename T>
class TypedPool : public ChunkPool {
public:
/// Returns the amount of memory needed to allocate ``num_objects``.
static constexpr size_t SizeNeeded(size_t num_objects) {
size_t needed = std::max(sizeof(T), ChunkPool::kMinSize);
PW_ASSERT(!PW_MUL_OVERFLOW(needed, num_objects, &needed));
return needed;
}
/// Returns the optimal alignment for the backing memory region.
static constexpr size_t AlignmentNeeded() {
return std::max(alignof(T), ChunkPool::kMinAlignment);
}
/// Provides aligned storage for `kNumObjects` of type `T`.
template <size_t kNumObjects>
struct Buffer {
static_assert(kNumObjects != 0);
alignas(
AlignmentNeeded()) std::array<std::byte, SizeNeeded(kNumObjects)> data;
};
/// Construct a ``TypedPool``.
///
/// This constructor uses the `Buffer` type to minimize wasted memory. For
/// example:
///
/// @code{.cpp}
/// TypedPool<MyObject>::Buffer<100> buffer;
/// TypedPool<MyObject> my_pool(buffer);
/// @endcode
///
/// @param buffer The memory to allocate from.
template <size_t kNumObjects>
TypedPool(Buffer<kNumObjects>& buffer)
: ChunkPool(buffer.data, Layout::Of<T>()) {}
/// Construct a ``TypedPool``.
///
/// To minimize wasted memory, align the buffer provided to the allocator to
/// the object type's alignment. For example:
///
/// @code{.cpp}
/// alignas(MyObject) std::array<std::byte, 0x1000> buffer;
/// TypedPool<MyObject> my_pool(buffer);
/// @endcode
///
/// @param region The memory to allocate from. Must be large enough to
/// allocate memory for at least one object.
TypedPool(ByteSpan region) : ChunkPool(region, Layout::Of<T>()) {}
/// Constructs and object from the given `args`
///
/// This method is similar to ``Allocator::New``, except that it is specific
/// to the pool's object type.
///
/// @param[in] args... Arguments passed to the object constructor.
template <int&... ExplicitGuard, typename... Args>
T* New(Args&&... args) {
void* ptr = Allocate();
return ptr != nullptr ? new (ptr) T(std::forward<Args>(args)...) : nullptr;
}
/// Constructs and object from the given `args`, and wraps it in a `UniquePtr`
///
/// This method is similar to ``Allocator::MakeUnique``, except that it is
/// specific to the pool's object type.
///
/// @param[in] args... Arguments passed to the object constructor.
template <int&... ExplicitGuard, typename... Args>
UniquePtr<T> MakeUnique(Args&&... args) {
return Deallocator::WrapUnique<T>(New(std::forward<Args>(args)...));
}
};
} // namespace pw::allocator