//////////////////////////////////////////////////////////////////////////////
//
// Asura_Platform_MemoryManagement.cpp
//
// Copyright (c) 2006 Rebellion. All rights reserved.
//
//////////////////////////////////////////////////////////////////////////////

// I N C L U D E S ///////////////////////////////////////////////////////////

#include "Core/Asura.h"

// This file's header.
#include "Asura_Platform_MemoryManagement.h"

// Stdlib independent.
#include <cell/gcm.h>
#include <sdk_version.h>

#if CELL_SDK_VERSION < 0x240000
#include <memory.h>
#else
#include <sys/memory.h>
#endif 

#include <stdlib.h>

// Engine independent.
#include "Core/Asura_Core.h"
#include "Core/Asura_Maths.h"
#include "Core/Asura_MemHeap.h"
#include "Core/Asura_Multithread.h"
#include "Debugging/Asura_Assert.h"

#ifdef SPU
#error Do not include this file in SPU projects
#endif

// M A C R O S ///////////////////////////////////////////////////////////////

// 0 = no
// 1 = yes but using calls straight to sys_memory_allocate(), etc.
// 2 = properly
#define OVERRIDE_MALLOC (2)

// C L A S S E S /////////////////////////////////////////////////////////////

class PS3HeapAllocator
{

public:

    static void Initialise( void );
    static void Shutdown( void );

    static Asura_MemHeap* GetMemHeap( void ) { return s_xMainMemAllocator.m_pxMemHeap; }
#ifdef ASURA_MEMORYMANAGEMENT_ENABLED
	static Asura_MemHeap* GetDebugMemHeap( void ) { return s_xDebugMemAllocator.m_pxMemHeap; }
#endif

	static void MapAllMemoryToRSX( char* pCommandBuffer, u_int uCommandBufferSize );

private:

    enum
    {
        MEM_BLOCK_SIZE        = 1024 * 1024,
        MAX_MEM_BLOCKS_TO_MAP = 256,         // 1MB each so 256 cover the entirety of physical main RAM and all of our possible needs.
    };

	void Internal_Initialise( Asura_Memheap_AllocFunc pfnMoreMem, Asura_Memheap_AllocFunc pfnTrimMem );
	void Internal_Shutdown( void );

	void* Internal_GetMoreMemory( size_t uNumBytes );
	void* Internal_TrimMemory( size_t uNumBytes );

	static void* GetMoreMemory( size_t uNumBytes )		{ return s_xMainMemAllocator.Internal_GetMoreMemory(uNumBytes); }
	static void* TrimMemory( size_t uNumBytes )			{ return s_xMainMemAllocator.Internal_TrimMemory(uNumBytes); }
#ifdef ASURA_MEMORYMANAGEMENT_ENABLED
	static void* GetMoreDebugMemory( size_t uNumBytes )	{ return s_xDebugMemAllocator.Internal_GetMoreMemory(uNumBytes); }
	static void* TrimDebugMemory( size_t uNumBytes )	{ return s_xDebugMemAllocator.Internal_TrimMemory(uNumBytes); }
#endif

	// member data
    char           m_acBfrForMemHeap[(sizeof(Asura_MemHeap) + 15) & ~15] __attribute__(( aligned(16) ));
    Asura_MemHeap* m_pxMemHeap;
    sys_addr_t     m_uBaseAddr;
    u_int          m_uNumBytesMapped;
    u_int          m_uMemBlockCount;
#if _DEBUG
    sys_memory_t   m_auMemBlockIDs[MAX_MEM_BLOCKS_TO_MAP];
#endif

	// static class data
	static PS3HeapAllocator s_xMainMemAllocator;
#ifdef ASURA_MEMORYMANAGEMENT_ENABLED
	static PS3HeapAllocator s_xDebugMemAllocator;
#endif
	static bool	s_bMapToRSX;
};

// G L O B A L S /////////////////////////////////////////////////////////////

// used to determine whether we need to unlock the mutex (which may or may not 
// have been used depending on the value of s_bMultithreadingEnabled when 
// Platform_Lock() was called.
static u_int g_uNumLocks;

#if OVERRIDE_MALLOC > 0 && defined(ASURA_MEMORYMANAGEMENT_ENABLED)
static char g_acBfrForMMS[(sizeof(Asura_MemoryManagement_System) + 15) & ~15] __attribute__(( aligned(16) ));
static Asura_MemoryManagement_System* g_pxMMS = NULL;
#endif

#if OVERRIDE_MALLOC > 0
static char g_acBfrForMutex[(sizeof(Asura_Mutex) + 15) & ~15] __attribute__(( aligned(16) ));
#endif

static Asura_Mutex* g_pxMutex = NULL;

// C L A S S   I M P L E M E N T A T I O N S /////////////////////////////////

void Asura_MemoryManagement_System::Platform_Lock(void)
{
	if (s_bMultithreadingEnabled)
	{
#if OVERRIDE_MALLOC == 0
        static bool bOnce = true;
        if( bOnce )
        {
            static Asura_Mutex xMutex;
            g_pxMutex = &xMutex;
            bOnce = false;
        }
#endif

		g_pxMutex->Lock();
		++g_uNumLocks;
	}
	else
	{
		Asura_Assert( Asura_Core::IsMainThread(), "should only allocate from main thread unless using mutex" );
	}
}


void Asura_MemoryManagement_System::Platform_Unlock(void)
{
	// if there is an outstanding lock but we stopped requiring use of the 
	// mutex, unlock it anyway
	if (g_uNumLocks)
	{
		--g_uNumLocks;
		g_pxMutex->Unlock();
	}
}


#ifdef ASURA_MEMORYMANAGEMENT_ENABLED

void Asura_MemoryManagement_System::Platform_Initialise(void)
{
}

void Asura_MemoryManagement_System::Platform_Shutdown(void)
{
}

#ifdef ASURA_MEMORYMANAGEMENT_FILL
void Asura_MemoryManagement_System::Platform_FillMem( void* pAddr,
                                                      u_int uSize,
                                                      ASURA_MM_EVENT_ID eEventID,
                                                      u_int uAlignment,
                                                      void* pReportedAddr /*= NULL*/,
                                                      u_int uReportedSize /*= 0*/ )
{
    if( uAlignment )
    {
        Asura_Assert( pReportedAddr != NULL, "" );
        Asura_Assert( uReportedSize > 0, "" );
    }

    u_char ucFill;
	switch( eEventID )
    {
        case ASURA_NEW:
        case ASURA_NEWALIGN:
        case ASURA_NEWARRAY:
        case ASURA_ALLOC:
            ucFill = ucFillValueAllocate;           
            break;

        case ASURA_DELETE:
        case ASURA_DELETEALIGN:
        case ASURA_DELETEARRAY:
        case ASURA_DEALLOC:
            ucFill = ucFillValueFree;           
            break;

        default:
            Asura_Assert( false, "Invalid memory manager event ID." );
            return;
    }
    
    // Fill.
    u_char* pucOut = reinterpret_cast<u_char*>( pAddr );
   
    {
        while( uSize-- ) { *pucOut++ = ucFill; }
    }
}
#endif // #ifdef ASURA_MEMORYMANAGEMENT_FILL

void Asura_MemoryManagement_System::Platform_PostDeallocate( void )
{
}

void Asura_MemoryUtility::Platform_Initialise( void )
{
}

void Asura_MemoryUtility::Platform_Shutdown( void )
{
}

void* Asura_MemoryUtility::Platform_PreAllocate(/*const*/ size_t uNumBytes)
{
 	return PS3HeapAllocator::GetDebugMemHeap()->Alloc( uNumBytes );
}

void* Asura_MemoryUtility::Platform_PostAllocate(void* /*const*/ pAddr)
{
	return pAddr;
}

bool Asura_MemoryUtility::Platform_PreDeallocate(void* /*const*/ pAddr)
{
	PS3HeapAllocator::GetDebugMemHeap()->Free(pAddr);
	return true;
}

void Asura_MemoryUtility::Platform_PostDeallocate(void* /*const*/ pAddr)
{
}

#endif // #ifdef ASURA_MEMORYMANAGEMENT_ENABLED

//
// PS3HeapAllocator
//

PS3HeapAllocator	PS3HeapAllocator::s_xMainMemAllocator;
#ifdef ASURA_MEMORYMANAGEMENT_ENABLED
PS3HeapAllocator	PS3HeapAllocator::s_xDebugMemAllocator;
#endif
bool				PS3HeapAllocator::s_bMapToRSX = false;

void PS3HeapAllocator::Initialise( void )
{
	s_xMainMemAllocator.Internal_Initialise(GetMoreMemory, TrimMemory);
#ifdef ASURA_MEMORYMANAGEMENT_ENABLED
	s_xDebugMemAllocator.Internal_Initialise(GetMoreDebugMemory, TrimDebugMemory);
#endif

	s_bMapToRSX = false;
}

void PS3HeapAllocator::Shutdown( void )
{
#ifdef ASURA_MEMORYMANAGEMENT_ENABLED
	s_xDebugMemAllocator.Internal_Shutdown();
#endif
	s_xMainMemAllocator.Internal_Shutdown();

	s_bMapToRSX = false;
}

void PS3HeapAllocator::Internal_Initialise( Asura_Memheap_AllocFunc pfnMoreMem, Asura_Memheap_AllocFunc pfnTrimMem )
{
#if OVERRIDE_MALLOC > 1

    // Allocate address space for all future needs.
    int iRet = sys_mmapper_allocate_address( MAX_MEM_BLOCKS_TO_MAP * MEM_BLOCK_SIZE,
                                             SYS_MEMORY_PAGE_SIZE_1M | SYS_MEMORY_ACCESS_RIGHT_ANY,
                                             0,
                                             &m_uBaseAddr );
    Asura_Assert( iRet == CELL_OK, "Failed to allocate process address space" );
	(void)iRet;


    m_uNumBytesMapped = 0;
    m_uMemBlockCount  = 0;
#else
    m_uBaseAddr = 0;
#endif

    // Initialise memheap using placement new - see comment in _malloc_init().
    {
        Asura_Memheap_Core_Details xMemHeapCfg( pfnMoreMem,
                                                pfnTrimMem,
                                                true,
                                                MEM_BLOCK_SIZE,
                                                -1,
                                                MEM_BLOCK_SIZE,
                                                uASURA_MEMHEAP_DEFAULT_TOP_PAD );

        void* pvBfrForMemHeap = &m_acBfrForMemHeap[0];
#include "Core/Asura_MemoryManagement_DebugOff.h"
        new(pvBfrForMemHeap) Asura_MemHeap(xMemHeapCfg);
#include "Core/Asura_MemoryManagement_DebugOn.h"
        m_pxMemHeap = static_cast<Asura_MemHeap*>(pvBfrForMemHeap);

        m_pxMemHeap->EnableThreadSafety();
    }
}


void PS3HeapAllocator::Internal_Shutdown( void )
{
#if OVERRIDE_MALLOC > 1
    Asura_Assert( !m_uMemBlockCount, "" );

    int iRet = sys_mmapper_free_address( m_uBaseAddr );
    Asura_Assert( iRet == CELL_OK, "Failed to free process address space - not all unmapped?" );
    (void)iRet;

#endif

    // Explicit destruction of our memheap - see comment in _malloc_init().
    m_pxMemHeap->~Asura_MemHeap();
    m_pxMemHeap = NULL;
}


void* PS3HeapAllocator::Internal_GetMoreMemory( size_t uNumBytes )
{
#if OVERRIDE_MALLOC > 1

    Asura_Assert( !(uNumBytes % MEM_BLOCK_SIZE), "Memory size specified not a multiple of the block size" );
    u_int uNumBlocks = uNumBytes / MEM_BLOCK_SIZE;

    Asura_Assert( m_uMemBlockCount + uNumBlocks <= MAX_MEM_BLOCKS_TO_MAP, "" );
    Asura_Assert( m_uNumBytesMapped == m_uMemBlockCount * MEM_BLOCK_SIZE, "" );

    int iRet;
    u_int uBlock;

    // Regardless of the size requested, allocate and map one block at a time
    // so that they can also be trimmed (released) one block at a time,
    // rather than necessarily in the allocation's entirety.
    for( uBlock = 0; uBlock < uNumBlocks; ++uBlock )
    {
        // Allocate corresponding physical memory.
        sys_memory_t uMemID;
        iRet = sys_mmapper_allocate_memory( MEM_BLOCK_SIZE, SYS_MEMORY_GRANULARITY_1M, &uMemID );
        if( iRet != CELL_OK )
        {
#if !defined(ASURA_NO_DEBUG_OUTPUT)
			//dump memory image:
			Asura_MallocInfo xInfo = m_pxMemHeap->GetMallocInfo();
			Asura_OutputToDebugger_Retail(("Ran out of memory, current heap state:\n"));
			Asura_OutputToDebugger_Retail(("  % 8.2fMB m_iSystemAllocated  		// space allocated from system						\n", (float)xInfo.m_iSystemAllocated/1048576.0f  			));
			Asura_OutputToDebugger_Retail(("  % 8d   m_iFreeChunks  			// number of free chunks							\n", xInfo.m_iFreeChunks  				));
			Asura_OutputToDebugger_Retail(("  % 8d   m_iFastbinBlocks   		// number of fastbin blocks							\n", xInfo.m_iFastbinBlocks   			));
			Asura_OutputToDebugger_Retail(("  % 8.2fMB m_iMaxSystemAllocated  	// highest amount of space allocated from system	\n", (float)xInfo.m_iMaxSystemAllocated/1048576.0f  		));
			Asura_OutputToDebugger_Retail(("  % 8.2fMB m_iSpaceInFreeFast  		// space available in freed fastbin blocks			\n", (float)xInfo.m_iSpaceInFreeFast/1048576.0f  			));
			Asura_OutputToDebugger_Retail(("  % 8.2fMB m_iTotalAllocated			// total allocated space							\n", (float)xInfo.m_iTotalAllocated/1048576.0f			));
			Asura_OutputToDebugger_Retail(("  % 8.2fMB m_iTotalFree 				// total free space									\n", (float)xInfo.m_iTotalFree/1048576.0f 				));
			Asura_OutputToDebugger_Retail(("  % 8.2fMB m_iKeepCost 				// top-most, releasable (via malloc_trim) space		\n", (float)xInfo.m_iKeepCost/1048576.0f	));
			Asura_OutputToDebugger_Retail(("  % 8d   m_uRequestedAllocations	// Total number of calls to Alloc					\n", xInfo.m_uRequestedAllocations		));
			Asura_OutputToDebugger_Retail(("  % 8d   m_uSystemMemCalls			// Calls to request more system memory				\n", xInfo.m_uSystemMemCalls			));
#endif

#if defined(ASURA_ASSERT)
            Asura_Assert( false, "Failed to allocate physical memory block - out of memory?" );
#else
			ASURA_PLATFORM_DEBUGBREAK();
#endif	//defined(ASURA_ASSERT)
            break;
        }

        // Map into the process space we reserved at start-up.
        sys_addr_t uAddr = m_uBaseAddr + m_uNumBytesMapped + uBlock * MEM_BLOCK_SIZE;
        iRet = sys_mmapper_map_memory( uAddr, uMemID, SYS_MEMORY_PROT_READ_WRITE );
        if( iRet != CELL_OK )
        {
            Asura_Assert( false, "Failed to map new physical memory block into reserved process space - out of kernel resources?" );
            sys_mmapper_free_memory( uMemID );
            break;
        }

		if ( s_bMapToRSX && this==&s_xMainMemAllocator )
		{
			// map all main memory so RSX can see it
			u_int uOffset; //ignored
			iRet = cellGcmMapMainMemory( reinterpret_cast<void*>(uAddr), MEM_BLOCK_SIZE, &uOffset );
			//Asura_OutputToDebugger(("Mapped %016x to RSX offset %08x\n", uAddr, uOffset));
			if ( iRet != CELL_OK )
			{
				// This should only happen if you're running on a debug kit with >256MB main memory.
				// It can screw up any render code that creates buffers in main mem for the RSX to read.
				Asura_OutputToDebugger_Retail(( "cellGcmMapMainMemory failed, some memory could not be mapped to the RSX\n" ));
			}
		}

#if _DEBUG
        m_auMemBlockIDs[m_uMemBlockCount + uBlock] = uMemID;
#endif
    }

    // Clean up on failure - it's all or nothing.
    if( uBlock != uNumBlocks )
    {
        while( uBlock-- )
		{
            sys_memory_t uMemID;
            sys_mmapper_unmap_memory( m_uBaseAddr + m_uNumBytesMapped + uBlock * MEM_BLOCK_SIZE, &uMemID );
            sys_mmapper_free_memory( uMemID );

#if _DEBUG
            Asura_Assert( m_auMemBlockIDs[m_uMemBlockCount + uBlock] == uMemID, "" );
#endif
        }

        return NULL;
    }

    sys_addr_t uReturnAddr = m_uBaseAddr + m_uNumBytesMapped;

    m_uNumBytesMapped += uNumBytes;
    m_uMemBlockCount  += uNumBlocks;

    return reinterpret_cast<void*>(uReturnAddr);

#else

    sys_addr_t uAddr;
    int iRet = sys_memory_allocate( uNumBytes, SYS_MEMORY_PAGE_SIZE_1M, &uAddr );
    Asura_Assert( iRet == CELL_OK, ":(" );
    m_uBaseAddr = uAddr + uNumBytes; // abuse this for end of mem value
    return reinterpret_cast<void*>(uAddr);

#endif
}


void PS3HeapAllocator::MapAllMemoryToRSX( char* pCommandBuffer, u_int uCommandBufferSize )
{
	// map all main memory so RSX can see it
	// map in 1MB chunks, for consistency
	// exclude the command buffer, for some unfathomable reason
	
	const u_int u1MB = 0x100000;
	u_int uOffset; //ignored
	for ( u_int uByte=0; uByte < s_xMainMemAllocator.m_uNumBytesMapped; uByte += u1MB )
	{
		char *pMB = reinterpret_cast<char*>(s_xMainMemAllocator.m_uBaseAddr+uByte); //pointer to an alloced megabyte
		if ( pMB+u1MB <= pCommandBuffer || pMB >= pCommandBuffer + uCommandBufferSize )
		{
			int iRet = cellGcmMapMainMemory( pMB, u1MB, &uOffset );
			//Asura_OutputToDebugger(("Mapped %016x to RSX offset %08x\n", reinterpret_cast<uint64_t>(pMB), uOffset));
			if ( iRet != CELL_OK )
			{
				Asura_Assert( 0, "cellGcmMapMainMemory failed" );
				return;
			}
		}
	}
	
	// set a flag to map all future allocs to RSX as well
	s_bMapToRSX = true;
}


// Map all of main memory to RSX, excluding the command buffer (which must not be mapped for reasons known only to Sony engineers)
void Asura_PS3_MapAllMemoryToRSX( void* pCommandBuffer, u_int uCommandBufferSize )
{
	PS3HeapAllocator::MapAllMemoryToRSX(reinterpret_cast<char*>(pCommandBuffer),uCommandBufferSize);
}


u_int Asura_PS3_GetFreeMainMemory()
{
	u_int uAvailableMemory = 0;
	std::malloc_managed_size xMMSize;
	if( !malloc_stats(&xMMSize) )
	{
#ifdef ASURA_MEMORYMANAGEMENT_ENABLED
		if( Asura_MemoryManagement_System::GetMaxMemToAllocate() )
		{
			uAvailableMemory = Asura_MemoryManagement_System::GetMaxMemToAllocate() - xMMSize.current_inuse_size;
		}
		else
#endif
		{
			sys_memory_info_t xMemInfo;
			if( CELL_OK == sys_memory_get_user_memory_size(&xMemInfo) )
			{
				// Total memory is the amount still available plus the amount already in the memheap minus the amount within the memheap already in use
				uAvailableMemory = xMemInfo.available_user_memory + xMMSize.current_system_size - xMMSize.current_inuse_size;
			}
		}
	}
	return uAvailableMemory;
}


void* PS3HeapAllocator::Internal_TrimMemory( size_t uNumBytes )
{
#if OVERRIDE_MALLOC > 1

    Asura_Assert( !(uNumBytes % MEM_BLOCK_SIZE), "Memory size specified not a multiple of the block size" );
    u_int uNumBlocksToTrim = uNumBytes / MEM_BLOCK_SIZE;

    Asura_Assert( uNumBlocksToTrim <= m_uMemBlockCount, "" );
    Asura_Assert( m_uNumBytesMapped == m_uMemBlockCount * MEM_BLOCK_SIZE, "" );

    int iRet;
    for( u_int u = 0; u < uNumBlocksToTrim; ++u )
    {
        sys_addr_t uBlockAddr = (m_uBaseAddr + m_uNumBytesMapped) - MEM_BLOCK_SIZE;

		if ( s_bMapToRSX && this==&s_xMainMemAllocator )
		{
			iRet = cellGcmUnmapEaIoAddress( reinterpret_cast<void*>(uBlockAddr) );
			//Asura_OutputToDebugger(("Unmapped 1MB at %016x from RSX\n", uBlockAddr));
			Asura_Assert( iRet == CELL_OK, "failed to unmap memory" );
		}

        sys_memory_t uMemID;
        iRet = sys_mmapper_unmap_memory( uBlockAddr, &uMemID );
        Asura_Assert( iRet == CELL_OK, "" );

        iRet = sys_mmapper_free_memory( uMemID );
        Asura_Assert( iRet == CELL_OK, "" );

        m_uNumBytesMapped -= MEM_BLOCK_SIZE;

        --m_uMemBlockCount;
#if _DEBUG
        Asura_Assert( m_auMemBlockIDs[m_uMemBlockCount] == uMemID, "" );
#endif
    }
    (void)iRet;

    return reinterpret_cast<void*>(m_uBaseAddr + m_uNumBytesMapped);

#else

    return NULL;

#endif
}

#ifdef ASURA_MEMORYMANAGEMENT_ENABLED
u_int Asura_MemoryManagement_System::Platform_GetAddressSpaceStart(void) { return 0x30000000; } //base address of our process
u_int Asura_MemoryManagement_System::Platform_GetAddressSpaceEnd(void) { return 0x40000000; } //256MB higher
#endif


// F U N C T I O N   I M P L E M E N T A T I O N S ///////////////////////////

#if OVERRIDE_MALLOC > 0

_C_STD_BEGIN
extern "C"
{

void _malloc_init( void )
{
    // Here we manually construct (and in _malloc_finalize() destruct) some
    // globals related to our memory management, rather than leaving it to be
    // dealt with by the compiler in the normal global constructor /
    // destructor run. Doing it manually provides guarantees over their
    // lifetimes, ie. that they will persist throughout any period when
    // allocation or deallocation (including by library functions) may occur.

    void* pvBfrForMutex = &g_acBfrForMutex[0];
#include "Core/Asura_MemoryManagement_DebugOff.h"
    new(pvBfrForMutex) Asura_Mutex();
#include "Core/Asura_MemoryManagement_DebugOn.h"
    g_pxMutex = static_cast<Asura_Mutex*>(pvBfrForMutex);

    PS3HeapAllocator::Initialise();

#ifdef ASURA_MEMORYMANAGEMENT_ENABLED
    // We enable ASURA_MEMORYMANAGEMENT_INIT_OBJ_IN_PLATFORM_CODE and just
    // explicitly construct a single instance here, rather than having one
    // included per source file to guarantee it comes first in the global
    // constructors.
    void* pvBfrForMMS = &g_acBfrForMMS[0];
#include "Core/Asura_MemoryManagement_DebugOff.h"
    new(pvBfrForMMS) Asura_MemoryManagement_System();
#include "Core/Asura_MemoryManagement_DebugOn.h"
    g_pxMMS = static_cast<Asura_MemoryManagement_System*>(pvBfrForMMS);
#endif
}


void _malloc_finalize( void )
{
#ifdef ASURA_MEMORYMANAGEMENT_ENABLED
    // Asura_MemoryManagement_System's destructor is currently empty but we
    // might as well go through the motions. The actual work is done via
    // a function registered with atexit() that will have been called before
    // this point (but after global destructors are complete).
    g_pxMMS->~Asura_MemoryManagement_System();

    g_pxMMS = NULL;
#endif

    PS3HeapAllocator::Shutdown();

    g_pxMutex->~Asura_Mutex();
    g_pxMutex = NULL;
}


void* calloc( size_t uNumElements, size_t uNumBytesPerElement )
{
    // Implemented as specified in the PS3 SDK docs.

    size_t uTotalNumBytes = Asura_Maths::Max( 16u, uNumElements * uNumBytesPerElement );

    void* pvMemory = memalign( 16, uTotalNumBytes );
    if( pvMemory )
    {
        memset( pvMemory, 0, uTotalNumBytes );
    }

    return pvMemory;
}


void free( void* pvMemory )
{
    return PS3HeapAllocator::GetMemHeap()->Free( pvMemory );
}


void* malloc( size_t uNumBytes )
{
    return PS3HeapAllocator::GetMemHeap()->Alloc( uNumBytes );
}


int malloc_stats( malloc_managed_size* pxMMSize )
{
    if( !pxMMSize ) { return 1; }

    const Asura_MallocInfo xMallocInfo = PS3HeapAllocator::GetMemHeap()->GetMallocInfo();

    pxMMSize->max_system_size     = xMallocInfo.m_iMaxSystemAllocated;
    pxMMSize->current_system_size = xMallocInfo.m_iSystemAllocated;
    pxMMSize->current_inuse_size  = xMallocInfo.m_iTotalAllocated;

    return 0;
}


size_t malloc_usable_size( void* pvMem )
{
    Asura_Assert( false, "Unsupported" );
    return 0;
}


void* memalign( size_t uAlignment, size_t uNumBytes )
{
    return PS3HeapAllocator::GetMemHeap()->Alloc( uNumBytes, uAlignment );
}


void* realloc( void* pvMemory, size_t uNumBytes )
{
	//Asura_Assert( false, "Unsupported" );
    //BW needed for shader compiling
    return PS3HeapAllocator::GetMemHeap()->Realloc( pvMemory, uNumBytes );
}    


void* reallocalign( void* pvMemory, size_t uNumBytes, size_t uAlignment )
{
    Asura_Assert( false, "Unsupported" );
    return NULL;
}

} // extern "C"
_C_STD_END
#endif // #if OVERRIDE_MALLOC > 0
