/*-
 * Copyright (c) 2000-2001
 * Tatsuya Kudoh(CDR/TK),ROYALPANDA.    All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 */

#include<stdlib.h>
#include<stdio.h>
#include"malloc.h"

#pragma noregalo

/**********************************
*
*   low-level memory allocation
*
**********************************/

extern char *_heap;

static char *sbrk( int cnt )
{
	char *p;
	unsigned s;

	if( (unsigned)_heap < 0x8002 )
		_heap = (char*)0x8002;

	s = (-(unsigned)_heap) & 0xffff;	/* 0x10000 - _heap */
	if( s < cnt )
		return NULL;

	p = _heap;
	_heap += cnt;
	return p;
}

/*********************************
*
* High-level memory allocation
*
*********************************/


typedef struct cell_t {
	int size;
	struct cell_t *next;
}cell_t;

#define HEADERSIZE	(sizeof(int))
#define CELLSIZE	(sizeof(cell_t))


static cell_t FreeHead = {0,NULL};

static char *reuse( int size )
{
	cell_t *p,*prev;
	int newsize;

	prev = &FreeHead;
	p = prev->next;
	while( p != NULL && p->size < size ){
		prev = p;
		p = p->next;
	}
	if( p == NULL )
		return NULL;

	newsize = p->size - size;
	if( newsize < CELLSIZE ){
		prev->next = p->next;
	}else{
		p->size = newsize;
		p = (cell_t*)((char*)p + newsize);
		p->size = size;
	}
	return (char*)p + HEADERSIZE;
}


void *malloc( int size )
{
	char *p;
	cell_t *cp;

	size += HEADERSIZE;
	if( size < CELLSIZE )
		size = CELLSIZE;

	p = reuse(size);
	if( p != NULL )
		return (void*)p;

	cp = (cell_t*)sbrk(size);
	if( cp == NULL )
		return NULL;

	cp->size = size;
	return (char*)cp + HEADERSIZE;
}


void free( void *ptr )
{
	cell_t *p,*q,*prev;
	int flag;

	if( ptr == NULL )
		return;

	q = (cell_t*)((char*)ptr - HEADERSIZE);

	prev = &FreeHead;
	p = prev->next;
	flag = 0;
	while( p != NULL ){
		if( (char*)p + p->size == (char*)q ){
			prev->next = p->next;
			p->size += q->size;
			q = p;
			flag |= 1;
			if( flag == 3 )
				break;
			p = prev->next;
		}else if( (char*)q + q->size == (char*)p ){
			prev->next = p->next;
			q->size += p->size;
			flag |= 2;
			if( flag == 3 )
				break;
			p = prev->next;
		}else{
			prev = p;
			p = p->next;
		}
	}
	q->next = FreeHead.next;
	FreeHead.next = q;
}


void *realloc( void *ptr, int size )
{
	cell_t *cp;
	int oldsize,newsize;
	char *p,*d,*s;

	if( ptr == NULL )
		return malloc(size);

	if( size == 0 ){
		free(ptr);
		return NULL;
	}

	cp = (cell_t*)((char*)ptr - HEADERSIZE);
	oldsize = cp->size - HEADERSIZE;

	newsize = oldsize - size;
	if( newsize < 0 ){
		p = malloc(size);
		if( p == NULL )
			return NULL;
		d = p;
		s = (char*)ptr;
		while( oldsize-- )
			*d++ = *s++;
		return p;
	}

	if( newsize < CELLSIZE )
		return ptr;

	size += HEADERSIZE;
	newsize = cp->size - size;
	cp->size = size;
	cp = (cell_t*)((char*)cp + size);
	cp->size = newsize;
	free((char*)cp + HEADERSIZE);
	return ptr;
}


void memmove( void *dst, void *src, int size )
{
	char *d,*s;

	d = (char*)dst;
	s = (char*)src;

	if( d < s ){
		while( size-- )
			*d++ = *s++;
	}else if( d > s ){
		d += size;
		s += size;
		while( size-- )
			*--d = *--s;
	}
}
