blob: 236c85720590b736549b5cce6644bb7d0aca8906 [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 { return DoGetCapacity(); }
/// 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);
}
/// Returns whether a given memory allocation was provided by this reource.
///
/// This method MUST only be used to dispatch between two or more objects
/// assoicated 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.
///
/// 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.
///
/// @param[in] deallocator The object that provided ``ptr``.
/// @param[in] ptr The pointer to be queried.
/// @param[in] layout Describes the memory pointed at by `ptr`.
///
/// @returns @rst
///
/// .. pw-status-codes::
///
/// OK: This object can re/deallocate the pointer.
///
/// OUT_OF_RANGE: Pointer cannot be re/deallocated by this object.
///
/// UNIMPLEMENTED: This object cannot recognize allocated pointers.
///
/// @endrst
static Status Query(const Deallocator& deallocator, const void* ptr) {
return deallocator.DoQuery(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 `GetCapacity` function that can be overridden by derived classes.
virtual StatusWithSize DoGetCapacity() const;
/// Virtual `Query` function that can be overridden by derived classes.
virtual Status DoQuery(const void*) const;
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