/** SGX

The   sss    gggg x   x   
     s      g      x x    Engine
      sss   g  gg   x
         s  g   g  x x          Copyright 1995-2009.
     ssss    gggg x   x         Steven Goodwin. All Rights Reserved.
    
This version has been released under the GNU Public License v2.0.

Please see the license files for details.

File: sgx/src/sgx/core/memory/allocbase.cpp

*/

#include "sgx/core/core.hpp"

/*
MEMORY
 
Q. How to get FILE and LINE into the calls, with existing sgx Macros?
Q. How to get info block from block that's been aligned by the general
alloc() method?
A. ALWAYS have four bytes preceding the returned memory which points to
an internal structure. This ensures it's always valid, and means
allocators can use any method (i.e. info blocks inline with data, or a
separate table).
NOTE: all returned ptrs must be aligned to CSGXMemoryInformationBlock *
so we can safely extract it from whatever address is passed back
 
Q. #define SGX_CPU_POINTER_ALIGNMENT	4		// usually
sizeof(tSIZET). For reading pointers, i.e. dword
 
On static free (ptr)
pSpecificBlock = ptr-SGX_CPU_POINTER_ALIGNMENT;
pSpecificBlock->pAllocator->free(ptr)
- this specific block is a static of each allocator to save space.
- it may instead be a ptr to some bytes N beforehand with the specific
info
 
Alloc:
maxSize = housekeeping with rounding, backpointer, current, with
rounding
maxSize roundup(sizeof(housekeeping)+ SGX_CPU_POINTER_ALIGNMENT) +
sizeof(tMEM_SIZE) + = requestedSize+rounding
 
 
 
Each memory block is essentially:
 
+--------------+---+---------+---+------------+
|              | p |         | p |            |
| Housekeeping | a | Backptr | a | Users data |
|              | d | to meta | d |            |
+--------------+---+---------+---+------------+
 
 
 
// for c/cpp we have no control over the ptr, so we request more than we
need and align
size += ... etc ...
 
// Fix the alignment for the user block to ensure the back
// pointer is suitably aligned
if (dataAlignment < SGX_CPU_POINTER_ALIGNMENT) {
	dataAlignment = SGX_CPU_POINTER_ALIGNMENT;
}
 
KeeperHelper(CSGXMemoryHouseKeeping *&keeper, tMEM_SIZE keeperSize) {
// for others, e.g. linear we calculate it from the current ptr
pHouseKeeping = sgxAlign32(m_pCurrent, SGX_CPU_POINTER_ALIGNMENT);
 
pUserData = sgxAlign32(pHouseKeeping + sizeof(CSGXMemoryHelperExternal)
+ sizeof(tMEM_SIZE), dataAlignment);
 
// write the backptr
*(tMEM_SIZE *)(pUserData-sizeof(tMEM_SIZE)) = pHouseKeeping;
 
return pUserData;
 
CSGXMemoryHouseKeeping {
};
 
CSGXMemoryHouseKeepingExternal : public CSGXMemoryHouseKeeping {
public:
	void *pRealPointer;	// as returned by an external allocator.
E.g. new or malloc
};
 
 
pFirstPossibleMemoryLocation = sgxAlignUp(ptr, sizeof(tMEM_SIZE)); //
possible for CSGXMemoryHelperExternal, e.g.
 
ptr = sgxAlignUpForce(pFirstPossibleMemoryLocation, alignment);
pDataBlock = ptr - sizeof(tMEM_SIZE);
*pDataBlock = ???
 
Always roundup, even if it's on a boundary
 
sgxDeviceDriverType::setAllocator. For e.g. graphics, strings. Q. Can
this be included in the base class, and new overridden correctly for all
sub-classes? ote. allocartor should be base class, with sub-classes of
sgxMemoryAllocatorPool, sgxMemoryAllocatorScratch,
sgxMemoryAllocatorHeap.

Linear : new allocs are added to end, and released en-masse on freeAll
 
NOTE: Memory block must contain the allocator

 */
 

CSGXMemoryAllocator *CSGXMemoryAllocator ::m_pDefaultAllocator;
CSGXMemoryAlignment CSGXMemoryAllocator::m_DefaultAlignment;
 
CSGXMemoryAllocator::CSGXMemoryAllocator(void *ptr, const tMEM_SIZE size, const CSGXMemoryAlignment &alignment) {
 	m_pBlock = (tBYTE *)ptr; 
	m_iBlockSize = size; 
	m_Alignment = alignment;
}

CSGXMemoryAllocator *CSGXMemoryAllocator::setDefault(CSGXMemoryAllocator *pNewDefault) {
	CSGXMemoryAllocator *prev = m_pDefaultAllocator;   
	m_pDefaultAllocator = pNewDefault;
	return prev;
}
 
void *CSGXMemoryAllocator::alloc(const tMEM_SIZE size, const CSGXMemoryAlignment &alignment, const char *pFilename, const int lineNumber) {
	tMEM_SIZE newSize = size;

 SGX_UNUSED(pFilename);
 SGX_UNUSED(lineNumber);
 
	if (newSize < alignment.m_MinimumSize) {
		newSize = alignment.m_MinimumSize;
	}
 
	newSize = sgxAlignUp(newSize, alignment.m_SizeAlignment);
 
	newSize += (1<<alignment.m_StartAlignment)-1;
 
	void *ptr = m_pDefaultAllocator->alloc(newSize);

	return ptr;
}
 
void *CSGXMemoryAllocator::calloc(const tMEM_SIZE typeSize, const tMEM_SIZE typeCount, const CSGXMemoryAlignment &alignment, const char *pFilename, const int lineNumber) {
	tMEM_SIZE size = typeSize * typeCount;
	void *ptr = alloc(size, alignment, pFilename, lineNumber);
	if (ptr) {
		sgxMemset(ptr, '\0', size);
	}
	return ptr;
}
 
void *CSGXMemoryAllocator::realloc(void *prev, const tMEM_SIZE size, const CSGXMemoryAlignment &alignment, const char *pFilename, const int lineNumber) {
	void *ptr = alloc(size, alignment, pFilename, lineNumber);
	if (ptr) {
		tMEM_SIZE oldSize = getSize(prev);
		tMEM_SIZE copySize = sgxMin<tMEM_SIZE>(oldSize, size);
		sgxMemcpy(ptr, prev, copySize);
		sgxFree(prev);
	}
	return ptr;
}

void *CSGXMemoryAllocator::alloc(const tMEM_SIZE size) {
	if (m_pDefaultAllocator) {
		return m_pDefaultAllocator->alloc(size);
	}
	return NULL;
}
 

 #if 0
static void *CSGXMemoryAllocator::alloc(const tMEM_SIZE size, const CSGXMemoryAlignment &alignment) {
	if (m_pDefaultAllocator) {
		return m_pDefaultAllocator->alloc(size, alignment);
	}
	return NULL;
}
#endif

CSGXMemoryAllocator *CSGXMemoryAllocator::getAllocator(void *ptr) {
	tSIZET *pBackPtrAddress = (tSIZET *)(((tBYTE *)ptr) - sizeof(tSIZET));
	tSIZET *pAllocPtr = (tSIZET *)*pBackPtrAddress;
	CSGXMemoryAllocator *pAllocator = (CSGXMemoryAllocator *)(*pAllocPtr);
	return pAllocator;
}

tMEM_SIZE CSGXMemoryAllocator::getSize(void *ptr) {
	if (ptr) {
		CSGXMemoryAllocator *pAllocator = getAllocator(ptr);
		return pAllocator->getBlockSize(ptr);
	}
	return 0;
}
 
tBOOL CSGXMemoryAllocator::free(void *ptr) {
	if (ptr) {
		CSGXMemoryAllocator *pAllocator = getAllocator(ptr);
		return pAllocator->freePtr(ptr);
	}
	return FALSE;
}
 
tBOOL CSGXMemoryAllocator::freePtr(void *ptr) {
	if (ptr) {
		CSGXMemoryAllocator *pAllocator = getAllocator(ptr);
		return pAllocator->freePtr(ptr);
	}
	return FALSE;
}
 
tBOOL CSGXMemoryAllocator::freeAll() {
	return FALSE;	// i.e. not supported
}
 
tBOOL CSGXMemoryAllocator::reportAll() {
	return FALSE;	// i.e. not supported
}
 