/*
 * Copyright (C) 2024-2025 Apple Inc. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1.  Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 * 2.  Redistributions in binary form must reproduce the above copyright
 *     notice, this list of conditions and the following disclaimer in the
 *     documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#pragma once

#include <cstdlib>
#include <new>
#include <wtf/DebugHeap.h>
#include <wtf/FastMalloc.h>
#include <wtf/ForbidHeapAllocation.h>
#include <wtf/Platform.h>
#include <wtf/StdLibExtras.h>

#if USE(PROTECTED_JIT)

namespace WTF {

// Arena Allocator

#if ASSERT_ENABLED
WTF_EXPORT_PRIVATE void sequesteredArenaSetMaxSingleAllocationSize(size_t);
#endif

WTF_EXPORT_PRIVATE bool isSequesteredArenaMallocEnabled();

WTF_EXPORT_PRIVATE void* sequesteredArenaMalloc(size_t) RETURNS_NONNULL;
WTF_EXPORT_PRIVATE void* sequesteredArenaZeroedMalloc(size_t) RETURNS_NONNULL;
WTF_EXPORT_PRIVATE void* sequesteredArenaCalloc(size_t numElements, size_t elementSize) RETURNS_NONNULL;
WTF_EXPORT_PRIVATE void* sequesteredArenaRealloc(void*, size_t) RETURNS_NONNULL;

WTF_EXPORT_PRIVATE TryMallocReturnValue trySequesteredArenaMalloc(size_t);
WTF_EXPORT_PRIVATE TryMallocReturnValue trySequesteredArenaZeroedMalloc(size_t);
WTF_EXPORT_PRIVATE TryMallocReturnValue trySequesteredArenaCalloc(size_t numElements, size_t elementSize);
WTF_EXPORT_PRIVATE TryMallocReturnValue trySequesteredArenaRealloc(void*, size_t);

WTF_EXPORT_PRIVATE void sequesteredArenaFree(void*);

// Allocations from sequesteredArenaAlignedMalloc() must be freed using sequesteredArenaAlignedFree().
WTF_EXPORT_PRIVATE void* sequesteredArenaAlignedMalloc(size_t alignment, size_t) RETURNS_NONNULL;
WTF_EXPORT_PRIVATE void* trySequesteredArenaAlignedMalloc(size_t alignment, size_t);
WTF_EXPORT_PRIVATE void sequesteredArenaAlignedFree(void*);

struct SequesteredArenaMallocStatistics { /* FIXME: add statistics */ };
WTF_EXPORT_PRIVATE SequesteredArenaMallocStatistics sequesteredArenaMallocStatistics();

WTF_EXPORT_PRIVATE void sequesteredArenaMallocDumpMallocStats();

template<typename T>
class SaAllocator {
public:
    using value_type = T;

    SaAllocator() = default;

    template<typename U> SaAllocator(const SaAllocator<U>&) { }

    T* allocate(size_t count)
    {
        return static_cast<T*>(sequesteredArenaMalloc(sizeof(T) * count));
    }

    void deallocate(T* pointer, size_t)
    {
        sequesteredArenaFree(pointer);
    }

    template <typename U>
    struct rebind {
        using other = SaAllocator<U>;
    };
};

template<typename T, typename U> inline bool operator==(const SaAllocator<T>&, const SaAllocator<U>&) { return true; }

struct SequesteredArenaMalloc {
    static void* malloc(size_t size) { return sequesteredArenaMalloc(size); }

    static void* tryMalloc(size_t size)
    {
        auto result = trySequesteredArenaMalloc(size);
        void* realResult;
        if (result.getValue(realResult))
            return realResult;
        return nullptr;
    }

    static void* zeroedMalloc(size_t size) { return sequesteredArenaZeroedMalloc(size); }

    static void* tryZeroedMalloc(size_t size)
    {
        auto result = trySequesteredArenaZeroedMalloc(size);
        void* realResult;
        if (result.getValue(realResult))
            return realResult;
        return nullptr;
    }

    static void* realloc(void* p, size_t size) { return sequesteredArenaRealloc(p, size); }

    static void* tryRealloc(void* p, size_t size)
    {
        auto result = trySequesteredArenaRealloc(p, size);
        void* realResult;
        if (result.getValue(realResult))
            return realResult;
        return nullptr;
    }

    static void free(void* p) { sequesteredArenaFree(p); }

    static constexpr ALWAYS_INLINE size_t nextCapacity(size_t capacity)
    {
        return capacity + capacity / 4 + 1;
    }
};

template<typename T>
struct SaFree {
    static_assert(std::is_trivially_destructible<T>::value);

    void operator()(T* pointer) const
    {
        sequesteredArenaFree(const_cast<typename std::remove_cv<T>::type*>(pointer));
    }
};

}

using WTF::SaAllocator;
using WTF::SequesteredArenaMalloc;
using WTF::SaFree;
using WTF::ForbidMallocUseForCurrentThreadScope;
using WTF::isSequesteredArenaMallocEnabled;
using WTF::sequesteredArenaCalloc;
using WTF::sequesteredArenaFree;
using WTF::sequesteredArenaMalloc;
using WTF::sequesteredArenaRealloc;
using WTF::sequesteredArenaZeroedMalloc;
using WTF::trySequesteredArenaAlignedMalloc;
using WTF::trySequesteredArenaCalloc;
using WTF::trySequesteredArenaMalloc;
using WTF::trySequesteredArenaZeroedMalloc;
using WTF::sequesteredArenaAlignedMalloc;
using WTF::sequesteredArenaAlignedFree;

#include <wtf/SequesteredAllocator.h>
#include <wtf/SequesteredImmortalHeap.h>

#define WTF_MAKE_SEQUESTERED_ARENA_ALLOCATED_COMMON \
    void* operator new(size_t, void* p) { return p; } \
    void* operator new[](size_t, void* p) { return p; } \
    \
    void* operator new(size_t size) \
    { \
        return ::WTF::sequesteredArenaMalloc(size); \
    } \
    \
    void operator delete(void* p) \
    { \
        ::WTF::sequesteredArenaFree(p); \
    } \
    \
    void* operator new[](size_t size) \
    { \
        return ::WTF::sequesteredArenaMalloc(size); \
    } \
    \
    void operator delete[](void* p) \
    { \
        ::WTF::sequesteredArenaFree(p); \
    } \
    void* operator new(size_t, NotNullTag, void* location) \
    { \
        ASSERT(location); \
        return location; \
    } \
    static void freeAfterDestruction(void* p) \
    { \
        ::WTF::sequesteredArenaFree(p); \
    } \
    using WTFIsSaAllocated = int; \

#define WTF_MAKE_SEQUESTERED_ARENA_NONALLOCATABLE(name) WTF_FORBID_HEAP_ALLOCATION

#define WTF_MAKE_SEQUESTERED_ARENA_ALLOCATED(name) \
public: \
    WTF_MAKE_SEQUESTERED_ARENA_ALLOCATED_COMMON \
private: \
using __thisIsHereToForceASemicolonAfterThisMacro UNUSED_TYPE_ALIAS = int

#define WTF_MAKE_SEQUESTERED_ARENA_ALLOCATED_FALLBACK_FAST_ALLOCATED(name) \
    WTF_MAKE_SEQUESTERED_ARENA_ALLOCATED(name)

#define WTF_MAKE_STRUCT_SEQUESTERED_ARENA_ALLOCATED(name) \
    WTF_MAKE_SEQUESTERED_ARENA_ALLOCATED_COMMON \
using __thisIsHereToForceASemicolonAfterThisMacro UNUSED_TYPE_ALIAS = int

#define WTF_MAKE_STRUCT_SEQUESTERED_ARENA_ALLOCATED_FALLBACK_FAST_ALLOCATED(name) \
    WTF_MAKE_STRUCT_SEQUESTERED_ARENA_ALLOCATED(name)

#define WTF_MAKE_COMPACT_SEQUESTERED_ARENA_ALLOCATED(name) \
public: \
    WTF_MAKE_COMPACT_SEQUESTERED_ARENA_ALLOCATED_COMMON \
private: \
using __thisIsHereToForceASemicolonAfterThisMacro UNUSED_TYPE_ALIAS = int

#define WTF_MAKE_COMPACT_SEQUESTERED_ARENA_ALLOCATED_EXPORT(name, exportMacro) \
    WTF_MAKE_COMPACT_SEQUESTERED_ARENA_ALLOCATED(name)

#define WTF_MAKE_SEQUESTERED_ARENA_ALLOCATED_EXPORT(name, exportMacro) \
    WTF_MAKE_SEQUESTERED_ARENA_ALLOCATED(name)

#define WTF_MAKE_STRUCT_COMPACT_SEQUESTERED_ARENA_ALLOCATED(name) \
    WTF_MAKE_COMPACT_SEQUESTERED_ARENA_ALLOCATED_COMMON \
using __thisIsHereToForceASemicolonAfterThisMacro UNUSED_TYPE_ALIAS = int

#define WTF_MAKE_SEQUESTERED_ARENA_ALLOCATED_TEMPLATE(name) WTF_MAKE_SEQUESTERED_ARENA_ALLOCATED(name)
#define WTF_MAKE_SEQUESTERED_ARENA_ALLOCATED_TEMPLATE_IMPL(_templateParameters, _type)

#define WTF_MAKE_SEQUESTERED_ARENA_ALLOCATED_TEMPLATE_IMPL_WITH_MULTIPLE_OR_SPECIALIZED_PARAMETERS()

#define WTF_MAKE_SEQUESTERED_ARENA_ALLOCATED_IMPL(type)

// Immortal Allocator

// This is not a proper malloc implementation as it effectively leaks all
// allocations, and does not support many of the variants, so much of the
// machinery used in the other mallocs is intentionally left out

namespace WTF {
WTF_EXPORT_PRIVATE void* sequesteredImmortalMalloc(size_t) RETURNS_NONNULL;
WTF_EXPORT_PRIVATE void* sequesteredImmortalAlignedMalloc(size_t alignment, size_t) RETURNS_NONNULL;
}
using WTF::sequesteredImmortalMalloc;
using WTF::sequesteredImmortalAlignedMalloc;

#define WTF_MAKE_SEQUESTERED_IMMORTAL_ALLOCATED_COMMON \
    void* operator new(size_t, void* p) { return p; } \
    void* operator new[](size_t, void* p) { return p; } \
    \
    void* operator new(size_t size) \
    { \
        return ::WTF::sequesteredImmortalMalloc(size); \
    } \
    \
    void operator delete(void*) { } \
    \
    void* operator new[](size_t size) \
    { \
        return ::WTF::sequesteredImmortalMalloc(size); \
    } \
    \
    void operator delete[](void*) \
    { \
    } \
    void* operator new(size_t, NotNullTag, void* location) \
    { \
        ASSERT(location); \
        return location; \
    } \
    static void freeAfterDestruction(void*) { } \
    using WTFIsSiAllocated = int; \

#define WTF_MAKE_SEQUESTERED_IMMORTAL_ALLOCATED(type) \
public: \
    WTF_MAKE_SEQUESTERED_IMMORTAL_ALLOCATED_COMMON \
private: \
using __thisIsHereToForceASemicolonAfterThisMacro UNUSED_TYPE_ALIAS = int

#define WTF_MAKE_STRUCT_SEQUESTERED_IMMORTAL_ALLOCATED(type) \
    WTF_MAKE_SEQUESTERED_IMMORTAL_ALLOCATED_COMMON \
using __thisIsHereToForceASemicolonAfterThisMacro UNUSED_TYPE_ALIAS = int

#else // !USE(PROTECTED_JIT)

// Arena Allocator

namespace WTF {
using SequesteredArenaMalloc = FastMalloc;
}

using WTF::SequesteredArenaMalloc;

#define WTF_MAKE_SEQUESTERED_ARENA_NONALLOCATABLE(name) WTF_FORBID_HEAP_ALLOCATION

#define WTF_MAKE_SEQUESTERED_ARENA_ALLOCATED(name) WTF_MAKE_TZONE_ALLOCATED(name)
#define WTF_MAKE_SEQUESTERED_ARENA_ALLOCATED_FALLBACK_FAST_ALLOCATED(name) WTF_DEPRECATED_MAKE_FAST_ALLOCATED(name)
#define WTF_MAKE_SEQUESTERED_ARENA_ALLOCATED_EXPORT(name, exportMacro) WTF_MAKE_TZONE_ALLOCATED_EXPORT(name, exportMacro)

#define WTF_MAKE_COMPACT_SEQUESTERED_ARENA_ALLOCATED(name) WTF_MAKE_COMPACT_TZONE_ALLOCATED(name)
#define WTF_MAKE_COMPACT_SEQUESTERED_ARENA_ALLOCATED_EXPORT(name, exportMacro) WTF_MAKE_COMPACT_TZONE_ALLOCATED_EXPORT(name, exportMacro)

#define WTF_MAKE_STRUCT_SEQUESTERED_ARENA_ALLOCATED(name) WTF_MAKE_STRUCT_TZONE_ALLOCATED(name)
#define WTF_MAKE_STRUCT_SEQUESTERED_ARENA_ALLOCATED_FALLBACK_FAST_ALLOCATED(name) WTF_DEPRECATED_MAKE_FAST_ALLOCATED(name)

#define WTF_MAKE_SEQUESTERED_ARENA_ALLOCATED_IMPL(type) WTF_MAKE_TZONE_ALLOCATED_IMPL(type)

#define WTF_MAKE_SEQUESTERED_ARENA_ALLOCATED_TEMPLATE(name) WTF_MAKE_TZONE_ALLOCATED_TEMPLATE(name)
#define WTF_MAKE_SEQUESTERED_ARENA_ALLOCATED_TEMPLATE_IMPL(_templateParameters, _type) WTF_MAKE_TZONE_ALLOCATED_TEMPLATE_IMPL(_templateParameters, _type)
#define WTF_MAKE_SEQUESTERED_ARENA_ALLOCATED_TEMPLATE_IMPL_WITH_MULTIPLE_OR_SPECIALIZED_PARAMETERS() \
    WTF_MAKE_TZONE_ALLOCATED_TEMPLATE_IMPL_WITH_MULTIPLE_OR_SPECIALIZED_PARAMETERS()

// Immortal Allocator

#define WTF_MAKE_SEQUESTERED_IMMORTAL_ALLOCATED(type) WTF_DEPRECATED_MAKE_FAST_ALLOCATED(type)
#define WTF_MAKE_STRUCT_SEQUESTERED_IMMORTAL_ALLOCATED(type) WTF_DEPRECATED_MAKE_FAST_ALLOCATED(type)

#endif // !USE(PROTECTED_JIT)
