/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#ifndef mozilla_mozalloc_h
#define mozilla_mozalloc_h

/*
 * https://bugzilla.mozilla.org/show_bug.cgi?id=427099
 */

#if defined(__cplusplus)
#  include <new>
// Since libstdc++ 6, including the C headers (e.g. stdlib.h) instead of the
// corresponding C++ header (e.g. cstdlib) can cause confusion in C++ code
// using things defined there. Specifically, with stdlib.h, the use of abs()
// in gfx/graphite2/src/inc/UtfCodec.h somehow ends up picking the wrong abs()
#  include <cstdlib>
#  include <cstring>
#else
#  include <stdlib.h>
#  include <string.h>
#endif

#if defined(__cplusplus)
#include "mozilla/fallible.h"
#include "mozilla/mozalloc_abort.h"
#include "mozilla/TemplateLib.h"
#endif
#include "mozilla/Attributes.h"
#include "mozilla/Types.h"

#if defined(MOZ_MIMALLOC) && !defined(MOZ_GLUE_IN_PROGRAM)
#  include "mimalloc.h"
#endif

#define MOZALLOC_HAVE_XMALLOC

#if defined(__cplusplus)
extern "C" {
#endif /* ifdef __cplusplus */

/*
 * We need to use malloc_impl and free_impl in this file when they are
 * defined, because of how mozglue.dll is linked on Windows, where using
 * malloc/free would end up using the symbols from the MSVCRT instead of
 * ours.
 */
#ifndef free_impl
#  if defined(MOZ_MIMALLOC) && !defined(MOZ_GLUE_IN_PROGRAM)
#    define free_impl mi_free
#  else
#    define free_impl free
#  endif
#  define free_impl_
#endif
#ifndef malloc_impl
#  if defined(MOZ_MIMALLOC) && !defined(MOZ_GLUE_IN_PROGRAM)
#    define malloc_impl mi_malloc
#  else
#    define malloc_impl malloc
#  endif
#  define malloc_impl_
#endif

/*
 * Each declaration below is analogous to a "standard" allocation
 * function, except that the out-of-memory handling is made explicit.
 * The |moz_x| versions will never return a NULL pointer; if memory
 * is exhausted, they abort.  The |moz_| versions may return NULL
 * pointers if memory is exhausted: their return value must be checked.
 *
 * All these allocation functions are *guaranteed* to return a pointer
 * to memory allocated in such a way that that memory can be freed by
 * passing that pointer to |free()|.
 */

MFBT_API void* moz_xmalloc(size_t size)
    MOZ_ALLOCATOR;

MFBT_API void* moz_xcalloc(size_t nmemb, size_t size)
    MOZ_ALLOCATOR;

MFBT_API void* moz_xrealloc(void* ptr, size_t size)
    MOZ_ALLOCATOR;

MFBT_API char* moz_xstrdup(const char* str)
    MOZ_ALLOCATOR;

MFBT_API size_t moz_malloc_usable_size(void *ptr);

MFBT_API size_t moz_malloc_size_of(const void *ptr);

#if defined(HAVE_STRNDUP)
MFBT_API char* moz_xstrndup(const char* str, size_t strsize)
    MOZ_ALLOCATOR;
#endif /* if defined(HAVE_STRNDUP) */


#if defined(HAVE_POSIX_MEMALIGN)
MFBT_API __attribute__ ((warn_unused_result))
int moz_posix_memalign(void **ptr, size_t alignment, size_t size);
#endif /* if defined(HAVE_POSIX_MEMALIGN) */


#if defined(HAVE_MEMALIGN)
MFBT_API void* moz_xmemalign(size_t boundary, size_t size)
    MOZ_ALLOCATOR;
#endif /* if defined(HAVE_MEMALIGN) */


#if defined(HAVE_VALLOC)
MFBT_API void* moz_xvalloc(size_t size)
    MOZ_ALLOCATOR;
#endif /* if defined(HAVE_VALLOC) */


#ifdef __cplusplus
} /* extern "C" */
#endif /* ifdef __cplusplus */


#ifdef __cplusplus

/*
 * We implement the default operators new/delete as part of
 * libmozalloc, replacing their definitions in libstdc++.  The
 * operator new* definitions in libmozalloc will never return a NULL
 * pointer.
 *
 * Each operator new immediately below returns a pointer to memory
 * that can be delete'd by any of
 *
 *   (1) the matching infallible operator delete immediately below
 *   (2) the matching "fallible" operator delete further below
 *   (3) the matching system |operator delete(void*, std::nothrow)|
 *   (4) the matching system |operator delete(void*) throw(std::bad_alloc)|
 *
 * NB: these are declared |throw(std::bad_alloc)|, though they will never
 * throw that exception.  This declaration is consistent with the rule
 * that |::operator new() throw(std::bad_alloc)| will never return NULL.
 */

/* NB: This is defined just to silence vacuous warnings about symbol
 * visibility on OS X/gcc. These symbols are force-inline and not
 * exported. */
#if defined(XP_MACOSX)
#  define MOZALLOC_EXPORT_NEW MFBT_API
#else
#  define MOZALLOC_EXPORT_NEW
#endif

#if defined(_MSC_VER)
/*
 * Suppress build warning spam (issue #2281).
 */
#define MOZALLOC_THROW_IF_HAS_EXCEPTIONS noexcept(true)
#define MOZALLOC_THROW_BAD_ALLOC_IF_HAS_EXCEPTIONS noexcept(false)
#elif __cplusplus >= 201103
#define MOZALLOC_THROW_IF_HAS_EXCEPTIONS noexcept(true)
#define MOZALLOC_THROW_BAD_ALLOC_IF_HAS_EXCEPTIONS noexcept(false)
#else
#define MOZALLOC_THROW_IF_HAS_EXCEPTIONS throw()
#define MOZALLOC_THROW_BAD_ALLOC_IF_HAS_EXCEPTIONS throw(std::bad_alloc)
#endif

#define MOZALLOC_THROW_BAD_ALLOC MOZALLOC_THROW_BAD_ALLOC_IF_HAS_EXCEPTIONS

MOZALLOC_EXPORT_NEW
#if defined(__GNUC__) && !defined(__clang__) && defined(__SANITIZE_ADDRESS__)
/* gcc's asan somehow doesn't like always_inline on this function. */
__attribute__((gnu_inline)) inline
#else
MOZ_ALWAYS_INLINE_EVEN_DEBUG
#endif
void* operator new(size_t size) MOZALLOC_THROW_BAD_ALLOC
{
    return moz_xmalloc(size);
}

MOZALLOC_EXPORT_NEW MOZ_ALWAYS_INLINE_EVEN_DEBUG
void* operator new(size_t size, const std::nothrow_t&) MOZALLOC_THROW_IF_HAS_EXCEPTIONS
{
    return malloc_impl(size);
}

MOZALLOC_EXPORT_NEW MOZ_ALWAYS_INLINE_EVEN_DEBUG
void* operator new[](size_t size) MOZALLOC_THROW_BAD_ALLOC
{
    return moz_xmalloc(size);
}

MOZALLOC_EXPORT_NEW MOZ_ALWAYS_INLINE_EVEN_DEBUG
void* operator new[](size_t size, const std::nothrow_t&) MOZALLOC_THROW_IF_HAS_EXCEPTIONS
{
    return malloc_impl(size);
}

MOZALLOC_EXPORT_NEW MOZ_ALWAYS_INLINE_EVEN_DEBUG
void operator delete(void* ptr) MOZALLOC_THROW_IF_HAS_EXCEPTIONS
{
    return free_impl(ptr);
}

MOZALLOC_EXPORT_NEW MOZ_ALWAYS_INLINE_EVEN_DEBUG
void operator delete(void* ptr, const std::nothrow_t&) MOZALLOC_THROW_IF_HAS_EXCEPTIONS
{
    return free_impl(ptr);
}

MOZALLOC_EXPORT_NEW MOZ_ALWAYS_INLINE_EVEN_DEBUG
void operator delete[](void* ptr) MOZALLOC_THROW_IF_HAS_EXCEPTIONS
{
    return free_impl(ptr);
}

MOZALLOC_EXPORT_NEW MOZ_ALWAYS_INLINE_EVEN_DEBUG
void operator delete[](void* ptr, const std::nothrow_t&) MOZALLOC_THROW_IF_HAS_EXCEPTIONS
{
    return free_impl(ptr);
}


/*
 * We also add a new allocator variant: "fallible operator new."
 * Unlike libmozalloc's implementations of the standard nofail
 * allocators, this allocator is allowed to return NULL.  It can be used
 * as follows
 *
 *   Foo* f = new (mozilla::fallible) Foo(...);
 *
 * operator delete(fallible) is defined for completeness only.
 *
 * Each operator new below returns a pointer to memory that can be
 * delete'd by any of
 *
 *   (1) the matching "fallible" operator delete below
 *   (2) the matching infallible operator delete above
 *   (3) the matching system |operator delete(void*, std::nothrow)|
 *   (4) the matching system |operator delete(void*) throw(std::bad_alloc)|
 */

MOZ_ALWAYS_INLINE_EVEN_DEBUG
void* operator new(size_t size, const mozilla::fallible_t&) MOZALLOC_THROW_IF_HAS_EXCEPTIONS
{
    return malloc_impl(size);
}

MOZ_ALWAYS_INLINE_EVEN_DEBUG
void* operator new[](size_t size, const mozilla::fallible_t&) MOZALLOC_THROW_IF_HAS_EXCEPTIONS
{
    return malloc_impl(size);
}

MOZ_ALWAYS_INLINE_EVEN_DEBUG
void operator delete(void* ptr, const mozilla::fallible_t&) MOZALLOC_THROW_IF_HAS_EXCEPTIONS
{
    free_impl(ptr);
}

MOZ_ALWAYS_INLINE_EVEN_DEBUG
void operator delete[](void* ptr, const mozilla::fallible_t&) MOZALLOC_THROW_IF_HAS_EXCEPTIONS
{
    free_impl(ptr);
}


/*
 * This policy is identical to MallocAllocPolicy, except it uses
 * moz_xmalloc/moz_xcalloc/moz_xrealloc instead of
 * malloc/calloc/realloc.
 */
class InfallibleAllocPolicy
{
public:
    template <typename T>
    T* maybe_pod_malloc(size_t aNumElems)
    {
        return pod_malloc<T>(aNumElems);
    }

    template <typename T>
    T* maybe_pod_calloc(size_t aNumElems)
    {
        return pod_calloc<T>(aNumElems);
    }

    template <typename T>
    T* maybe_pod_realloc(T* aPtr, size_t aOldSize, size_t aNewSize)
    {
        return pod_realloc<T>(aPtr, aOldSize, aNewSize);
    }

    template <typename T>
    T* pod_malloc(size_t aNumElems)
    {
        if (aNumElems & mozilla::tl::MulOverflowMask<sizeof(T)>::value) {
            reportAllocOverflow();
        }
        return static_cast<T*>(moz_xmalloc(aNumElems * sizeof(T)));
    }

    template <typename T>
    T* pod_calloc(size_t aNumElems)
    {
        return static_cast<T*>(moz_xcalloc(aNumElems, sizeof(T)));
    }

    template <typename T>
    T* pod_realloc(T* aPtr, size_t aOldSize, size_t aNewSize)
    {
        if (aNewSize & mozilla::tl::MulOverflowMask<sizeof(T)>::value) {
            reportAllocOverflow();
        }
        return static_cast<T*>(moz_xrealloc(aPtr, aNewSize * sizeof(T)));
    }

    void free_(void* aPtr)
    {
        free_impl(aPtr);
    }

    void reportAllocOverflow() const
    {
        mozalloc_abort("alloc overflow");
    }

    bool checkSimulatedOOM() const
    {
        return true;
    }
};

#endif  /* ifdef __cplusplus */

#ifdef malloc_impl_
#undef malloc_impl_
#undef malloc_impl
#endif
#ifdef free_impl_
#undef free_impl_
#undef free_impl
#endif

#endif /* ifndef mozilla_mozalloc_h */
