/*
 * Copyright (C) 2020-2021 Aloysius Indrayanto
 *
 * This file is part of the eCxx library, see LICENSE file for the license details.
 */

/*
 * ----------------------------------------------------------------------------------
 * The code in this file is copied (with a slight modifications) from:
 *     abi.cpp
 * ----------------------------------------------------------------------------------
 * Copyright (C) 2014 Arduino. All right reserved.
 *
 * This library is free software; you can redistribute it and/or modify it under the
 * terms of the GNU Lesser General Public License as published by the Free Software
 * Foundation; either version 2.1 of the License, or (at your option) any later
 * version.
 *
 * This library is distributed in the hope that it will be useful, but WITHOUT ANY
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
 * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License along
 * with this library; if not, write to the Free Software Foundation, Inc., 51
 * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
 * ----------------------------------------------------------------------------------
 */


#include <cxxabi.h>

#include "CoreSys.h"


//
// External variables from "SysHeap.cpp"
//
extern void*         umm_last_fail_alloc_addr;
extern eCxx::int32_t umm_last_fail_alloc_size;


//
// C++ portability helper functions
//
extern "C" {


    void __cxa_pure_virtual() __ECXX_ATTR_NORETURN;
    void __cxa_pure_virtual()
    { panic(); }

    void __cxa_deleted_virtual() __ECXX_ATTR_NORETURN;
    void __cxa_deleted_virtual()
    { panic(); }


    struct Guard {
        uint8_t guard;
        uint8_t ps;
    };

    int __cxa_guard_acquire(__cxxabiv1::__guard* pg)
    {
        const uint8_t ps = xt_rsil(15);

        if(reinterpret_cast<Guard*>(pg)->guard) {
            xt_wsr_ps(ps);
            return 0;
        }

        reinterpret_cast<Guard*>(pg)->ps = ps;

        return 1;
    }

    void __cxa_guard_release(__cxxabiv1::__guard* pg)
    {
        reinterpret_cast<Guard*>(pg)->guard = 1;

        xt_wsr_ps(reinterpret_cast<Guard*>(pg)->ps);
    }

    void __cxa_guard_abort(__cxxabiv1::__guard* pg)
    { xt_wsr_ps(reinterpret_cast<Guard*>(pg)->ps); }


} // extern "C"


//
// Implementations of operator new that return nullptr on OOM
//
#if !defined(__cpp_exceptions)


void* operator new(size_t size) noexcept
{
    void* ret = malloc(size);

    if(size && !ret) {
        umm_last_fail_alloc_addr = __builtin_return_address(0);
        umm_last_fail_alloc_size = size;
        __unhandled_exception( PGM_STR("OOM").p );
    }

    return ret;
}


void* operator new[](size_t size) noexcept
{
    void* ret = malloc(size);

    if(size && !ret) {
        umm_last_fail_alloc_addr = __builtin_return_address(0);
        umm_last_fail_alloc_size = size;
        __unhandled_exception( PGM_STR("OOM").p );
    }

    return ret;
}


void* operator new (size_t size, const eCxx::nothrow_t&)
{
    void* ret = malloc(size);

    if(size && !ret) {
        umm_last_fail_alloc_addr = __builtin_return_address(0);
        umm_last_fail_alloc_size = size;
    }

    return ret;
}


void* operator new[] (size_t size, const eCxx::nothrow_t&)
{
    void* ret = malloc(size);

    if(size && !ret) {
        umm_last_fail_alloc_addr = __builtin_return_address(0);
        umm_last_fail_alloc_size = size;
    }

    return ret;
}


#endif // #if !defined(NEW_OOM_ABORT) ...
