#include <exec/types.h>
#include <exec/nodes.h>
#include <exec/lists.h>
#include <exec/memory.h>
#include <proto/exec.h>

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "mem.h"

#define FALSE	0
#define TRUE	1


void *	__regargs	myalloc (ULONG size);
void			myfreeall (void);

static	void *	ralloc (struct List *lp, ULONG size);
static	void	rfreeall (struct List *lp);

/*-------------------------------------------------------------------------
 * GLOBALS
 *-------------------------------------------------------------------------
 */

	/*
	 * Structure used by ralloc() and rfree()
	 */

struct	RHeader {
	struct MinNode	rNode;
	LONG		rSize;
};

	/*
	 * We want to reduce memory fragmentation. We get memory from the
	 * system in chunks of ALLOCMAX. Any request smaller than ALLOCMIN
	 * will be carved out of the most recent chunk, if possible;
	 * otherwise we will allocate a new chunk.  Requests larger than
	 * ALLOCMIN will be allocated directly from the system.
	 */

#define	ALLOCMAX 0xFF00		/* largest size we can handle */
#define	ALLOCMIN 1024		/* minimum size to allocate separately */

	/*
	 * "mychain" is a list of all blocks allocated by myalloc(),
	 * and tracked by ralloc()/rfreeall().
	 */

static	struct List	mychain =
{
	(struct Node *) &mychain.lh_Tail,
	(struct Node *) NULL,
	(struct Node *) &mychain.lh_Head,
	0, 0
};

/*-------------------------------------------------------------------------
 * We request relatively large chunks of memory, and hand out pieces as
 * necessary.  
 *-------------------------------------------------------------------------
 */

void *	__regargs	myalloc( ULONG size )
{
	void	*p;

static	UBYTE *pointer	= NULL;	/* most recent chunk */
static	ULONG offset	= 0;	/* next available offset */

	/*-------------------------------------------------------
	 * Round requested size up to a multiple of 4.
	 * This should be adequate for alignment of most objects.
	 *-------------------------------------------------------
	 */

	size = (size + 3) & 0xFFFFFFFCL;

	/*----------------------------------------------------------
	 * There is a limit to how much we can allocate at one time.
	 *----------------------------------------------------------
	 */

#if 0
	if (size > ALLOCMAX)
		return( NULL );
#endif

	/*-----------------------------------------------------------------
	 * For objects above a certain minimum size, we allocate a separate
	 * block.  This should help when we have a mixture of large and
	 * small objects.
	 *-----------------------------------------------------------------
	 */

	if (size > ALLOCMIN)
	{
		p = ralloc (&mychain, size);
		return( p );
	}

	/*------------------------------------------------------------------
	 * If there is no room for new object in previously allocated block,
	 * we allocate another block.
	 *------------------------------------------------------------------
	 */

	if ( !pointer || (size > ALLOCMAX-offset) )	/* prevent overflow */
	{
		pointer = ralloc (&mychain, ALLOCMAX);
	 	offset = 0;
	}

	/*-----------------------------------------------------------------
	 * If there is a block hanging around, it should have room for this
	 * object.  Carve off a piece.
	 *-----------------------------------------------------------------
	 */

	if (pointer)
	{
		p = (void *) (pointer + offset);
		offset += size;
		return( p );
	}
	else
		return( NULL );
}


/*-------------------------------------------------------------------------
 * myfreeall()
 * Free all blocks from list.
 *-------------------------------------------------------------------------
 */

void	myfreeall (void)
{
	rfreeall (&mychain);
}

/*-------------------------------------------------------------------------
 * ralloc()
 *-------------------------------------------------------------------------
 */

static	void *	ralloc (struct List *lp, ULONG size)
{
	register ULONG		asize = size + sizeof(struct RHeader);
	register ULONG		aflag = MEMF_CLEAR | MEMF_PUBLIC;
	register struct RHeader	*rp = NULL;

	if (size > 0)
		rp = (struct RHeader *) AllocMem (asize, aflag);

	if (rp == NULL)
		return NULL;

	rp->rSize = asize;
	AddHead (lp, (struct Node *) rp);
	return (void *) (++rp);
}

/*-------------------------------------------------------------------------
 * rfreeall()
 * Free all blocks from list.
 *-------------------------------------------------------------------------
 */

static	void	rfreeall (struct List *lp)
{
	struct RHeader *rp;
	while (rp = (struct RHeader *) RemHead(lp))
		FreeMem ((UBYTE *) rp, rp->rSize);
}

