/**********************************************************************
 
	Copyright (C) 2003 Hirohisa MORI <joshua@nichibun.ac.jp>
 
	This program is free software; you can redistribute it 
	and/or modify it under the terms of the GLOBALBASE 
	Library General Public License (G-LGPL) as published by 

	http://www.globalbase.org/
 
	This program is distributed in the hope that it will be 
	useful, but WITHOUT ANY WARRANTY; without even the 
	implied warranty of MERCHANTABILITY or FITNESS FOR A 
	PARTICULAR PURPOSE.

**********************************************************************/

#define MEMORY_DEBUG
/*
#define DONT_USE_LOCK
*/
#include	<stdlib.h>
#include	"memory_debug.h"
#include	"lock_level.h"
#include	"save_global.h"

#ifndef DONT_USE_LOCK
#include	"task.h"
#endif



#ifdef MEMORY_DEBUG

#define ALIGN	4

int flag;

#ifndef DONT_USE_LOCK
SEM md_lock;
#endif /* DONT_USE_LOCK */

#ifdef DONT_USE_LOCK
#define LOCK	{}
#define UNLOCK	{}
#define NEW_LOCK {}
#endif /* DON_USE_LOCK */

#ifndef DONT_USE_LOCK
#define LOCK	if ( md_lock ) lock_task(md_lock);
#define UNLOCK	if ( md_lock ) unlock_task(md_lock,"unlock");
#define NEW_LOCK	md_lock = new_lock(LL_MD);
#endif /* DONT_USE_LOCK */


typedef struct mem_header {
	MEM_LIST *		mlp;
	int			size;
	char *			last_free_file;
	int			last_free_line;
} MEM_HEADER;

#define MEM_LIST_HASH_SIZE	53
#define MEM_FREE_INFO_MAX	1000
MEM_LIST ** mem_list_hash;
MEM_LIST * mem_max_list;

MEM_FREE_INFO ** mem_free_info_hash;
MEM_FREE_INFO * mem_free_time_head;
MEM_FREE_INFO * mem_free_time_tail;
int mem_free_info_len;

void ind_tick();
void list_tick();

void
init_d_alloc()
{
	NEW_LOCK
#ifndef DONT_USE_LOCK
	new_tick(list_tick,120,0);
#endif
}

void
_new_mem_hash()
{
int i;
	mem_list_hash = malloc(MEM_LIST_HASH_SIZE*sizeof(MEM_LIST*));
	mem_free_info_hash = malloc(MEM_LIST_HASH_SIZE
		*sizeof(MEM_FREE_INFO*));
	for ( i = 0 ; i < MEM_LIST_HASH_SIZE ; i ++ ) {
		mem_list_hash[i] = 0;
		mem_free_info_hash[i] = 0;
	}
}

MEM_LIST *
_search_mem_list(char * file,int line)
{
MEM_LIST * ret;
int key;
	key = line % MEM_LIST_HASH_SIZE;
	for ( ret = mem_list_hash[key] ; ret ; ret = ret->next )
		if ( ret->line == line &&
			strcmp(ret->file,file) == 0 )
			return ret;
	return 0;
}

MEM_LIST *
_new_mem_list(char * file,int line)
{
MEM_LIST * ret;
int key;
	ret = malloc(sizeof(*ret));
	key = line % MEM_LIST_HASH_SIZE;
	ret->line = line;
	ret->file = file;
	ret->size = 0;
	ret->next = mem_list_hash[key];
	mem_list_hash[key] = ret;
	return ret;
}




void *
xx_d_alloc(int size,char * file,int line)
{
MEM_HEADER * mh;
MEM_LIST * ml;
void * ret;
	LOCK;
	if ( mem_list_hash == 0 )
		_new_mem_hash();
	mh = malloc(size + sizeof(*mh));
	if ( mh == 0 ) {
		ret = 0;
	}
	else {
		ml = _search_mem_list(file,line);
		if ( ml == 0 )
			ml = _new_mem_list(file,line);
		mh->mlp = ml;
		mh->size = size;
		mh->last_free_file = 0;
		mh->last_free_line = 0;
		ml->size += size;
		ret = mh+1;
	}
	UNLOCK;
	return ret;
}



void * xx_d_calloc(int size,int el,char * file,int line)
{
char * ret;
int i;
	ret = xx_d_alloc(size*el,file,line);
	if ( ret == 0 )
		return 0;
	for ( i = 0 ; i < size*el ; i ++ )
		ret[i] = 0;
	return ret;
}

void * d_re_alloc(void * ptr,int size)
{
void * ret;
char * pp, * qq;
MEM_HEADER * mh;
MEM_LIST *ml;
int old_size;
	LOCK;
	if ( ptr == 0 ) {
		UNLOCK;
		return d_alloc(size); 
	}
	mh = ptr;
	mh = mh - 1;
	ml = mh->mlp;
	old_size = mh->size;
	if ( ml->size < old_size )
		er_panic("d_re_alloc");
	mh = realloc(mh,size + sizeof(*mh));
	if ( mh ) {
		ml->size = ml->size - old_size + size;
		mh->size = size;
		ret = mh + 1;
	}
	else {
		ml->size -= old_size;
		ret = 0;
	}
	UNLOCK;
	return ret;
}

void
_delete_free_info(MEM_FREE_INFO * fi)
{
unsigned int key;
MEM_FREE_INFO ** fip;
	key = fi->ptr % MEM_LIST_HASH_SIZE;
	fip = &mem_free_info_hash[key];
	for ( ; *fip && *fip != fi ; fip = &(*fip)->next );
	if ( *fip == 0 )
		er_panic("free");
	*fip = fi->next;
	free(fi);
}

void
_insert_free_info(void * ptr,char * file,int line)
{
unsigned int _ptr;
MEM_FREE_INFO * fi;
unsigned int key;
	_ptr = (unsigned int)ptr;
	fi = malloc(sizeof(*fi));
	fi->ptr = _ptr;
	fi->file = file;
	fi->line = line;

	key = _ptr % MEM_LIST_HASH_SIZE;
	fi->next = mem_free_info_hash[key];
	mem_free_info_hash[key] = fi;

	fi->time_next = 0;
	if ( mem_free_time_tail ) {
		mem_free_time_tail->time_next = fi;
		mem_free_time_tail = fi;
	}
	else mem_free_time_tail = mem_free_time_head = fi;
	mem_free_info_len ++;
	for ( ; mem_free_info_len > MEM_FREE_INFO_MAX ; ) {
		fi = mem_free_time_head;
		mem_free_time_head = fi->time_next;
		_delete_free_info(fi);
		mem_free_info_len --;
	}
}

void xx_d_f_ree(void * ptr,char * file,int line)
{
MEM_HEADER * mh;
MEM_LIST * ml;
	LOCK;

	mh = ptr;
	mh = mh - 1;
	if ( mh->last_free_file || mh->last_free_line )
		er_panic("xx_d_f_ree(1)");
/*	_insert_free_info(ptr,file,line);*/
	mh->last_free_file = file;
	mh->last_free_line = line;
	ml = mh->mlp;
	ml->size -= mh->size;
	free(mh);
	if ( ml->size < 0 )
		er_panic("xx_d_f_ree(2)");

	UNLOCK;
}

void
ind_tick()
{
MEM_LIST * ml;
int large;
int i;
	LOCK;
	large = 0;
	printf("IND_MEM\n");
	for ( i = 0; i < MEM_LIST_HASH_SIZE ; i ++ ) {
		ml = mem_list_hash[i];
		for ( ; ml ; ml = ml->next ) {
			if ( ml->size == 0 )
				continue;
			if ( large < ml->size ) {
				printf("*");
				large = ml->size;
			}
			printf("[%s:%i]==>%i\n",ml->file,ml->line,ml->size);
		}
	}
	printf("IND_MEM END\n");
	UNLOCK;
}

void
list_tick()
{
MEM_LIST * ml;
int large;
int i;
	LOCK;
	large = 0;
	mem_max_list = 0;
	for ( i = 0; i < MEM_LIST_HASH_SIZE ; i ++ ) {
		ml = mem_list_hash[i];
		for ( ; ml ; ml = ml->next ) {
			if ( ml->size == 0 )
				continue;
			if ( large < ml->size ) {
				large = ml->size;
				ml->max_next = mem_max_list;
				mem_max_list = ml;
			}
		}
	}
	UNLOCK;
}

MEM_LIST *
get_mem_debug_list(MEM_LIST * m)
{
int key;
MEM_LIST * ret;
	LOCK;
	if ( mem_list_hash == 0 ) {
		goto end;
	}
	if ( m == 0 )
		key = 0;
	else {
		if ( m->next ) {
			ret = m->next;
			goto end;
		}
		key = (m->line % MEM_LIST_HASH_SIZE)+1;
	}
	if ( key >= MEM_LIST_HASH_SIZE ) {
		ret = 0;
		goto end;
	}
	for ( ; key < MEM_LIST_HASH_SIZE &&
			mem_list_hash[key] == 0 ; key ++ );
	if ( key >= MEM_LIST_HASH_SIZE ) {
		ret = 0;
		goto end;
	}
	ret = mem_list_hash[key];
end:
	UNLOCK;
	return ret;
}

#endif /* MEMORY_DEBUG */

#ifndef MEMORY_DEBUG


void
init_d_alloc()
{
}

void *
xx_d_alloc(int size,int d,char * file,int line)
{
	return malloc(size);
}

void *
xx_d_calloc(int size,int el,int d,char * file,int line)
{
	return calloc(size,el);
}

void *
d_re_alloc(void * ptr,int size)
{
	return realloc(ptr,size);
}

void
xx_d_f_ree(void * ptr,char * file,int line)
{
	free(ptr);
}

MEM_LIST * 
get_mem_debug_list(MEM_LIST * m)
{
	return 0;
}


#endif /* ndef MEMORY_DEBUG */

