carb/Memory.h

File members: carb/Memory.h

// Copyright (c) 2021-2024, NVIDIA CORPORATION. All rights reserved.
//
// NVIDIA CORPORATION and its licensors retain all intellectual property
// and proprietary rights in and to this software, related documentation
// and any modifications thereto. Any use, reproduction, disclosure or
// distribution of this software and related documentation without an express
// license agreement from NVIDIA CORPORATION is strictly prohibited.
//

#pragma once

#include "Defines.h"
#include "Types.h"
#include "cpp/Bit.h"
#include "detail/DeferredLoad.h"

#if CARB_REQUIRE_LINKED
CARB_DYNAMICLINK void* carbReallocate(void* p, size_t size, size_t align);
#else
CARB_DYNAMICLINK void* carbReallocate(void* p, size_t size, size_t align) CARB_ATTRIBUTE(weak);
#endif

namespace carb
{
namespace detail
{

CARB_DETAIL_DEFINE_DEFERRED_LOAD(getCarbReallocate, carbReallocate, (void* (*)(void*, size_t, size_t)));

} // namespace detail

inline void* allocate(size_t size, size_t align = 0) noexcept
{
    if (auto impl = detail::getCarbReallocate())
        return impl(nullptr, size, align);
    else
        return nullptr;
}

inline void deallocate(void* p) noexcept
{
    if (p)
    {
        if (auto impl = detail::getCarbReallocate())
            impl(p, 0, 0);
    }
}

inline void* reallocate(void* p, size_t size, size_t align = 0) noexcept
{
    if (auto impl = detail::getCarbReallocate())
        return impl(p, size, align);
    else
        return p;
}

template <class T, class Align = std::integral_constant<size_t, 0>>
class Allocator
{
public:
    using pointer = T*;
    using const_pointer = const T*;
    using reference = T&;
    using const_reference = const T&;
    using void_pointer = void*;
    using const_void_pointer = const void*;
    using value_type = T;
    using size_type = std::size_t;
    using difference_type = std::ptrdiff_t;

    static_assert(!Align::value || ::carb::cpp::has_single_bit(Align::value), "Must be a power of two");
    constexpr static size_t alignment = Align::value;

    template <class U>
    struct rebind
    {
        using other = Allocator<U, Align>;
    };

    constexpr Allocator() noexcept = default;

    constexpr Allocator(const Allocator&) noexcept = default;

    constexpr Allocator& operator=(const Allocator&) noexcept = default;

    template <class U, class UAlign>
    constexpr Allocator(const Allocator<U, UAlign>& other) noexcept
    {
        CARB_UNUSED(other);
    }

    template <class U, class UAlign>
    constexpr Allocator& operator=(const Allocator<U, UAlign>& other) noexcept
    {
        CARB_UNUSED(other);
        return *this;
    }

    ~Allocator() = default;

    CARB_NODISCARD constexpr bool operator==(const Allocator& other) const noexcept
    {
        CARB_UNUSED(other);
        return true;
    }

    CARB_NODISCARD constexpr bool operator!=(const Allocator& other) const noexcept
    {
        CARB_UNUSED(other);
        return false;
    }

    CARB_NODISCARD pointer allocate(size_type n = 1)
    {
        auto align = ::carb_max(+alignment, alignof(T));
        auto p = ::carb::allocate(sizeof(T) * n, align);
#if CARB_EXCEPTIONS_ENABLED
        if (!p)
        {
            throw std::bad_alloc();
        }
#endif
        return pointer(p);
    }

    CARB_NODISCARD pointer allocate(size_type n, const_void_pointer p)
    {
        return p ? pointer(p) : allocate(n);
    }

    void deallocate(pointer p, size_type n) noexcept /*strengthened*/
    {
        CARB_UNUSED(n);
        ::carb::deallocate(p);
    }

    CARB_NODISCARD size_type max_size() const noexcept
    {
        return size_type(-1);
    }

    template <class X, class... Args>
    void construct(X* const p, Args&&... args)
    {
        ::new (const_cast<void*>(static_cast<const volatile void*>(p))) X(std::forward<Args>(args)...);
    }

    template <class X>
    void destroy(X* const p)
    {
        p->~X();
    }
};

template <size_t Align = 0>
class UseCarbAllocatorAligned
{
public:
    constexpr static size_t alignment = Align;

    void* operator new(std::size_t count)
    {
        return carb::allocate(count, alignment);
    }
    void* operator new[](std::size_t count)
    {
        return carb::allocate(count, alignment);
    }
    void operator delete(void* ptr)
    {
        carb::deallocate(ptr);
    }
    void operator delete[](void* ptr)
    {
        carb::deallocate(ptr);
    }

#if CARB_HAS_CPP17
    void* operator new(std::size_t count, std::align_val_t al)
    {
        return carb::allocate(count, ::carb_max(alignment, size_t(al)));
    }
    void* operator new[](std::size_t count, std::align_val_t al)
    {
        return carb::allocate(count, ::carb_max(alignment, size_t(al)));
    }
    void operator delete(void* ptr, std::align_val_t al)
    {
        CARB_UNUSED(al);
        carb::deallocate(ptr);
    }
    void operator delete[](void* ptr, std::align_val_t al)
    {
        CARB_UNUSED(al);
        carb::deallocate(ptr);
    }
#endif
};

template <class T>
class Deleter
{
public:
    void operator()(T* p) noexcept
    {
        p->~T();
        carb::deallocate(p);
    }
};

using UseCarbAllocator = UseCarbAllocatorAligned<>;

} // namespace carb