blob: db2714aaf4f562267ae4f282419736fbe24be920 [file] [log] [blame]
// Copyright 2023 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 "pw_allocator/allocator.h"
#include "pw_allocator/capability.h"
#include "pw_bytes/span.h"
namespace pw::allocator {
namespace internal {
/// Type-erased base class to allow destroying a generic instance of ``Owned``.
class GenericOwned {
public:
virtual ~GenericOwned() = default;
void set_next(GenericOwned* next) { next_ = next; }
void Destroy() { DoDestroy(); }
private:
virtual void DoDestroy() = 0;
GenericOwned* next_ = nullptr;
};
/// Wrapper class that invokes an object's destructor when a BumpAllocator is
/// destroyed.
template <typename T>
class Owned : public GenericOwned {
public:
void set_object(T* object) { object_ = object; }
private:
void DoDestroy() override {
if (object_ != nullptr) {
std::destroy_at(object_);
object_ = nullptr;
}
}
T* object_ = nullptr;
};
} // namespace internal
/// Allocator that does not automatically delete.
///
/// A "bump" or "arena" allocator provides memory by simply incrementing a
/// pointer to a memory region in order to "allocate" memory for requests, and
/// doing nothing on deallocation. All memory is freed only when the allocator
/// itself is destroyed. As a result, this allocator is extremely fast.
///
/// Bump allocators are useful when short-lived to allocate objects from small
/// buffers with almost zero overhead. Since memory is not deallocated, a bump
/// allocator with a longer lifespan than any of its allocations will end up
/// holding unusable memory. An example of a good use case might be decoding
/// RPC messages that may require a variable amount of space only for as long as
/// it takes to decode a single message.
///
/// On destruction, the destructors for any objects allocated using `New` or
/// `MakeUnique` are NOT called. To have these destructors invoked, you can
/// allocate "owned" objects using `NewOwned` and `MakeUniqueOwned`. This adds a
/// small amount of overhead to the allocation.
class BumpAllocator : public Allocator {
public:
static constexpr Capabilities kCapabilities = kSkipsDestroy;
explicit BumpAllocator(ByteSpan region);
~BumpAllocator() override;
/// Constructs an "owned" object of type `T` from the given `args`
///
/// Owned objects will have their destructors invoked when the allocator goes
/// out of scope.
///
/// The return value is nullable, as allocating memory for the object may
/// fail. Callers must check for this error before using the resulting
/// pointer.
///
/// @param[in] args... Arguments passed to the object constructor.
template <typename T, int&... ExplicitGuard, typename... Args>
T* NewOwned(Args&&... args) {
internal::Owned<T>* owned = New<internal::Owned<T>>();
T* ptr = owned != nullptr ? New<T>(std::forward<Args>(args)...) : nullptr;
if (ptr != nullptr) {
owned->set_object(ptr);
owned->set_next(owned_);
owned_ = owned;
}
return ptr;
}
/// Constructs and object of type `T` from the given `args`, and wraps it in a
/// `UniquePtr`
///
/// Owned objects will have their destructors invoked when the allocator goes
/// out of scope.
///
/// The returned value may contain null if allocating memory for the object
/// fails. Callers must check for null before using the `UniquePtr`.
///
/// @param[in] args... Arguments passed to the object constructor.
template <typename T, int&... ExplicitGuard, typename... Args>
[[nodiscard]] UniquePtr<T> MakeUniqueOwned(Args&&... args) {
return WrapUnique<T>(NewOwned<T>(std::forward<Args>(args)...));
}
private:
/// @copydoc Allocator::Allocate
void* DoAllocate(Layout layout) override;
/// @copydoc Allocator::Deallocate
void DoDeallocate(void*) override;
ByteSpan remaining_;
internal::GenericOwned* owned_ = nullptr;
};
} // namespace pw::allocator