#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <math.h>
#include <time.h>
#include <errno.h>
#include <stdarg.h>
#ifndef TRUE
#define FALSE 0
#define TRUE (!FALSE)
#endif
#include "defines.h"

#include "lib.h"
#include "lprintf.h"

#define DMID 15
#include "debug.h"
#include "fmalloc.h"


#define CHUNKSZ 1000

Fmalloc *fmalloc_new()
{
	return calloc(1,sizeof(Fmalloc));	
}
int fmalloc_align(int sz)
{
	int rest = sz % 8; // 64 bit alignment.  
	if (rest==0) return sz;
	return sz + (8-rest);
}
void *fmalloc_calloc_real(Fmalloc *m, int count, int sz, char *file, int line)
{
	void *p = fmalloc_real(m,count*sz,file,line);
	memset(p,0,count*sz);
	return p;
}
void *fmalloc_real(Fmalloc *m, int sz, char *file, int line)
{
	// 	find fmalloc one that has free chunk of right size, or create one, add allocate it.
	Fchunk *f;
	int at;
	sz = fmalloc_align(sz);
	for (f=m->head;f!=NULL;f=f->next) {
		if (f->size!=sz) continue; // skip wrong size ones.  
		if (f->nused<f->max) break;
	}
	if (f==NULL) {
		f = d_callocid(DMID,1,sizeof(Fchunk),file,line);  // 
		f->max = CHUNKSZ;
		if (m->head!=NULL) { 
			if (f->max*sz < 5000) f->max *= 10;  // first one 1k, second one 10k.   
			if (f->max*sz > 1000000) f->max = 100;
		}
		f->mem = d_callocid(DMID,f->max,sz,file,line); // 400k...
		f->size = sz;
		f->isused = calloc(f->max,1);
		f->next = m->head;
		m->head = f;
	}
	if (sz!=f->size) lib_abort();
	for (at=f->at;at<f->max;at++) {
		if (!f->isused[at]) goto foundit;
	}
	for (at=0;at<f->at;at++) {
		if (!f->isused[at]) goto foundit;
	}
	lib_abort();
foundit:
	f->isused[at] = TRUE;
	f->at = at+1;
	f->nused++;
	return (char *) f->mem + at * sz;
}
void fmalloc_free(Fmalloc *m, void *p)
{
	// Find the fmalloc that contains 'p'
	// clear the isused flag
	// nused--;
	// crash if not used or not in memory area.
	Fchunk *f;
	char *cp = (char *) p;
	for (f=m->head;f!=NULL;f=f->next) {
		if (cp>=f->mem && cp<f->mem+(f->size*f->max)) {
			int at;
			at = (cp-f->mem)/f->size;
			f->nused--;
			if (!f->isused[at]) {
				printf("Not used %d/%d\n",at,f->max);
				lib_abort(); // crashed here...
			}
			f->isused[at] = FALSE;
			if (f->nused<0) lib_abort();
			return;
		}
	}
	lib_abort();
}
void fmalloc_freeall(Fmalloc *m)
{
	// release all the data
	// crash if any not released. 
	Fchunk *f,*next;

	if (m==NULL) return;

	for (f=m->head;f!=NULL;f=next) {
		next = f->next;
		if (f->nused!=0) lib_abort();
		free(f->mem);
		free(f->isused);
		free(f);
	}
	free(m);
}
void fmalloc_test(void)
{
	char *mem[5000];
	int i;
	Fmalloc *m = fmalloc_new();
	int k;
	time_t start = time(NULL);
	for (k=0; k<10000; k++) {
		for (i=0; i<5000; i++) {
			mem[i] = fmalloc(m,25);
			strcpy(mem[i],"testing");
		}
		for (i=0; i<5000; i++) {
			fmalloc_free(m,mem[i]);
		}
	}
	fmalloc_freeall(m); m = NULL;
	printf("Elapsed %d\n",(int) (time(NULL)-start));

	start = time(NULL);
	for (k=0; k<10000; k++) {
		for (i=0; i<5000; i++) {
			mem[i] = malloc(25);
			strcpy(mem[i],"testing");
		}
		for (i=0; i<5000; i++) {
			free(mem[i]);
		}
	}
	printf("Elapsed %d\n",(int) (time(NULL)-start));
}
