#include <../source/TAL/objbase.h>
#include <../source/PAL/dataman.h>

/*
 *	Part 1: memory management.
 */

#ifdef	MALLOC_DEBUG

/*
 * User - supplied tag to attach to memory blocks
 */
void	V_GLOBAL
memSetOpaque(unsigned opaque)
{
	GetThreadData()->mem_opaque=opaque;
}

/*
 *	List - based memory routines
 */
V_LOCAL	mem_list_entry_p
libvAllocEntry(size_t data_size)
{
    size_t cksize;
	mem_list_entry_t *entry;
    cksize=sizeof(mem_list_entry_t) + data_size;
	entry=(mem_list_entry_p)malloc(cksize);
    if(!entry)
    {
#ifdef	CHECKED_BUILD
    	conlPrintf(LOG_ERROR,
        	"libvAllocEntry(): malloc(%u) failed.\n",data_size);
#endif	/* CHECKED_BUILD */
        return NULL;
    }
	entry->next=NULL;
    entry->size=data_size;
    entry->opaque=GetThreadData()->mem_opaque;
    entry->data=entry+1;
    return entry;
}

V_LOCAL	vboolean
libvPointerInList(mem_list_entry_t *head,void *ptr)
{
	while(head->next)
    {
    	/* Skip first (head) node */
    	head=head->next;
    	if(head->data == ptr)
			return true;
	}
    return false;
}

#ifdef	CHECKED_BUILD
void	libv_null_pointer()
{
}
#endif	/* CHECKED_BUILD */

V_LOCAL void
libvFree(mem_list_entry_t *head,void *entry_data)
{
	mem_list_entry_t *entry;
	while(head->next)
    {
    	if(head->next->data == entry_data)
        {
        	entry=head->next;
           	head->next=entry->next;
        	/* Note: we must libvHashUnref() here */
        	libvHashUnref();
			/*
			 * Erase first dword of data block.
			 */
			MARK_OBJECT(entry->data,LIBV_INVALID_MAGIC);
        	free(entry);
        	return;
		}
    	head=head->next;
	}
#ifdef	CHECKED_BUILD
	conlPrintf(LOG_ERROR,
		"libvFree(): no pointer 0x%0000000X in hash.\n",entry_data);
	if(entry_data == NULL)
		libv_null_pointer();
#endif	/* CHECKED_BUILD */
	return;
}

/*
 *	Hash - based memory routines
 */
void_p	V_GLOBAL
memAlloc(size_t size)
{
	mem_list_entry_t *head, *entry;
    if(!size)
    	return NULL;
    entry=libvAllocEntry(size);
    if(!entry)
    	return NULL;
   	head=libvLockMemoryHash() + MEMORY_HASHPROC(entry->data);
	while(head->next)
   		head=head->next;
	head->next=entry;
	/* new node inserted, so increase hash reference count */
	libvHashRef();
	libvUnlockGlobal();
    return entry->data;
}

vboolean	V_GLOBAL
memIsPointer(void *ptr)
{
	vboolean result;
	mem_list_entry_t *head;
   	head=libvLockMemoryHash() + MEMORY_HASHPROC(ptr);
   	result=libvPointerInList(head,ptr);
	libvUnlockGlobal();
	return result;
}

void	V_GLOBAL
memFree(void *ptr)
{
	mem_list_entry_t *head;
   	head=libvLockMemoryHash() + MEMORY_HASHPROC(ptr);
	libvFree(head,ptr);
	libvUnlockGlobal();
}

/*
 *	Note: tag remains unchanged !
 */
void_p	V_GLOBAL
memRealloc(void *ptr,size_t newsize)
{
	mem_list_entry_t *hash, *head, *entry;
    if(!newsize)
    {
    	memFree(ptr);
        return NULL;
    }
	if(!ptr)
        return memAlloc(newsize);
    /* Find entry */
    hash=libvLockMemoryHash();
   	head=hash + MEMORY_HASHPROC(ptr);
	while(head->next)
    {
    	if(head->next->data == ptr)
        	break;
    	head=head->next;
	}
    entry=head->next;
    if(!entry)
    {
#ifdef	CHECKED_BUILD
		conlPrintf(LOG_ERROR,
			"memRealloc(): no pointer 0x%0000000X in memory hash.\n",ptr);
#endif	/* CHECKED_BUILD */
    	libvUnlockGlobal();
        return ptr;
	}
    /* Remove entry from hash */
	head->next=entry->next;
    /* Reallocate memory */
	entry=(mem_list_entry_t*)realloc(entry,sizeof(mem_list_entry_t)+newsize);
    /* Note: we won't change 'opaque' */
    entry->next=NULL;
    entry->size=newsize;
    entry->data=entry+1;
    /* Add entry to hash */
   	head=hash + MEMORY_HASHPROC(entry->data);
	while(head->next)
   		head=head->next;
	head->next=entry;
	libvUnlockGlobal();
    return entry->data;
}

void	V_GLOBAL
memStats(u32_t *bytes_ret,u32_t *blocks_ret,u32_t *handles_ret)
{
	int i;
    unsigned pc=0,oc=0,mc=0;
	mem_list_entry_t *head;
	global_data_t *gd=libvLockGlobal();
	if(gd->object_hash != NULL)
	{
	    for(i=0;i<=OBJECT_HASH_LIMIT;i++)
    	{
	    	head=(gd->object_hash+i)->next;
			while(head)
    		{
	            mc+=head->size;
    	        oc++;
        	    head=head->next;
			}
		}
	}
	if(gd->memory_hash != NULL)
	{
	    for(i=0;i<=MEMORY_HASH_LIMIT;i++)
    	{
    		head=(gd->memory_hash+i)->next;
			while(head)
    		{
        	    mc+=head->size;
	            pc++;
    	        head=head->next;
			}
		}
	}
	if(bytes_ret)
		*bytes_ret=mc;
	if(blocks_ret)
		*blocks_ret=pc;
	if(handles_ret)
		*handles_ret=oc;
	libvUnlockGlobal();
}

void	V_GLOBAL
memPrintObjStats(void)
{
	int i;
	mem_list_entry_t *head;
	global_data_t *gd=libvLockGlobal();
	if(gd->object_hash != NULL)
	{
	    for(i=0;i<=OBJECT_HASH_LIMIT;i++)
    	{
	    	head=(gd->object_hash+i)->next;
			while(head)
    		{
#ifdef	CHECKED_BUILD
    			char *magic=(char*)head->data;
				conlPrintf(LOG_TRACE,"Object '%c%c%c%c' (%X): %u bytes (opaque %u)\n",
					magic[3],magic[2],magic[1],magic[0],
					head->data,
					head->size,
					head->opaque);
#else	/* !CHECKED_BUILD */
				conlPrintf(LOG_TRACE,"Object %X: %u bytes (opaque %u)\n",
					head->data,
					head->size,
					head->opaque);
#endif	/* !CHECKED_BUILD */
				head=head->next;
			}
		}
	}
	libvUnlockGlobal();
}

void	V_GLOBAL
memPrintMemStats(void)
{
	int i;
	mem_list_entry_t *head;
	global_data_t *gd=libvLockGlobal();
	if(gd->memory_hash != NULL)
	{
	    for(i=0;i<=MEMORY_HASH_LIMIT;i++)
    	{
	    	head=(gd->memory_hash+i)->next;
			while(head)
    		{
				conlPrintf(LOG_TRACE,"Block %X: %u bytes (opaque %u)\n",
					head->data,
					head->size,
					head->opaque);
				head=head->next;
			}
		}
	}
	libvUnlockGlobal();
}

/*
 *	Part 2: object management.
 */
vhandle_t V_CALL
memAllocObject(size_t size)
{
	mem_list_entry_t *head, *entry;
    entry=libvAllocEntry(size);
    if(!entry)
    	return NULL;
   	head=libvLockObjectHash() + OBJECT_HASHPROC(entry->data);
	while(head->next)
   		head=head->next;
	head->next=entry;
	/* new node inserted, so increase hash reference count */
    libvHashRef();
    memZero(entry->data,size);
    libvUnlockGlobal();
    return entry->data;
}

/*
**	Returns 'true' if 'ptr' was allocated by memAllocObject().
*/
vboolean	V_CALL
memIsObject(vhandle_t ptr)
{
	vboolean result;
	mem_list_entry_t *head;
   	head=libvLockObjectHash() + OBJECT_HASHPROC(ptr);
	result=libvPointerInList(head,ptr);
	libvUnlockGlobal();
	return result;
}

/*
**	Frees memory block previosly allocated by memAllocObject().
**	Should be used only in destructors.
*/
void	V_CALL
memFreeObject(vhandle_t ptr)
{
	mem_list_entry_t *head;
   	head=libvLockObjectHash() + OBJECT_HASHPROC(ptr);
	libvFree(head,ptr);
	libvUnlockGlobal();
}

#else	/* !MALLOC_DEBUG */

/*
 *	Note: changing 32-bit integers is an atomic operation,
 *	so it's safe NOT to move counters into protected section.
 */
V_LOCAL	u32_t	pointer_count = 0;
V_LOCAL	u32_t	handle_count = 0;

void_p	V_GLOBAL
memAlloc(size_t size)
{
    void *ptr=malloc(size);
    if(ptr!=NULL)
    	pointer_count++;
    return ptr;
}

vhandle_t V_CALL
memAllocObject(size_t size)
{
    void *ptr=malloc(size);
    if(ptr!=NULL)
		handle_count++;
	return ptr;
}

vboolean	V_GLOBAL
memIsPointer(void *ptr)
{
	return ptr != NULL ? true : false;
}

vboolean	V_CALL
memIsObject(vhandle_t ptr)
{
	return ptr != NULL ? true : false;
}

void	V_GLOBAL
memFree(void *ptr)
{
	free(ptr);
	pointer_count--;
}

void	V_CALL
memFreeObject(vhandle_t ptr)
{
	free(ptr);
	handle_count--;
}

void_p	V_GLOBAL
memRealloc(void *ptr,size_t newsize)
{
	return realloc(ptr,newsize);
}

void	V_GLOBAL
memStats(u32_t *bytes_ret,u32_t *blocks_ret,u32_t *handles_ret)
{
	if(bytes_ret)
		*bytes_ret=alloc_count;
	if(blocks_ret)
		*blocks_ret=pointer_count;
	if(handles_ret)
		*handles_ret=handle_count;
	libvUnlockGlobal();
}

#endif	/* !MALLOC_DEBUG */

void	V_GLOBAL
memPrintStats(void)
{
    u32_t pc,oc,mc;
    memStats(&mc,&pc,&oc);
#ifdef	MALLOC_DEBUG
	conPrintf("memStats(): %u bytes in %d blocks "
		"and %u handles.\n",mc,pc,oc);
#else	/* !MALLOC_DEBUG */
	conPrintf("memStats(): %d blocks, %u handles.\n",pc,oc);
#endif	/* !MALLOC_DEBUG */
}

/*
 * Here (at default destructor) we can only decrement reference
 * counter and self-destroy if it reaches zero. Thus, for all objects
 * which perform dynamic memory/resource allocation, own object-specific
 * destructors must be defined.
 */
u32_t	V_CALL
libv_object_destructor(vhandle_t handle)
{
	/* Note: we assume 'handle' is valid, because it was tested
	 * for validity in wrapper - libvCloseHandle(). */
	libv_object_t *self=(libv_object_t *)handle;
	self->ref_count--;
    if(!self->ref_count)
    {
#ifdef	CHECKED_BUILD
		conlPuts(LOG_WARNING,"libv_object_destructor(): default "
			"destructor called; probably broken code.\n");
#endif	/* CHECKED_BUILD */
		memFreeObject(self);
        return 0;
	}
    return self->ref_count;
}

/*
 * Quiet version of above function; if caller wasn't allocated
 * any resources, it can use this as destructor.
 */
u32_t	V_CALL
libv_object_destructor_quiet(vhandle_t handle)
{
	libv_object_t *self=(libv_object_t *)handle;
	self->ref_count--;
    if(!self->ref_count)
    {
		memFreeObject(self);
        return 0;
	}
    return self->ref_count;
}

#ifdef	CHECKED_BUILD

void_p	V_CALL
libvIsHandle(vhandle_t handle,char *caller,char *object,
	u32_t magic)
{
	if(!memIsObject(handle))
	{
		conlPrintf(LOG_ERROR,
			"%s(): descriptor 0x%08X not owned by object manager.\n",
        	caller,handle);
		return NULL;
	}
	if(*((u32_t*)handle) != magic)
	{
		conlPrintf(LOG_ERROR,"%s(): %s damaged: bad magic 0x%08X.\n",
			caller,object ? object : "object");
		return NULL;
	}
	return handle;
}

/*
 * Note: this procedure only calls object - specific destructor.
 * It's up to object's destructor to manipulate with reference
 * counter.
 */
u32_t	V_CALL
libvCloseHandle(vhandle_t handle,char *caller,char *object,
	u32_t magic)
{
	libv_object_t *obj;
    obj=(libv_object_t*)libvIsHandle(handle,caller,object,magic);
	if(!obj)
    	return 0; /* Invalid object: say 'NOT EXIST' */
	/* Derefernce / destroy object */
	return obj->destroy(obj);
}

#endif	/* CHECKED_BUILD */

/*
 *	Quiet version of libvCloseHandle()
 */
u32_t	V_CALL
__libvCloseHandle(vhandle_t handle)
{
	libv_object_t *obj;
	if(!memIsObject(handle))
    {
#ifdef	CHECKED_BUILD
		conlPrintf(LOG_ERROR,
        	"libvCloseHandle(): invalid descriptor %X.\n",handle);
#endif	/* CHECKED_BUILD */
		obj=NULL;
    }
    else
	    obj=(libv_object_t*)handle;
	if(!obj)
    	return 0;
	/* Derefernce / destroy object */
	return obj->destroy(obj);
}

