//========================================================================
//
//      malloc.cxx
//
//      Implementation of ISO C memory allocation routines, C++
//      operators, and heap-related debug facilities.
//
//========================================================================
// ####ECOSGPLCOPYRIGHTBEGIN####                                            
// -------------------------------------------                              
// This file is part of eCos, the Embedded Configurable Operating System.   
// Copyright (C) 1998, 1999, 2000, 2001, 2002, 2004, 2005 Free Software Foundation, Inc.
// Copyright (C) 2004, 2005 eCosCentric Limited                        
//
// eCos is free software; you can redistribute it and/or modify it under    
// the terms of the GNU General Public License as published by the Free     
// Software Foundation; either version 2 or (at your option) any later      
// version.                                                                 
//
// eCos 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 General Public License    
// for more details.                                                        
//
// You should have received a copy of the GNU General Public License        
// along with eCos; if not, write to the Free Software Foundation, Inc.,    
// 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.            
//
// As a special exception, if other files instantiate templates or use      
// macros or inline functions from this file, or you compile this file      
// and link it with other works to produce a work based on this file,       
// this file does not by itself cause the resulting work to be covered by   
// the GNU General Public License. However the source code for this file    
// must still be made available in accordance with section (3) of the GNU   
// General Public License v2.                                               
//
// This exception does not invalidate any other reasons why a work based    
// on this file might be covered by the GNU General Public License.         
// -------------------------------------------                              
// ####ECOSGPLCOPYRIGHTEND####                                              
//========================================================================
//#####DESCRIPTIONBEGIN####
//
// Author(s):     jlarmour
// Contributors:  bartv
// Date:          2000-04-30
// Purpose:       Provides ISO C calloc(), malloc(), realloc() and free()
//                functions, optionally C++ new and delete, and related
//                debug functionality.
// Description:   Implementation of ISO standard allocation routines as per
//                ISO C section 7.10.3
//
//####DESCRIPTIONEND####
//========================================================================

#include <pkgconf/system.h>        // Packages in configuration
#include <pkgconf/memalloc.h>      // Configuration header
#include <cyg/infra/cyg_type.h>    // Common type definitions and support
#include <cyg/infra/cyg_trac.h>    // Common tracing support
#include <cyg/infra/cyg_ass.h>     // Common assertion support
#include <string.h>                // For memset() and memmove()
#include <stdlib.h>                // header for this file
#include "memdebug.h"

// ----------------------------------------------------------------------------
// First, we need to identify the eCos memory pool which should be used
// as the system heap. Usually this is generated from linker script info
// by heapgen.tcl, resulting in an exported header <pkgconf/heaps.hxx>
// and a file heaps.cxx in the build tree. The latter provides one
// or more pools. File sysheap.cxx takes care of various complications.
// The pool objects live in a separate file to ensure that linker garbage
// collection works as desired.
//
// In some circumstances it may be desirable for another package, for
// example a platform HAL, to provide the system heap by other means.
#ifdef CYGBLD_MEMALLOC_MALLOC_EXTERNAL_HEAP_H
# include CYGBLD_MEMALLOC_MALLOC_EXTERNAL_HEAP_H
#else
# include <pkgconf/heaps.hxx>       // heap pools information
#endif
#include CYGBLD_MEMALLOC_MALLOC_IMPLEMENTATION_HEADER

// Now we can work out which pool should be used for the system heap.

#if CYGMEM_HEAP_COUNT == 0
// sysheap.cxx will provide a fallback
extern CYGCLS_MEMALLOC_MALLOC_IMPL cyg_memalloc_mallocpool;
# define POOL cyg_memalloc_mallocpool

#elif CYGMEM_HEAP_COUNT == 1
// heaps.cxx provides a single pool
# define POOL (*cygmem_memalloc_heaps[0])

#else 
// heaps.cxx provides multiple pools. sysheap.cxx combines them.
# ifdef CYGBLD_MEMALLOC_MALLOC_EXTERNAL_JOIN_H
#  include CYGBLD_MEMALLOC_MALLOC_EXTERNAL_JOIN_H
# else
#  include <cyg/memalloc/memjoin.hxx>
# endif
extern Cyg_Mempool_Joined<CYGCLS_MEMALLOC_MALLOC_IMPL> cyg_memalloc_mallocpool;
# define POOL cyg_memalloc_mallocpool

#endif

// From here on POOL can be used for memory allocations.

// ----------------------------------------------------------------------------
// Internal functions/macros. First some structures/macros to do the
// right thing for whether or not guards are enabled.

// An 8-byte structure (usually, so no problems with alignment)
typedef struct malloc_guard_head {
    size_t      mg_magic;
    size_t      mg_size;
} malloc_guard_head;

// The tail is just four bytes at the end, not necessarily aligned.
# define MG_MAGIC_H     0xe0d1c2b3
# define MG_MAGIC_T0    0x1d
# define MG_MAGIC_T1    0x4c
# define MG_MAGIC_T2    0x7b
# define MG_MAGIC_T3    0xaa

#ifdef CYGDBG_MEMALLOC_MALLOC_DEBUG_GUARDS

// If dlmalloc is in use, someone may have upped the alignment requirements.
// We fit in with that here in a somewhat layer-breaking way, although it
// doesn't seem that evil. The default alignment is 2^^3 anyway, so nothing
// would be different in that case.
# ifdef CYGNUM_MEMALLOC_ALLOCATOR_DLMALLOC_ALIGNMENT
#  define _DLMALLOC_ALIGNMENT_BYTES ((2<<CYGNUM_MEMALLOC_ALLOCATOR_DLMALLOC_ALIGNMENT) - 1)
#  define MG_HEAD_SIZE   ( (sizeof(malloc_guard_head) + _DLMALLOC_ALIGNMENT_BYTES) & ~_DLMALLOC_ALIGNMENT_BYTES )
# else
#  define MG_HEAD_SIZE   sizeof(malloc_guard_head)
# endif

# define MG_TAIL_SIZE   4

# define MG_SET_GUARDS(_ptr_, _size_)                                       \
    CYG_MACRO_START                                                         \
    malloc_guard_head*  head    = (malloc_guard_head*)_ptr_;                \
    cyg_uint8*          tail    = _ptr_ + _size_ + MG_HEAD_SIZE;            \
    head->mg_magic              = MG_MAGIC_H;                               \
    head->mg_size               = _size_;                                   \
    tail[0]                     = MG_MAGIC_T0;                              \
    tail[1]                     = MG_MAGIC_T1;                              \
    tail[2]                     = MG_MAGIC_T2;                              \
    tail[3]                     = MG_MAGIC_T3;                              \
    CYG_MACRO_END

// FIXME: CYG_FAIL is the wrong thing to use here. Memory guards can be
// enabled without assertions. Unfortunately cyg_assert_fail() is usually
// in an #ifdef.
# define MG_CHECK_GUARDS(_ptr_)                                                         \
    CYG_MACRO_START                                                                     \
    malloc_guard_head*  head    = (malloc_guard_head*)_ptr_;                            \
    cyg_uint8*          tail    = _ptr_ + head->mg_size + MG_HEAD_SIZE;                 \
    if (MG_MAGIC_H != head->mg_magic) {                                                 \
        CYG_FAIL("Corrupt or unallocated memory chunk passed to free");                 \
    }                                                                                   \
    if ((MG_MAGIC_T0 != tail[0]) || (MG_MAGIC_T1 != tail[1]) ||                         \
        (MG_MAGIC_T2 != tail[2]) || (MG_MAGIC_T3 != tail[3])) {                         \
        CYG_FAIL("corrupt memory chunk passed to free - buffer overflow");              \
    }                                                                                   \
    CYG_MACRO_END

# define MG_CLEAR_GUARDS(_ptr_)                                                         \
    CYG_MACRO_START                                                                     \
    malloc_guard_head* head = (malloc_guard_head*) _ptr_;                               \
    head->mg_magic          = 0;                                                        \
    head->mg_size           = 0;                                                        \
    CYG_MACRO_END

#else

# define MG_HEAD_SIZE   0
# define MG_TAIL_SIZE   0
# define MG_SET_GUARDS(_ptr_, _size_)   CYG_EMPTY_STATEMENT
# define MG_CHECK_GUARDS(_ptr_)         CYG_EMPTY_STATEMENT
# define MG_CLEAR_GUARDS(_ptr_)         CYG_EMPTY_STATEMENT

#endif

#ifdef CYGDBG_MEMALLOC_MALLOC_DEBUG_FILL_FREE
# define MG_FILL(_ptr_)                                                                                     \
    CYG_MACRO_START                                                                                         \
    malloc_guard_head*  head    = (malloc_guard_head*) _ptr_;                                               \
    memset(_ptr_, CYGDBG_MEMALLOC_MALLOC_DEBUG_FILL_FREE, head->mg_size + MG_HEAD_SIZE + MG_TAIL_SIZE);     \
    CYG_MACRO_END
# else
#  define MG_FILL(_ptr_) CYG_EMPTY_STATEMENT
# endif

// Now for the actual implementations. There are four combinations to worry about:
//   1) no debugging of any sort
//   2) debug guards only
//   3) debugdata only
//   4) both debug guards and debug data
//
// The exported functions malloc() etc. work in terms of macros
// ALLOC() and FREE(), which are defined here.

#if !defined(CYGDBG_MEMALLOC_MALLOC_DEBUG_GUARDS) && !defined(CYGDBG_MEMALLOC_DEBUG_DEBUGDATA)

// The simple case: no guards, no debug data, just go directly to the pool.
# define ALLOC(_size_, _dd_)                                POOL.try_alloc(_size_)
# define FREE(_ptr_, _dd_)                                  POOL.free(_ptr_)
# define REALLOC(_ptr_, _size_, _old_size_, _dda_, _ddf_)   POOL.resize_alloc(_ptr_, _size_, _old_size_)

#elif defined(CYGDBG_MEMALLOC_MALLOC_DEBUG_GUARDS) && !defined(CYGDBG_MEMALLOC_DEBUG_DEBUGDATA)

// Guards only, no debugdata.
#  define ALLOC(_size_, _dd_)                               debug_alloc(_size_)
#  define FREE(_ptr_, _dd_)                                 debug_free(_ptr_)
#  define REALLOC(_ptr_, _size_, _old_size_, _dda_, _ddf_)  debug_realloc(_ptr_, _size_, _old_size_)

static cyg_uint8*
debug_alloc(size_t size)
{
    size_t alloc_size = size + MG_HEAD_SIZE + MG_TAIL_SIZE;

    // Need to check if someone passed in a bogus value (like (size_t)-1) and we
    // wrapped round.
    if ( alloc_size < size ) {
        return NULL;
    }
    cyg_uint8*  ptr = POOL.try_alloc(alloc_size);
    if (NULL == ptr) {
        return NULL;
    }
    MG_SET_GUARDS(ptr, size);
    return &(ptr[MG_HEAD_SIZE]);
}

static cyg_bool
debug_free(cyg_uint8* ptr)
{
    ptr -= MG_HEAD_SIZE;
    MG_CHECK_GUARDS(ptr);
    MG_FILL(ptr);
    MG_CLEAR_GUARDS(ptr);
    return POOL.free(ptr);
}

static cyg_uint8*
debug_realloc(cyg_uint8* ptr, size_t size, cyg_int32* old_size)
{
    size_t alloc_size = size + MG_HEAD_SIZE + MG_TAIL_SIZE;

    // Need to check if someone passed in a bogus value (like (size_t)-1) and we
    // wrapped round.
    if ( alloc_size < size ) {
        return NULL;
    }
    ptr -= MG_HEAD_SIZE;
    MG_CHECK_GUARDS(ptr);
    ptr = POOL.resize_alloc(ptr, alloc_size, old_size);
    *old_size -= (MG_HEAD_SIZE + MG_TAIL_SIZE);
    if (NULL == ptr) {
        return NULL;
    }
    MG_SET_GUARDS(ptr, size);
    return &(ptr[MG_HEAD_SIZE]);
}

# else

// Debug data, with or without guards. Macros can take care of the
// latter
# define ALLOC(_size_, _dd_)                                debug_alloc(_size_, _dd_)
# define FREE(_ptr_, _dd_)                                  debug_free(_ptr_, _dd_)
# define REALLOC(_ptr_, _size_, _old_size_, _dda_, _ddf_)   debug_realloc(_ptr_, _size_, _old_size_, _dda_, _ddf_)

static cyg_uint8*
debug_alloc(size_t size, cyg_memalloc_dd_alloc* dd)
{
    static cyg_bool first_alloc = true;
    size_t alloc_size = size + MG_HEAD_SIZE + MG_TAIL_SIZE;

    // Need to check if someone passed in a bogus value (like (size_t)-1) and we
    // wrapped round.
    if ( alloc_size < size ) {
        return NULL;
    }

    cyg_uint8*  ptr = POOL.try_alloc(alloc_size);
    if (NULL == ptr) {
        return NULL;
    }
    if (first_alloc) {
        // There may be actually be several threads calling debug_alloc()
        // at the same time, but they will all report the same fields to
        // the memory debug code.
        Cyg_Mempool_Status stat;
        POOL.get_status( CYG_MEMPOOL_STAT_ARENABASE | CYG_MEMPOOL_STAT_ARENASIZE, stat);
        cyg_memalloc_dd_report_heap_details(stat.arenabase, stat.arenasize);
        first_alloc = false;
    }
    
    MG_SET_GUARDS(ptr, size);
#ifdef CYGDBG_MEMALLOC_DEBUG_DEBUGDATA_ACTUAL_SIZE
    dd->dd_actual_size  = POOL.get_allocation_size(ptr);
#endif    
    // The pointer stored in the debug data is what gets passed to the
    // user, not what the heap knows about. This should allow the user
    // to better match debug data with application objects.
    if (! cyg_memalloc_dd_report_alloc(&(ptr[MG_HEAD_SIZE]), dd)) {
        // Not enough memory to store the debug data. Fail the
        // malloc() rather than end up with a mismatch between the
        // heap and the debug data.
        POOL.free(ptr);
        return NULL;
    }
    return &(ptr[MG_HEAD_SIZE]);
}

static cyg_bool
debug_free(cyg_uint8* ptr, cyg_memalloc_dd_free* dd)
{
    cyg_uint8*  hdr = ptr - MG_HEAD_SIZE;
    MG_CHECK_GUARDS(hdr);
    MG_FILL(hdr);
    MG_CLEAR_GUARDS(hdr);
    cyg_memalloc_dd_report_free(ptr, dd);
    return POOL.free(hdr);
}

static cyg_uint8*
debug_realloc(cyg_uint8* ptr, size_t size, cyg_int32* old_size, cyg_memalloc_dd_alloc* dda, cyg_memalloc_dd_free* ddf)
{
    cyg_uint8*  hdr     = ptr - MG_HEAD_SIZE;
    cyg_uint8*  new_ptr;
    size_t alloc_size = size + MG_HEAD_SIZE + MG_TAIL_SIZE;

    // Need to check if someone passed in a bogus value (like (size_t)-1) and we
    // wrapped round.
    if ( alloc_size < size ) {
        return NULL;
    }
    
    MG_CHECK_GUARDS(hdr);
    new_ptr = POOL.resize_alloc(hdr, alloc_size, old_size);
    *old_size   -= (MG_HEAD_SIZE + MG_TAIL_SIZE);
    if (NULL == new_ptr) {
        return NULL;
    }
    MG_SET_GUARDS(new_ptr, size);
#ifdef CYGDBG_MEMALLOC_DEBUG_DEBUGDATA_ACTUAL_SIZE
    dda->dd_actual_size = POOL.get_allocation_size(new_ptr);
#endif    

    // The old info is freed first, to ensure that there is a free
    // alloc node for the new info
    cyg_memalloc_dd_report_free(ptr, ddf);
    cyg_memalloc_dd_report_alloc(&(new_ptr[MG_HEAD_SIZE]), dda);
    return &(new_ptr[MG_HEAD_SIZE]);
}

#endif  // CYGDBG_MEMALLOC_MALLOC_DEBUG_GUARDS || CYGDBG_MEMALLOC_DEBUG_DEBUGDATA

// ----------------------------------------------------------------------------
// Now the exported functions. First the C ones.

void *
malloc( size_t size )
{
    void *data_ptr;

    CYG_REPORT_FUNCNAMETYPE( "malloc", "returning pointer %08x" );
    CYG_REPORT_FUNCARG1DV( size );

#ifdef CYGSEM_MEMALLOC_MALLOC_ZERO_RETURNS_NULL
    // first check if size wanted is 0
    if ( 0 == size ) {
        CYG_REPORT_RETVAL( NULL );
        return NULL;
    } // if
#endif

    // ask the pool for the data
    CYG_MEMALLOC_DD_ALLOC(CYG_MEMALLOC_DD_MALLOC, size);
    data_ptr = ALLOC(size, CYG_MEMALLOC_DD_ALLOC_DATA);

    // if it isn't NULL is the pointer valid and appropriately aligned?
    if ( NULL != data_ptr ) {
        CYG_CHECK_DATA_PTR( data_ptr, "allocator returned invalid pointer!" );
        CYG_ASSERT( !((CYG_ADDRWORD)data_ptr & (sizeof(CYG_ADDRWORD) - 1)),
                    "Allocator has returned badly aligned data!");
    } // if

    CYG_REPORT_RETVAL( data_ptr );
    return data_ptr;
} // malloc()

void
free( void *ptr )
{
    cyg_bool freeret;

    CYG_REPORT_FUNCNAME( "free");
    CYG_REPORT_FUNCARG1XV( ptr );

    // if null pointer, do nothing as per spec
    if ( NULL==ptr )
        return;

    CYG_CHECK_DATA_PTR( ptr, "Pointer to free isn't even valid!" );
    CYG_MEMALLOC_DD_FREE();
    freeret = FREE((cyg_uint8*)ptr, CYG_MEMALLOC_DD_FREE_DATA);

    CYG_ASSERT( freeret , "Couldn't free!" );
    CYG_REPORT_RETURN();
} // free()


void *
calloc( size_t nmemb, size_t size )
{
    void *data_ptr;
    cyg_ucount32 realsize;

    CYG_REPORT_FUNCNAMETYPE( "calloc", "returning pointer %08x" );
    CYG_REPORT_FUNCARG2DV( nmemb, size );

    realsize = nmemb * size;
#ifdef CYGSEM_MEMALLOC_MALLOC_ZERO_RETURNS_NULL
    if ( 0 == realsize ) {
        CYG_REPORT_RETVAL( NULL );
        return NULL;
    } // if
#endif
    
    CYG_MEMALLOC_DD_ALLOC(CYG_MEMALLOC_DD_CALLOC, size);
    data_ptr = ALLOC(realsize, CYG_MEMALLOC_DD_ALLOC_DATA);

    // Fill with 0's if non-NULL
    if ( data_ptr != NULL ) {
        CYG_CHECK_DATA_PTR( data_ptr, "allocator returned invalid pointer!" );
        CYG_ASSERT( !((CYG_ADDRWORD)data_ptr & (sizeof(CYG_ADDRWORD) - 1)),
                    "Allocator has returned badly aligned data!");
        memset( data_ptr, 0, realsize );
    } // if

    CYG_REPORT_RETVAL( data_ptr );
    return data_ptr;
} // calloc()


externC void *
realloc( void *ptr, size_t size )
{
    void *      newptr;
    cyg_int32   oldsize;
    cyg_bool    freeret;

    CYG_REPORT_FUNCNAMETYPE( "realloc", "returning pointer %08x" );
    CYG_REPORT_FUNCARG2( "ptr=%08x, size=%d", ptr, size );

    CYG_MEMALLOC_DD_ALLOC(CYG_MEMALLOC_DD_REALLOC, size);
    CYG_MEMALLOC_DD_FREE();
    
    // if ptr is NULL this is equivalent to malloc()
    if ( ptr == NULL ) {
#ifdef CYGSEM_MEMALLOC_MALLOC_ZERO_RETURNS_NULL
        if ( 0 == size ) {
            CYG_REPORT_RETVAL( NULL );
            return NULL;
        }
#endif
        newptr = ALLOC(size, CYG_MEMALLOC_DD_ALLOC_DATA);
    } else {
        CYG_CHECK_DATA_PTR( ptr, "realloc() passed a bogus pointer!" );

        // if size is 0 this is equivalent to free
        if (size == 0) {
            freeret = FREE((cyg_uint8*)ptr, CYG_MEMALLOC_DD_FREE_DATA);
            CYG_ASSERT( freeret , "Couldn't free!" );
            CYG_REPORT_RETVAL( NULL );
            return NULL;
        } // if
        
        // A straightforward realloc(). First, can the pool take care of this?
        // This call also gives the old size.
        newptr = REALLOC( (cyg_uint8 *)ptr, size, &oldsize, CYG_MEMALLOC_DD_ALLOC_DATA, CYG_MEMALLOC_DD_FREE_DATA );
        if ( NULL == newptr ) {
            // The pool cannot change the size of the allocated chunk. Instead
            // try to allocate a new chunk and copy the old data
            CYG_ASSERT( oldsize != 0, "resize_alloc() couldn't determine allocation size!" );
            newptr = ALLOC(size, CYG_MEMALLOC_DD_ALLOC_DATA);
            if ( NULL != newptr ) {
                memcpy( newptr, ptr, size < (size_t) oldsize ? size : (size_t) oldsize );
                freeret = FREE( (cyg_uint8*) ptr, CYG_MEMALLOC_DD_FREE_DATA);
                CYG_ASSERT( freeret , "Couldn't free!" );
            }
        }
    }
    
    if ( NULL != newptr ) {
        CYG_CHECK_DATA_PTR( newptr, "allocator returned invalid pointer!" );
        CYG_ASSERT( !((CYG_ADDRWORD)newptr & (sizeof(CYG_ADDRWORD) - 1)),
                    "Allocator has returned badly aligned data!");
    }
    CYG_REPORT_RETVAL( newptr );
    return newptr;
} // realloc()


externC struct mallinfo
mallinfo( void )
{
    struct mallinfo ret = { 0 }; // initialize to all zeros
    Cyg_Mempool_Status stat;

    CYG_REPORT_FUNCTION();

    POOL.get_status( CYG_MEMPOOL_STAT_ARENASIZE|
                     CYG_MEMPOOL_STAT_FREEBLOCKS|
                     CYG_MEMPOOL_STAT_TOTALALLOCATED|
                     CYG_MEMPOOL_STAT_TOTALFREE|
                     CYG_MEMPOOL_STAT_MAXFREE, stat );

    if ( stat.arenasize > 0 )
        ret.arena = stat.arenasize;
    
    if ( stat.freeblocks > 0 )
        ret.ordblks = stat.freeblocks;

    if ( stat.totalallocated > 0 )
        ret.uordblks = stat.totalallocated;
    
    if ( stat.totalfree > 0 )
        ret.fordblks = stat.totalfree;

    if ( stat.maxfree > 0 ) {
        ret.maxfree = stat.maxfree;
#ifdef CYGDBG_MEMALLOC_MALLOC_DEBUG_GUARDS
        // Mainly to keep the test cases happy
        ret.maxfree -= (MG_HEAD_SIZE + MG_TAIL_SIZE);
#endif
    }

    CYG_REPORT_RETURN();
    return ret;
} // mallinfo()


// Now the C++ operators. Usually these come from libsupc++ and just
// call malloc() or free(). This gives a small overhead of an extra
// function call compared with accessing the pool directly, but given
// the overheads generally associated with dynamic memory allocation
// that is minor. However when collecting debug data and especially
// backtraces it significantly reduces the info gathered - on some
// architectures only a single level of backtrace is guaranteed.
//
// Optionally this file can provide the C++ operators directly.
// This also means that this file (and by extension the memalloc)
// package must be built with -fexceptions if it is possible for the
// user to be using C++ exceptions. This is indicated by the presence
// of the GCC-defined __EXCEPTIONS macro, which indicates whether we
// have been built with -fexceptions, without which exceptions are
// not supportable.

#ifdef CYGFUN_MEMALLOC_MALLOC_CXX_OPERATORS
#include <new>

extern std::new_handler __new_handler;  // Managed by libsupc++

void*
operator new(size_t size) throw (std::bad_alloc)
{
    void*   data_ptr;
    CYG_MEMALLOC_DD_ALLOC(CYG_MEMALLOC_DD_NEW, size);
    data_ptr = ALLOC(size, CYG_MEMALLOC_DD_ALLOC_DATA);
    while (NULL == data_ptr) {
        std::new_handler handler = __new_handler;
        if (handler) {
            handler();
            data_ptr = ALLOC(size, CYG_MEMALLOC_DD_ALLOC_DATA);
        }
#ifdef __EXCEPTIONS
        else {
            throw std::bad_alloc();
        }
#endif
        // It is not necessarily futile to loop even if there are no
        // exceptions and no user new_handler - another thread could
        // pre-empt and free data.
    }
    return data_ptr;
}

void*
operator new(size_t size, const std::nothrow_t&) throw()
{
    void*   data_ptr;
    CYG_MEMALLOC_DD_ALLOC(CYG_MEMALLOC_DD_NEWNT, size);
    data_ptr = ALLOC(size, CYG_MEMALLOC_DD_ALLOC_DATA);
    while (NULL == data_ptr) {
        std::new_handler    handler = __new_handler;
        if (! handler) {
            return 0;
        }
#ifdef __EXCEPTIONS
        try {
            handler();
        } catch (std::bad_alloc&) {
            return 0;
        }
#else
        handler();
#endif
        data_ptr = ALLOC(size, CYG_MEMALLOC_DD_ALLOC_DATA);
    }
    return data_ptr;
}

void*
operator new[](size_t size) throw (std::bad_alloc)
{
    void*   data_ptr;
    CYG_MEMALLOC_DD_ALLOC(CYG_MEMALLOC_DD_NEWV, size);
    data_ptr = ALLOC(size, CYG_MEMALLOC_DD_ALLOC_DATA);
    while (NULL == data_ptr) {
        std::new_handler handler = __new_handler;
        if (handler) {
            handler();
            data_ptr = ALLOC(size, CYG_MEMALLOC_DD_ALLOC_DATA);
        }
#ifdef __EXCEPTIONS
        else {
            throw std::bad_alloc();
        }
#endif
    }
    return data_ptr;
}

void*
operator new[](size_t size, const std::nothrow_t&) throw()
{
    void*   data_ptr;
    CYG_MEMALLOC_DD_ALLOC(CYG_MEMALLOC_DD_NEWNTV, size);
    data_ptr = ALLOC(size, CYG_MEMALLOC_DD_ALLOC_DATA);
    while (NULL == data_ptr) {
        std::new_handler    handler = __new_handler;
        if (! handler) {
            return 0;
        }
#ifdef __EXCEPTIONS
        try {
            handler();
        } catch (std::bad_alloc&) {
            return 0;
        }
#else
        handler();
#endif
        data_ptr = ALLOC(size, CYG_MEMALLOC_DD_ALLOC_DATA);
    }
    return data_ptr;
}

void
operator delete(void* ptr) throw()
{
    cyg_bool    freeret;
    if (NULL != ptr) {
        CYG_MEMALLOC_DD_FREE();
        freeret = FREE((cyg_uint8*)ptr, CYG_MEMALLOC_DD_FREE_DATA);
        CYG_ASSERT( freeret , "Couldn't free!" );
    }
}

void
operator delete(void* ptr, const std::nothrow_t&) throw()
{
    cyg_bool    freeret;
    if (NULL != ptr) {
        CYG_MEMALLOC_DD_FREE();
        freeret = FREE((cyg_uint8*)ptr, CYG_MEMALLOC_DD_FREE_DATA);
        CYG_ASSERT( freeret , "Couldn't free!" );
    }
}

void
operator delete[](void* ptr) throw()
{
    cyg_bool    freeret;
    if (NULL != ptr) {
        CYG_MEMALLOC_DD_FREE();
        freeret = FREE((cyg_uint8*)ptr, CYG_MEMALLOC_DD_FREE_DATA);
        CYG_ASSERT( freeret , "Couldn't free!" );
    }
}

void
operator delete[](void* ptr, const std::nothrow_t&) throw()
{
    cyg_bool    freeret;
    if (NULL != ptr) {
        CYG_MEMALLOC_DD_FREE();
        freeret = FREE((cyg_uint8*)ptr, CYG_MEMALLOC_DD_FREE_DATA);
        CYG_ASSERT( freeret , "Couldn't free!" );
    }
}

#endif

// Allow the debugdata code to perform malloc() operations without introducing
// recursive calls into itself
externC void*
cyg_memalloc_ddmalloc(size_t size) __THROW
{
    return POOL.try_alloc(size);
}

externC void
cyg_memalloc_ddfree(void* ptr) __THROW
{
    POOL.free((cyg_uint8*)ptr);
}

inline void *
operator new(size_t size,  CYGCLS_MEMALLOC_MALLOC_IMPL *ptr)
{ return (void *)ptr; };

externC cyg_bool
cyg_memalloc_heap_reinit( cyg_uint8 *base, cyg_uint32 size )
{
#if CYGMEM_HEAP_COUNT == 0
    CYGCLS_MEMALLOC_MALLOC_IMPL *m = new(&cyg_memalloc_mallocpool)
        CYGCLS_MEMALLOC_MALLOC_IMPL( base, size );
    return true;
#elif CYGMEM_HEAP_COUNT == 1    
    cygmem_memalloc_heaps[0] = new(cygmem_memalloc_heaps[0])
        CYGCLS_MEMALLOC_MALLOC_IMPL( base, size );
    return true;
#else
    return false;
#endif
}

// EOF malloc.cxx
