#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <time.h>
#define FALSE 0
#define TRUE (!FALSE)
#define LOW_MAIN 1
#include "low.h"

//#define TESTING 1
#ifdef TESTING
#define NTEST 10000
int main()
{
	char *s[10000];
	int i;
	int tot=0;
	int k;
	int start=time(NULL);
	low_init();
	for (k=0; k<120; k++) {
		for (i=0; i<NTEST; i++) {
			s[i] = low_malloc(10+i,"SRC1",1);
			tot += 10+i;
			if (i%3==1) {
				low_free(s[i],"sdf",3);
				s[i] = NULL;
				tot -= 10+i;
			}
		}
		for (i=0; i<NTEST; i++) {
			if (s[i]!=NULL) {
				low_free(s[i],"src",2);
				tot -= 10+i;
			}
			s[i] = NULL;
		}
	}
	low_check("test1");
	printf("tot %d %d sec\n",tot,time(NULL)-start);
	return 0;
}
#endif


#define CHUNK 500
typedef struct MBLOCK {int size; int nfree; int n; int i; char *src[CHUNK]; int line[CHUNK]; int used[CHUNK]; char *data; struct MBLOCK *next; } Mblock;
static Mblock *mtable[1000];
static Mblock *mtable_last[1000];
static Mblock *mtable_free[1000];
static int mtable_size[1000];
static int ntable;
void low_abort(Mblock *m, int i, int size, char *src, int line, int k, int v);
void *low_malloc_doit(int t, int size, char *src, int line);

static int lowtot;
int low_tot(void)
{
	return lowtot;
}
Mblock *low_add(int i, int size)
{
	Mblock *m;
	m = calloc(1,sizeof(Mblock));
//	printf("low add %p\n",m);
	m->size = size;
	m->n = CHUNK;
	if (size>1000000) m->n = 4;
	if (m->size*m->n>100000) {
		m->n = 100000 / m->size;	
		if (m->n<=0) m->n = 1;
	}
	m->data = malloc(m->n*m->size);
	lowtot += m->n*m->size;
//	printf("low_add, [%d] size %d n=%d tot=%d\n",i,size,m->n,tot);
	m->nfree = m->n;
	m->next = mtable[i];
	memset(m->data,0x07,m->n*m->size);
	mtable[i] = m;
	return m;
}
void low_init(void)
{
	int i;
	static int done;
	int size=8;
	if (done) return;
	done = TRUE;
	for (i=0; size<500000000; size = size * 2,i++) {
		mtable_size[i] = size;
//		low_add(i,size);
		ntable = i+1;
	}
}
void *low_malloc(int size, char *src, int line)
{
	//  20 40 80 160 320 600 1200 2000 4000 ....
	int i;
	for (i=0; i<ntable; i++) {
		if (mtable_size[i]>=size) return low_malloc_doit(i,size,src,line);
	}
	return malloc(size);
}
void low_free_doit(Mblock *m, void *pp, char *src, int line)
{
	int r;
	char *p = (char *) pp;
	if (p>=m->data 	&& p<m->data+m->size*m->n) {
		r = (p - m->data) / m->size;
		if (!m->used[r]) low_abort(m,0,0,"not_used",line,0,0);
		m->used[r] = FALSE;
		m->nfree ++ ;
		memset(p,0x07,m->size);
		return;
	}
	abort();
}
Mblock *low_free_find(void *p);

void *low_strdup(const char *str,  char *src, int line)
{
	int sz;
	char *x;
	sz = strlen(str)+1;
	x = low_malloc(sz,src,line);
	strcpy(x,str);
	return x;
}
void *low_calloc(int num, int size,  char *src, int line)
{
	void *x;
	x = low_malloc(num*size,src,line);
	memset(x,0,num*size);
	return x;
}
void low_free(void *p, char *src, int line)
{
	Mblock *m;
	m = low_free_find(p);
	if (m==NULL) low_abort(m,0,0,"LOW_FREE_NOT_FOUND",line,0,0);
	low_free_doit(m, p, src,line);
}
void *low_realloc(void *p, int size, char *src, int line)
{
	Mblock *m;
	void *newp;
	m = low_free_find(p);
	if (m==NULL) low_abort(m,0,size,"LOW_realloc_NOT_FOUND",line,0,0);
	if (m->size>=size) return p;	
	newp = low_malloc(size,src,line);
	memcpy(newp,p,m->size);
	low_free(p,src,line);
	return newp;
}
Mblock *low_free_find(void *pp)
{
	static int lastfree;
	int i;
	char *p = (char *) pp;
	Mblock *m;
again:
	for (i=lastfree; i<ntable; i++) {
		m=mtable_free[i];
		if (m==NULL || p<m->data 	|| p>=m->data+m->size*m->n) {
			m=mtable[i];
		}
		for (;m!=NULL;m=m->next) {
			if (p>=m->data 	&& p<m->data+m->size*m->n) {
				lastfree = i;
				mtable_free[i] = m;
				return m;
			}
		}
	}
	if (lastfree!=0) {
		lastfree = 0;
		goto again;
	}
	return NULL;
}
void *low_malloc_doit(int t, int size, char *src, int line)
{
	int *v;
	int start;
	int i;
	int k;
	char *p;
	int sz=mtable_size[t];
	Mblock *m = mtable_last[t];	
	if (m==NULL || m->nfree<=0) m = mtable[t];
	for (;m!=NULL;m=m->next) {
		if (m->nfree<=0) continue;
		start = m->i;
again:	for (i=start; i < m->n; i++) {
			if (m->used[i]) continue;
			mtable_last[t] = m;
			m->i = i + 1;
			m->src[i] = src;
			m->line[i] = line;
			m->used[i] = TRUE;
			m->nfree--;
			p = m->data + i * m->size;
			v = (int *) p;
			for (k=0; k<m->size/4; k++) {
				if (v[k]!=0x07070707) low_abort(m,i,size,src,line,k,v[k]);
			} 
			memset(p,2,m->size);
			return p;
		}
		if (start!=0) {
			start = 0; 
			goto again;
		}
	}
	m = low_add(t,sz);
	start = 0;
	goto again;
}
void low_check(char *whence)
{
	Mblock *m;
	int i;
	int c;
	int k;
	int *v;
	char *p;
	for (i=0; i<ntable; i++) {
		for (m=mtable[i];m!=NULL;m=m->next) {
			for (c=0; c<m->n; c++) {
				if (!m->used[c]) {
					p = m->data + c * m->size;
					v = (int *) p;
					for (k=0; k<m->size/4; k++) {
						if (v[k]!=0x07070707) low_abort(m,c,0,whence,0,k,v[k]);
					} 
				}
			}
		}
	}
	return ;
}
static void (*abort_fn)(int x);
void low_set_abort(void (*fn)(int x))
{
	abort_fn = fn;
}
void low_abort(Mblock *m, int i, int size, char *src, int line, int k, int v)
{
	FILE *f;
	f = fopen("startstop.log","a");
	if (f!=NULL) {
		printf("low_malloc abort: %s:%d  %s:%d offset %d value %x\n",src ? src : "",line,m->src[i] ? m->src[i]:"",m->line[i],k,v);
		fprintf(f,"low_malloc abort: %s:%d  %s:%d offset %d value %x\n",src ? src : "",line,m->src[i] ? m->src[i]:"",m->line[i],k,v);
		fclose(f);
//		printf("low_malloc abort: %s:%d  %s:%d offset %d value %x\n",src,line,m->src[i],m->line[i],k,v);
	}
	abort_fn(45);
}
