blob: 2879a407676f72a18dbe73c1cef78dc93c23adb4 [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 "pw_allocator/capability.h"
#include "pw_allocator/layout.h"
#include "pw_allocator/unique_ptr.h"
#include "pw_result/result.h"
#include "pw_status/status.h"
#include "pw_status/status_with_size.h"
namespace pw {
/// Abstract interface for releasing memory.
class Deallocator {
public:
using Capabilities = allocator::Capabilities;
using Capability = allocator::Capability;
using Layout = allocator::Layout;
virtual ~Deallocator() = default;
const Capabilities& capabilities() const { return capabilities_; }
/// Returns whether a given capabilityis enabled for this object.
bool HasCapability(Capability capability) const {
return capabilities_.has(capability);
}
/// Releases a previously-allocated block of memory.
///
/// The given pointer must have been previously provided by this memory
/// resource; otherwise the behavior is undefined.
///
/// @param[in] ptr Pointer to previously-allocated memory.
void Deallocate(void* ptr) {
if (ptr != nullptr) {
DoDeallocate(ptr);
}
}
/// Deprecated version of `Deallocate` that takes a `Layout`.
/// Do not use this method. It will be removed.
/// TODO(b/326509341): Remove when downstream consumers migrate.
void Deallocate(void* ptr, Layout layout) {
if (ptr != nullptr) {
DoDeallocate(ptr, layout);
}
}
/// Destroys the object at ``ptr`` and deallocates the associated memory.
///
/// The given pointer must have been previously obtained from a call to
/// ``New`` using the same object; otherwise the behavior is undefined.
///
/// @param[in] ptr Pointer to previously-allocated object.
template <typename T>
void Delete(T* ptr) {
if (!capabilities_.has(Capability::kSkipsDestroy)) {
std::destroy_at(ptr);
}
Deallocate(ptr);
}
/// Returns the total amount of memory provided by this object.
///
/// This is an optional method. Some memory providers may not have an easily
/// defined capacity, e.g. the system allocator. If implemented, the returned
/// capacity may be less than the memory originally given to an allocator,
/// e.g. if the allocator must align the region of memory, its capacity may be
/// reduced.
StatusWithSize GetCapacity() const {
auto result = DoGetInfo(InfoType::kCapacity, nullptr);
return StatusWithSize(result.status(), Layout::Unwrap(result).size());
}
/// Returns whether the given object is the same as this one.
///
/// This method is used instead of ``operator==`` in keeping with
/// ``std::pmr::memory_resource::is_equal``. There currently is no
/// corresponding virtual ``DoIsEqual``, as objects that would
/// require ``dynamic_cast`` to properly determine equality are not supported.
/// This method will allow the interface to remain unchanged should a future
/// need for such objects arise.
///
/// @param[in] other Object to compare with this object.
bool IsEqual(const Deallocator& other) const { return this == &other; }
protected:
/// TODO(b/326509341): Remove when downstream consumers migrate.
constexpr Deallocator() = default;
explicit constexpr Deallocator(const Capabilities& capabilities)
: capabilities_(capabilities) {}
/// Wraps an object of type ``T`` in a ``UniquePtr``
///
/// @param[in] ptr Pointer to memory provided by this object.
template <typename T>
[[nodiscard]] UniquePtr<T> WrapUnique(T* ptr) {
return UniquePtr<T>(UniquePtr<T>::kPrivateConstructor, ptr, this);
}
/// Wraps an array of type ``T`` in a ``UniquePtr``
///
/// @param[in] ptr Pointer to memory provided by this object.
/// @param[in] size The size of the array.
template <typename T>
[[nodiscard]] UniquePtr<T[]> WrapUniqueArray(T* ptr, size_t size) {
return UniquePtr<T[]>(UniquePtr<T[]>::kPrivateConstructor, ptr, this, size);
}
/// Indicates what kind of information to retrieve using `GetInfo`.
///
/// Note that this enum is considered open, and may be extended in the future.
/// As a result, implementers of `DoGetInfo` should include a default case
/// that handles unrecognized info types. If building with `-Wswitch-enum`,
/// you will also want to locally disable that diagnostic and build with
/// `-Wswitch-default` instead, e.g.:
///
/// @code{.cpp}
/// class MyAllocator : public Allocator {
/// private:
/// Layout MyGetLayoutFromPointer(const void* ptr) { /* ... */ }
///
/// Result<Layout> DoGetInfo(InfoType info_type, const void* ptr) override {
/// PW_MODIFY_DIAGNOSTICS_PUSH();
/// PW_MODIFY_DIAGNOSTIC(ignored, "-Wswitch-enum");
/// switch(info_type) {
/// case InfoType::kAllocatedLayoutOf:
/// return MyGetLayoutFromPointer(ptr);
/// default:
/// return Status::Unimplmented();
/// }
/// PW_MODIFY_DIAGNOSTICS_POP();
/// }
/// };
/// @endcode
///
/// See also `GetInfo`.
enum class InfoType {
/// If supported, `GetInfo` will return `OK` with the `Layout` of the
/// requested memory associated with the given pointer, or `NOT_FOUND` if
/// the pointer is not recognized.
///
/// The requested layout may differ from either the layout of usable memory,
/// the layout of memory used to fulfill the request, or both.
///
/// For example, it may have a smaller size than the usable memory if the
/// latter was padded to an alignment boundary, or may have a less strict
/// alignment than the actual memory.
kRequestedLayoutOf,
/// If supported, `GetInfo` will return `OK` with the `Layout` of the
/// usable memory associated with the given pointer, or `NOT_FOUND` if
/// the pointer is not recognized.
/// The usable layout may from either the requested layout, the layout of
/// memory used to fulfill the request, or both.
///
/// For example, it may have a larger size than the requested layout if it
/// was padded to an alignment boundary, but may be less than the acutal
/// memory if the object includes some overhead for metadata.
kUsableLayoutOf,
/// If supported, `GetInfo` will return `OK` with the `Layout` of the
/// allocated memory associated with the given pointer, or `NOT_FOUND` if
/// the pointer is not recognized.
///
/// The layout of memory used to fulfill a request may differ from either
/// the requested layout, the layout of the usable memory, or both.
///
/// For example, it may have a larger size than the requested layout or the
/// layout of usable memory if the object includes some overhead for
/// metadata.
kAllocatedLayoutOf,
/// If supported, `GetInfo` will return `OK` with a `Layout` whose size
/// is the total number of bytes that can be allocated by this object, and
/// whose alignment is the minimum alignment of any allocation.
///
/// The given pointer is ignored.
kCapacity,
/// If supported, `GetInfo` will return `OK` with a default `Layout` if the
/// given pointer was provided by this object, or `NOT_FOUND`.
///
/// This MUST only be used to dispatch between two or more objects
/// associated with non-overlapping regions of memory. Do NOT use it to
/// determine if this object can deallocate pointers. Callers MUST only
/// deallocate memory using the same ``Deallocator`` that provided it.
kRecognizes,
};
/// Returns deallocator-specific information about allocations.
///
/// Deallocators may support any number of `InfoType`s. See that type for what
/// each supported type returns. For unsupported types, this method returns
/// `UNIMPLEMENTED`.
Result<Layout> GetInfo(InfoType info_type, const void* ptr) const {
return DoGetInfo(info_type, ptr);
}
/// @copydoc GetInfo
///
/// This method is protected in order to restrict it to object
/// implementations. It is static and takes an ``deallocator`` parameter in
/// order to allow forwarding allocators to call it on wrapped allocators.
static Result<Layout> GetInfo(const Deallocator& deallocator,
InfoType info_type,
const void* ptr) {
return deallocator.DoGetInfo(info_type, ptr);
}
/// Convenience wrapper of `DoGetInfo` for getting the requested layout
/// associated with a pointer.
Result<Layout> GetRequestedLayout(const void* ptr) const {
return DoGetInfo(InfoType::kRequestedLayoutOf, ptr);
}
/// Static version of `GetRequestedLayout` that allows forwarding allocators
/// to call it on wrapped allocators.
static Result<Layout> GetRequestedLayout(const Deallocator& deallocator,
const void* ptr) {
return deallocator.GetRequestedLayout(ptr);
}
/// Convenience wrapper of `DoGetInfo` for getting the usable layout
/// associated with a pointer.
Result<Layout> GetUsableLayout(const void* ptr) const {
return DoGetInfo(InfoType::kUsableLayoutOf, ptr);
}
/// Static version of `GetUsableLayout` that allows forwarding allocators to
/// call it on wrapped allocators.
static Result<Layout> GetUsableLayout(const Deallocator& deallocator,
const void* ptr) {
return deallocator.GetUsableLayout(ptr);
}
/// Convenience wrapper of `DoGetInfo` for getting the allocated layout
/// associated with a pointer.
Result<Layout> GetAllocatedLayout(const void* ptr) const {
return DoGetInfo(InfoType::kAllocatedLayoutOf, ptr);
}
/// Static version of `GetAllocatedLayout` that allows forwarding allocators
/// to call it on wrapped allocators.
static Result<Layout> GetAllocatedLayout(const Deallocator& deallocator,
const void* ptr) {
return deallocator.GetAllocatedLayout(ptr);
}
/// Convenience wrapper of `DoGetInfo` for getting whether the allocator
/// recognizes a pointer.
bool Recognizes(const void* ptr) const {
return DoGetInfo(InfoType::kRecognizes, ptr).ok();
}
/// Static version of `Recognizes` that allows forwarding allocators to call
/// it on wrapped allocators.
static bool Recognizes(const Deallocator& deallocator, const void* ptr) {
return deallocator.Recognizes(ptr);
}
private:
/// Virtual `Deallocate` function implemented by derived classes.
///
/// @param[in] ptr Pointer to memory, guaranteed to not be null.
virtual void DoDeallocate(void*) {
// This method will be pure virtual once consumer migrate from the deprected
// version that takes a `Layout` parameter. In the meantime, the check that
// this method is implemented is deferred to run-time.
PW_ASSERT(false);
}
/// Deprecated version of `DoDeallocate` that takes a `Layout`.
/// Do not use this method. It will be removed.
virtual void DoDeallocate(void* ptr, Layout) { DoDeallocate(ptr); }
/// Virtual `GetInfo` function that can be overridden by derived classes.
virtual Result<Layout> DoGetInfo(InfoType, const void*) const {
return Status::Unimplemented();
}
const Capabilities capabilities_;
};
namespace allocator {
// Alias for module consumers using the older name for the above type.
using Deallocator = ::pw::Deallocator;
} // namespace allocator
} // namespace pw