#include <string.h>
#include <stdlib.h>
#ifdef __MAC
	#if defined(__cplusplus) && defined(_MSL_USING_NAMESPACE)
		using namespace std;
	#endif
#endif

#include "c4d_memory.h"
#include "operatingsystem.h"
#include "c4d_general.h"

void _GeFree(void **Daten)
{
	if (*Daten)
	{
		C4DOS.Ge->Free(*Daten);
	}
	*Daten=NULL;
}

// only for debug purposes
void _GeCheckAllMemory()
{
	C4DOS.Ge->GeCheckMem((void*)(VULONG)0xdeadbeef);
}

void _GeCheck(void *memptr)
{
	if (memptr)
	{
		C4DOS.Ge->GeCheckMem(memptr);
	}
}

Bool	GeGetAllocSize( void *p, VLONG *out_size )
{
	return C4DOS.Ge->GetAllocSize( p, out_size );
}

// allocator is either NULL (current thread's allocator as used by GeAlloc, gNew etc.) or a pool created via GeAllocPool
Bool	GeGetAllocatorStatistics( BaseContainer &stat, void *allocator )
{
	return C4DOS.Ge->GeGetAllocatorStatistics( stat, allocator );
}

// try to estimate how much physical memory is still unused
VULONG	GeMemGetFreePhysicalMemoryEstimate( void )
{
	return C4DOS.Ge->GeMemGetFreePhysicalMemoryEstimate();
}

#define	STDLIB_MEM_MAGIC		-1
static Bool	stdlib_mem_used = FALSE;												// changed to TRUE if static constructors have allocated memory before c4d's memory management was available

//long	new_array_memory_offset = -1;
//static void	*array_alloc_ptr = NULL;

Bool	IsAlienMem( void *p )
{
	if ( p )
	{
		if (((VLONG*)p)[-1]==STDLIB_MEM_MAGIC)									// is this a block for the stdlib?
			return TRUE;
	}	
	return FALSE;
}

static void *AlienMem(size_t s, Bool clear)
{
	if (s<1) s=1;

	void *p = malloc(s+sizeof(VLONG));
	if (!p) return NULL;
	
	if (clear) memset(p,0,s+sizeof(VLONG));
	*(VLONG *) p = STDLIB_MEM_MAGIC;
	p = (void *) ((UCHAR *)p + sizeof(VLONG));
	stdlib_mem_used = TRUE;																		// static constructor has allocated memory
	return p;
}

#ifndef	__C4D_NO_NEW_DELETE__																// use c4d's fast new/delete operators instead of the default implementation?

// standard new operators (which theoretically could throw), just for compatibility with 3rd party libs
// Consider using gNew/gDelete instead. To disable deprecation, use _CRT_SECURE_NO_WARNINGS/GCC_WARN_ABOUT_DEPRECATED_FUNCTIONS. See online help for details.
#if defined( __MAC )
	void	*operator new(size_t s) throw (std::bad_alloc)
#else
	void	*operator new(size_t s)
#endif
{
	if (t_C4DOS) return C4DOS.Ge->Alloc(s<1?1:s,0,NULL);

	return AlienMem(s,TRUE);
}

void	*operator new(size_t s,const std::nothrow_t&) throw()
{
	if (t_C4DOS) return C4DOS.Ge->Alloc(s<1?1:s,0,NULL);

	return AlienMem(s,TRUE);
}

// Consider using bNew/bDelete instead. To disable deprecation, use _CRT_SECURE_NO_WARNINGS/GCC_WARN_ABOUT_DEPRECATED_FUNCTIONS. See online help for details.
#if defined( __MAC )
	void	*operator new[](size_t s) throw (std::bad_alloc)
#else
	void	*operator new[](size_t s)
#endif
{
	if (t_C4DOS) return C4DOS.Ge->Alloc(s<1?1:s,0,NULL);

	return AlienMem(s,TRUE);
}

void	*operator new[](size_t s,const std::nothrow_t&) throw()
{
	if (t_C4DOS) return C4DOS.Ge->Alloc(s<1?1:s,0,NULL);

	return AlienMem(s,TRUE);
}

// delete operators for c4d (called by gDelete and bDelete)
void operator delete(void *p) throw()
{
	if (p)
	{
		void	*temp = p;

		if (stdlib_mem_used)																		// memory allocated by static constructors?
		{
			if (((VLONG*)p)[-1]==STDLIB_MEM_MAGIC)								// is this a block for the stdlib?
			{
				free((void*)((UCHAR*)p - sizeof(VLONG)));
				return;
			}
		}
		C4DOS.Ge->Free(temp);
	}
}

void operator delete[](void *p) throw()
{
	if (p)
	{
		void	*temp = p;

		if (stdlib_mem_used)																		// memory allocated by static constructors?
		{
			if (((VLONG*)p)[-1]==STDLIB_MEM_MAGIC)								// is this a block for the stdlib?
			{
				free((void*)((UCHAR*)p - sizeof(VLONG)));
				return;
			}
		}
		C4DOS.Ge->Free(temp);
	}
}

// usually the nothrow delete operators are not called by the compilers (delete (std::nothrow) is not supported), we define them for completeness
void operator delete(void *p,const std::nothrow_t&) throw()
{
	C4DOS.Ge->Free( p );
}

void operator delete[](void *p,const std::nothrow_t&) throw()
{
	C4DOS.Ge->Free( p );
}

#endif

// new operators for c4d (called by gNew and bNew)
void	*operator new( size_t s, const std::nothrow_t&, int line, const CHAR *file ) throw()
{
	if (t_C4DOS) return C4DOS.Ge->Alloc(s<1?1:s,line,file);

	return AlienMem(s,TRUE);
}

void	*operator new( size_t s,const std::nothrow_t&, int line, const CHAR *file, Bool clear ) throw()
{
	if (t_C4DOS) 
	{
		if (clear)
			return C4DOS.Ge->Alloc(s<1?1:s,line,file);
		else
			return C4DOS.Ge->AllocNC(s<1?1:s,line,file);
	}

	return AlienMem(s,clear);
}

void	*operator new[](size_t s,const std::nothrow_t&,int line,const CHAR *file) throw()
{
	void	*d;
	
	if ( s <= 0 )
		s = 1;

	d = (VULONG *) ( t_C4DOS ? C4DOS.Ge->Alloc( s, line, file ) : AlienMem( s, TRUE ));

#ifdef _DEBUG
	if ((VULONG) d & __C4D_MEM_ALIGNMENT_MASK__ )
		GeBoom();
#endif

	return d;
}

void	*operator new[](size_t s,const std::nothrow_t&,int line,const CHAR *file, Bool clear) throw()
{
	void	*d;
	
	if ( s <= 0 )
		s = 1;

	if ( t_C4DOS )
		d = (VULONG *) ( clear ? C4DOS.Ge->Alloc( s, line, file ) : C4DOS.Ge->AllocNC( s, line, file ));
	else
		d = (VULONG *) AlienMem( s, TRUE );

#ifdef _DEBUG
	if ((VULONG) d & __C4D_MEM_ALIGNMENT_MASK__ )
		GeBoom();
#endif

	return d;
}


void operator delete(void *p,const std::nothrow_t&,int line,const CHAR *file) throw()
{
	C4DOS.Ge->Free( p );
}

void operator delete[](void *p,const std::nothrow_t&,int line,const CHAR *file) throw()
{
	C4DOS.Ge->Free( p );
}

void operator delete(void *p,const std::nothrow_t&,int line,const CHAR *file, Bool clear) throw()
{
	C4DOS.Ge->Free( p );
}

void operator delete[](void *p,const std::nothrow_t&,int line,const CHAR *file, Bool clear) throw()
{
	C4DOS.Ge->Free( p );
}
