// Copyright (C) 2004-2009 Christoph Kubisch & Eike Decker
// This file is part of the "Luxinia Engine".
// For conditions of distribution and use, see copyright notice in luxiniapublicsdk.txt

#include <luxcore/memorycluster.h>
#include <luxcore/debug.h>

//////////////////////////////////////////////////////////////////////////
// Memory Cluster
/**
* Author: Eike Decker
* Allows to allocate a chunk of data which is parted
* into clusters. You can malloc only one cluster per time. Usefull for data
* structures with constant size like listnodes. There is no guarantee that
* two malloced variables will be adjacent in the memory.
*/

typedef struct MemoryCluster_s {
	size_t	clusterSize;
	ptrdiff_t clusterCount;

	char *data;
	char **freeClusterStack;
	size_t	*usedMemory;
	ptrdiff_t stackPointer;
} MemoryCluster_t;


// constructs a new memorystructure
MemoryCluster_t* MemoryCluster_new (size_t clusterSize, size_t clusterCount)
{
	MemoryCluster_t* memg;
	size_t i;

	//if (clusterMutex == -1) clusterMutex = critical_create();

	memg = (MemoryCluster_t*) MEMORY_GLOBAL_MALLOC(sizeof(MemoryCluster_t));
	memg->data = (char*) MEMORY_GLOBAL_MALLOC(clusterSize*clusterCount);
	memg->freeClusterStack = (char**) MEMORY_GLOBAL_MALLOC(clusterCount*sizeof(void*));
	memg->clusterCount = clusterCount;
	memg->clusterSize = clusterSize;
	memg->stackPointer = 0;
	memg->usedMemory = (size_t*) MEMORY_GLOBAL_MALLOC (clusterCount/32+1);

	for (i=0;i<clusterCount;i++) {
		memg->freeClusterStack[i]= (memg->data+clusterSize*i);
		memg->usedMemory[i/32]=0;
	}
	return memg;
}

void * MemoryCluster_mallocVar (MemoryCluster_t * mem) {
	if (mem->stackPointer >= mem->clusterCount) {
		LUX_PRINTF("ERROR: MemClusterAlloc failed\n");
		LUX_ASSERT(0);
		return NULL;
	}
	{
		ptrdiff_t i=mem->freeClusterStack[mem->stackPointer]-mem->data;
		if (mem->usedMemory[i/32]&(1<<(i%32))) {
			LUX_PRINTF("ERROR: MemClusterAlloc failed\n");
			LUX_ASSERT(0);
		}
		mem->usedMemory[i/32]=mem->usedMemory[i/32]|(1<<(i%32));
		return mem->freeClusterStack[mem->stackPointer++];
	}
}

void MemoryCluster_freeVar (MemoryCluster_t * mem, void *ptrv) {
	ptrdiff_t i;
	char* ptr ;

	ptr = (char*)ptrv;
	i = ptr-mem->data;
	if (mem->usedMemory[i/32]&(1<<(i%32)))
		mem->usedMemory[i/32]=mem->usedMemory[i/32]&(~1<<(i%32));
	else {
		LUX_PRINTF("ERROR: MemClusterFree failed\n");
		LUX_ASSERT(0);
		return;
	}
	if (mem->stackPointer<=0) {
		return;
	}
	mem->usedMemory[--mem->stackPointer] = (size_t)ptr;
}

void MemoryCluster_free (MemoryCluster_t *mem) {
	MEMORY_GLOBAL_FREE(mem->data);
	MEMORY_GLOBAL_FREE(mem->freeClusterStack);
	MEMORY_GLOBAL_FREE(mem->usedMemory);
	MEMORY_GLOBAL_FREE(mem);
}
