/*
    (C) 1995 AROS - The Amiga Replacement OS
    $Id: allocate.c 1.1 1995/11/14 22:31:07 digulla Exp digulla $
    $Log: allocate.c $
 * Revision 1.1  1995/11/14  22:31:07  digulla
 * Initial revision
 *
    Desc:
    Lang: english
*/
#include "exec_intern.h"
#include "memory.h"
#include <exec/alerts.h>

/*****************************************************************************

    NAME */
	#include <exec/memory.h>
	#include <clib/exec_protos.h>

	__AROS_LH2(APTR, Allocate,

/*  SYNOPSIS */
	__AROS_LA(struct MemHeader *, freeList, A0),
	__AROS_LA(unsigned long     , byteSize, D0),

/*  LOCATION */
	struct ExecBase *, SysBase, 31, Exec)

/*  FUNCTION
	Allocate memory out of a private region handled by the MemHeader
	structure.

    INPUTS
	freeList - Pointer to the MemHeader structure which holds the memory
	byteSize - Number of bytes you want to get

    RESULT
	A pointer to the number of bytes you wanted or NULL if the memory
	couldn't be allocated

    NOTES
	The memory is aligned to sizeof(struct MemChunk). All requests
	are rounded up to a multiple of that size.

    EXAMPLE
	#define POOLSIZE 4096
	\* Get a MemHeader structure and some private memory *\
	mh=(struct MemHeader *)
	    AllocMem(sizeof(struct MemHeader)+POOLSIZE,MEMF_ANY);
	if(mh!=NULL)
	{
	    \* Build a private pool *\
	    mh->mh_First=(struct MemChunk *)(mh+1);
	    mh->mh_First->mc_Next=NULL;
	    mh->mh_First->mc_Bytes=POOLSIZE;
	    mh->mh_Free=POOLSIZE;
	    {
		\* Use the pool *\
		UBYTE *mem1,*mem2;
		mem1=Allocate(mh,1000);
		mem2=Allocate(mh,2000);
		\* Do something with memory... *\
	    }
	    \* Free everything at once *\
	    FreeMem(mh,sizeof(struct MemHeader)+POOLSIZE);
	}

    BUGS

    SEE ALSO
	Deallocate()

    INTERNALS

    HISTORY
	17-09-95    created by m. fleischer
	16-10-95    increased portability
	26-10-95    digulla adjusted to new calling scheme

******************************************************************************/
{
    __AROS_FUNC_INIT
    struct MemChunk *p1, *p2;

    /* Zero bytes requested? May return everything ;-). */
    if(!byteSize)
	return NULL;

    /* First round byteSize to a multiple of MEMCHUNK_TOTAL. */
    byteSize = (byteSize + MEMCHUNK_TOTAL-1) & -MEMCHUNK_TOTAL;

    /* Is there enough free memory in the list? */
    if(freeList->mh_Free<byteSize)
	return NULL;

    /*
	The free memory list is only single linked, i.e. to remove
	elements from the list I need the node as well as it's
	predessor. For the first element I can use freeList->mh_First
	instead of a real predessor.
    */
    p1=(struct MemChunk *)&freeList->mh_First;
    p2=p1->mc_Next;

    /* Is the list enpty? */
    if(p2==NULL)
	return NULL;

    /* Follow the list */
    for(;;)
    {
#if !defined(NO_CONSISTENCY_CHECKS)
	/* Consistency check: Check alignment restrictions */
	if( ((ULONG)p2|(ULONG)p2->mc_Bytes) & (MEMCHUNK_TOTAL-1) )
	{
	    Alert(AN_MemCorrupt);
	    return NULL;
	}
#endif
	/* Check if current block is large enough */
	if(p2->mc_Bytes>=byteSize)
	{
	    /* It is. Remove it from the list and return it. */
	    if(p2->mc_Bytes==byteSize)
		/* Fits exactly. Just relink the list. */
		p1->mc_Next=p2->mc_Next;
	    else
	    {
		/* Split the current chunk and return the first bytes. */
		p1->mc_Next=(struct MemChunk *)((UBYTE *)p2+byteSize);
		p1=p1->mc_Next;
		p1->mc_Next=p2->mc_Next;
		p1->mc_Bytes=p2->mc_Bytes-byteSize;
	    }
	    /* Adjust free memory count and return */
	    freeList->mh_Free-=byteSize;
	    return p2;
	}

	/* Go to next block */
	p1=p2;
	p2=p1->mc_Next;

	/* Check if this was the end */
	if(p2==NULL)
	    return NULL;
#if !defined(NO_CONSISTENCY_CHECKS)
	/*
	    Consistency check:
	    If the end of the last block+1 is bigger or equal to
	    the start of the current block something must be wrong.
	*/
	if((UBYTE *)p2<=(UBYTE *)p1+p1->mc_Bytes)
	{
	    Alert(AN_MemCorrupt);
	    return NULL;
	}
#endif
    }

    __AROS_FUNC_EXIT
} /* Allocate */
