/* -----------------------------------------------------------------------------
	GSFramework
	Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
----------------------------------------------------------------------------- */


		#include <stdlib.h>
		#include "alloc/ialloc.h"
		#include "thread/mutex.h"
		#include "log/log.h"

		using namespace GS::Alloc;


//------------------------------------------------------------------------------
void				*DefaultAllocator::Alloc(size_t size, System sys)
{	__NSTAT_WRAPALLOC(malloc(size), sys)		}
void				DefaultAllocator::Delete(void *addr, System sys)
{	__NSTAT_WRAPDELETE(free(addr), sys)			}
//------------------------------------------------------------------------------


#if		__ENABLE_ALLOCATION_STAT__

namespace GS {
	namespace Alloc {

Stat		system_stat[SystemCount];
SystemDesc	system_desc[SystemCount] =
{
	{	"General", "System"				},

	{	"Container", "List Item"		},
	{	"String", "String Buffer"		},

	{	"I/O", "Filesystem"				},
	{	"I/O", "Metatag"				},

	{	"Animation", "Curve"			},
	{	"Animation", "Motion"			},
	{	"Animation", "Animation Source"	},

	{	"Maths", "Vector"				},
	{	"Maths", "Matrix"				},

	{	"Physics", "Physics"			},

	{	"Scene 3D", "Item"				},

	{	"Resource", "Geometry"			},
	{	"Resource", "Material"			},
	{	"Resource", "Texture"			},
	{	"Resource", "Picture"			},

	{	"Mixer", "System"				},
	{	"Resource", "Sound"				},

	{	"Renderer", "System"			},
	{	"Renderer", "Render Job"		},
	{	"Renderer", "Terrain"			},

	{	"Renderer", "VBO"				},

	{	"Global", "Other"				}
};

//------------------------------------------------------------------------------
size_t				GetAdjustedAllocationSize(size_t size)
{	return size + sizeof(Header);	}
void				*SetupAllocationStat(void *addr, size_t size)
{
	Header *h = (Header *)addr;
	h->size = size;
	return (void *)(h + 1);
}
void				*GetAllocationStat(void *addr, Header *&h)
{
	h = ((Header *)addr) - 1;
	return (void *)h;
}
//------------------------------------------------------------------------------

static Mutex stat_mutex;

//------------------------------------------------------------------------------
void				UpdateStatAlloc(size_t size, System system)
{
	MutexLock lock(&stat_mutex);

	++system_stat[system].alloc_count;
	++system_stat[system].alive_count;
	if	(system_stat[system].alive_count > system_stat[system].alive_count_peak)
		system_stat[system].alive_count_peak = system_stat[system].alive_count;
	system_stat[system].size += size;
	if	(system_stat[system].size > system_stat[system].size_peak)
		system_stat[system].size_peak = system_stat[system].size;
}
void				UpdateStatDelete(size_t size, System system)
{
	MutexLock lock(&stat_mutex);

	system_stat[system].alive_count--;
	system_stat[system].size -= size;
}
//------------------------------------------------------------------------------

	} // Alloc
} // GS


#endif	// __ENABLE_ALLOCATION_STAT__


#include "container/narray.h"

//------------------------------------------------------------------------------
class	SmallBlockAllocatorPool
{
		void		*root;
		char		*pool, *pool_end;

public:

		bool		Owns(void *p) const
		{	return (p >= (void *)pool) && (p < (void *)pool_end);	}

		void		*Alloc()
		{
			if	(!root)
				return NULL;

			void *p = root;
			root = *((void **)root);
			return p;
		}
		void		Free(void *p)
		{
			*((void **)p) = root;
			root = p;
		}

		bool		Init(size_t block_size, uint block_count)
		{
			Uninit();

			pool = (char *)malloc(block_size * block_count);
			if	(pool == NULL)
				return false;
			pool_end = pool + block_size * block_count;

			for (uint n = 0; n < (block_count - 1); ++n)
				*((void **)(pool + n * block_size)) = (void *)(pool + (n + 1) * block_size);
			*((void **)(pool + (block_count - 1) * block_size)) = NULL;

			root = (void *)pool;
			return true;
		}
		void		Uninit()
		{
			free(pool);
			root = NULL;
		}

		SmallBlockAllocatorPool()
		{
			pool = pool_end = NULL;
			root = NULL;
		}
		~SmallBlockAllocatorPool()
		{
			Uninit();
		}
};
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
class	MixedBlockAllocator
{
		SmallBlockAllocatorPool allocator[4];

public:

		void		*operator new (size_t size)
		{	return malloc(size);	}
		void		operator delete(void *addr)
		{	free(addr);		}

		void		*Alloc(size_t size)
		{
			void *p = NULL;

			if	(size <= 8)
				p = allocator[0].Alloc();
			else if	(size <= 16)
				p = allocator[1].Alloc();
			else if	(size <= 32)
				p = allocator[2].Alloc();
			else if	(size <= 64)
				p = allocator[3].Alloc();

			return p ? p : malloc(size);
		}
		void		Free(void *p)
		{
			if (allocator[0].Owns(p))
				allocator[0].Free(p);
			else if	(allocator[1].Owns(p))
				allocator[1].Free(p);
			else if	(allocator[2].Owns(p))
				allocator[2].Free(p);
			else if	(allocator[3].Owns(p))
				allocator[3].Free(p);
			else
				free(p);
		}

		bool		Init()
		{
			allocator[0].Init(8, 16000);	// 128k
			allocator[1].Init(16, 16000);	// 256k
			allocator[2].Init(32, 8000);	// 256k
			allocator[3].Init(64, 8000);	// 512k

			return true;
		}
};
//------------------------------------------------------------------------------

#if		__ENABLE_GLOBAL_SBA__

MixedBlockAllocator *mixed_allocator = NULL;

MixedBlockAllocator *GetMixedAllocator()
{
	if	(!mixed_allocator)
	{
		mixed_allocator = new MixedBlockAllocator;
		mixed_allocator->Init();
	}
	return mixed_allocator;
}

//------------------------------------------------------------------------------
void	*operator new(size_t size)
{
	__NSTAT_WRAPALLOC(GetMixedAllocator()->Alloc(size), Alloc::Global)
}
void	operator delete(void *addr)
{
	__NSTAT_WRAPDELETE(GetMixedAllocator()->Free(addr), Alloc::Global)
}
void	*operator new [] (size_t size)
{
	__NSTAT_WRAPALLOC(GetMixedAllocator()->Alloc(size), Alloc::Global)
}
void	operator delete [] (void *addr)
{
	__NSTAT_WRAPDELETE(GetMixedAllocator()->Free(addr), Alloc::Global)
}
//------------------------------------------------------------------------------

#endif

//------------------------------------------------------------------------------
void	*_align_alloc(size_t size, size_t align)
{
#ifdef _WIN32
	return _aligned_malloc(size, align);
#else
	return malloc(size);
#endif
}
void	_align_free(void *p)
{
#ifdef _WIN32
	_aligned_free(p);
#else
	free(p);
#endif
}
//------------------------------------------------------------------------------
