#include <malloc.h>
#include <stdio.h>
#include <sys/types.h>
#include <fcntl.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <stdlib.h>


extern "C"	{

void *malloc(size_t);
void free(void *);
void *realloc(void *, size_t);
int mallopt(int, int);
struct mallinfo mallinfo(void);
void *calloc(size_t, size_t);

	}

static int boomnum = 0;

static int write(int f, const char *s)
{
 return write(f, s, strlen(s));
}

static int write(int f, char c)
{
 return write(f, &c, 1);
}

static void write(int f, long i);

static void write(int f, int i)
{
 write(f, (long)i);
}

static void write(int f, long i)
{
 if(i < 0)	{
	i = -i;
	write(f, '-');
	}
 long rev = 10;
 for(;i>0;i/=10,rev*=10)
	rev += i%10;
 for(;rev>=10; rev/=10)
	write(f, (char)('0' + rev%10));
}


static void Assert(int line, char *file)
{
 write(2, "assertion failed, file: ");
 write(2, file);
 write(2, ", line: ");
 write(2, line);
 write(2, ' ');
 write(2, boomnum);
 write(2, "\n");
 abort();
}

#define assert(x) if(!(x)) Assert(__LINE__, __FILE__)

struct endspace	{
	int magic1;
	time_t time_alloc;
	time_t time_free;
	int sqn_alloc;
	size_t size;
	int sqn_free;
	int free;
	struct endspace *next;
	int junk;
	int magic2;
	static const int mgk;
	};

const int endspace::mgk = 31415926;

static int fd = -2;

static void * force_align(void *p)
{
 return (void *)(sizeof(double)*((long)p/sizeof(double) + 1));
}

static struct endspace *walkptr = 0;
extern end;


static int write_ok(int f, const struct endspace *e1)
{
 if(e1 < (void*)&end)	{
	write(f, 'Q');
	return 0;
	}
 int a1 = e1->magic1==endspace::mgk && e1->magic2==endspace::mgk;
 write(f, a1 ? 'F' : 'X');
 if(!a1)	{
	write(f, '(');
	write(f, e1->magic1);
	write(f, ',');
	write(f, e1->magic2);
	write(f, ')');
	}
 int a2 = e1->size>0;
 write(f, a2 ? ':' : 'S');
 write(f, e1->free ? 'f' : 'a');
 if(e1->size > 0)	{
	struct endspace e2;
 	memcpy(&e2, (char *)e1 + sizeof(*e1) + e1->size, sizeof(e2));
 	int a4 = memcmp(&e2, e1, sizeof(e2))==0;
 	write(f, a4 ? ':' : 'x');
	if(!a4)	{
		write(f, "[sz=");
		write(f, (int)(e1->size));
		write(f, ']');
		}
	int a5 = e2.magic1==endspace::mgk;
 	write(f, a5 ? ':' : 'm');
	if(!a5)	{
		write(f, '(');
		write(f, e2.magic1);
		write(f, ')');
		}
	int a6 = e2.magic2==endspace::mgk;
 	write(f, a6 ? ':' : 'M');
	if(!a6)	{
		write(f, '(');
		write(f, e2.magic2);
		write(f, ')');
		}
	return a1 && a2 && a4 && a5 && a6;
	}
 return 0;
}

void walk(const char *from)
{
 const struct endspace *p = walkptr;
 const struct endspace *last = 0;
 while(p)	{
	if(p<(void*)&end
			|| p->magic1!=endspace::mgk || p->magic2!=endspace::mgk
			|| p->size<0 || memcmp((char *)p + sizeof(*p) + p->size, p, sizeof(*p))!=0)	{
		write(2, "from=");
		write(2, from);
		write(2, " boomnum=");
		write(2, boomnum);
		write(2, " alloc_sqn=");
		write(2, p->sqn_alloc);
		write(2, " free_sqn=");
		write(2, p->sqn_free);
		write(2, '=');
		write_ok(2, p);
		if(last)	{
			write(2, "\nlast=");
			write_ok(2, last);
			write(2, " last_alloc_sqn=");
			write(2, last->sqn_alloc);
			write(2, " last_free_sqn=");
			write(2, last->sqn_free);
			}
		write(2, '\n');
		abort();
		}
	last = p;
	p = p->next;
	}
}

extern "C" void *malloc(size_t a)
{
 static int sqn = 0;
 assert(a > 0);
 assert(sizeof(struct endspace)%sizeof(double) == 0);
 if(fd == -2)
	fd = open("amoeba.his", O_WRONLY|O_CREAT|O_TRUNC, 0666);
 if(fd < 0)	{
	write(2, "ERROR OPENING HISTORY=");
	write(2, errno);
	write(2, ' ');
	write(2, strerror(errno));
	write(2, '\n');
	}
 void *tmp = force_align(sbrk(a+2*sizeof(struct endspace)+sizeof(double)));
 if(!tmp || tmp == (void*)(-1))
	return 0;
 struct endspace *e1 = (struct endspace *)tmp;
 struct endspace *e2 = (struct endspace *)
						((char *)tmp+sizeof(struct endspace)+a);
 e1->magic1 = endspace::mgk;
 e1->magic2 = endspace::mgk;
 e1->sqn_alloc = sqn++;
 e1->time_alloc = time(0);
 e1->free = 0;
 e1->sqn_free = -1;
 e1->time_free = -1;
 e1->size = a;
 e1->next = walkptr;
 walkptr = e1;
 write(fd, "A ");
 write(fd, e1->sqn_alloc);
 write(fd, ' ');
 write(fd, e1->time_alloc);
 write(fd, ' ');
 write(fd, (long)a);
 write(fd, ' ');
 write(fd, (long)(e1+1));
 write(fd, ' ');
 write(fd, boomnum);
 write(fd, '\n');
 memcpy(e2, e1, sizeof(struct endspace));
 walk("malloc");
 return (void *)((char *)tmp+sizeof(struct endspace));
}

extern "C" void free(void *p)
{
 static int sqn = 0;
 int ok;
 walk("free");
 assert(p);
 assert((int *)p>=&end);
 struct endspace *e1 = (struct endspace *)((char *)p - sizeof(struct endspace));
 struct endspace e2;
 if(ok = write_ok(fd, e1))
 	memcpy(&e2, (char *)p + e1->size, sizeof(e2));
 else	memset(&e2, 0, sizeof(struct endspace));

 e1->time_free = e2.time_free = time(0);
 write(fd, ' ');
 write(fd, e1->sqn_alloc);
 if(e2.sqn_alloc != e1->sqn_alloc)	{
	write(fd, ',');
 	write(fd, e2.sqn_alloc);
	}
 write(fd, ' ');
 write(fd, e1->time_alloc);
 if(e2.time_alloc != e1->time_alloc)	{
	write(fd, ',');
 	write(fd, e2.time_alloc);
	}
 write(fd, ' ');
 write(fd, (int)(e1->size));
 if(e2.size != e1->size)	{
	write(fd, ',');
 	write(fd, (int)(e2.size));
	}
 write(fd, ' ');
 write(fd, (long)p);
 write(fd, ' ');
 write(fd, sqn);
 write(fd, ' ');
 write(fd, e1->time_free);
 if(e2.time_free != e1->time_free)	{
	write(fd, ',');
 	write(fd, e2.time_free);
	}
 write(fd, ' ');
 write(fd, boomnum);
 write(fd, '\n');
 assert(!e1->free);
 e1->free = 0;
 e1->sqn_free = sqn++;
 assert(e1->magic1 == endspace::mgk);
 assert(e1->magic2 == endspace::mgk);
 assert(e1->size >= 0);
 memcpy((char *)p + e1->size, e1, sizeof(e2));
 if(!ok)
	abort();
}



extern "C" void *calloc(size_t a, size_t b)
	{int n = a*b; int i; char *tmp = (char *)malloc(a*b);
		for(i=0;i<n;i++) tmp[i] = 0;
		return tmp;
		}
extern "C" void *realloc(void *, size_t)
{
assert(0);
return 0;
}

extern "C" int mallopt(int, int)
{
assert(0);
return 0;
}

extern "C" struct mallinfo mallinfo(void)
{
struct mallinfo rv;
assert(0);
return rv;
}

void boom(int a)
{
 boomnum = a;
 walk("boom");
}
