#include <stdio.h>
#include <malloc.h>
#include <sys/types.h>
#include "misc.h"
#include "stack.h"
#include "Oalloc.h"

#define MAX_NUM_SETS 128    /* initial guess */
#define SET_SIZE (512*1024)  /* maximum of 64 megabytes */

#define ALIGN_2 1   /* boolean */
#define ALIGN_4 0   /* boolean: if true then ALIGN_2 must also be true */

static numLeft = 0;
static STACK *Set, *BigSet;

/* alloc and clear n bytes. Ofree frees ALL previously alloc'd sets.
** Use stacks to keep track of the set's we've alloc'd.
*/
void *Omalloc(unsigned n)
{
    static void *currentSet;
    if(n == 0)
	return malloc(0);       /* do whatever the system does with 0 */
    else if(n > SET_SIZE) /* too big for us, pass on to Malloc directly */
    {
	if(!BigSet)
	    BigSet = StackAlloc(MAX_NUM_SETS);
	return StackPush(BigSet, (foint)Calloc(1,n)).v;
    }
    else if(n > numLeft) /* this set is exhausted, get another one */
    {
	if(!Set)
	    Set = StackAlloc(MAX_NUM_SETS);
	currentSet = StackPush(Set, (foint)Calloc(1,SET_SIZE)).v;
	numLeft = SET_SIZE;
    }

    /* Cheap attempt at alignment */
#if ALIGN_2
    if(n > 1 && (n & 1)) n++;
#endif
#if ALIGN_4
    if(n > 3 && (n & 3)) n += 2;
#endif
    numLeft -= n;
    return (currentSet + SET_SIZE - numLeft - n);
}

void *Ocalloc(unsigned n, size_t s)
{
    return Omalloc(s*n);
}

/* Orealloc can't be implemented, because we don't know the size of the
** old storage, so we don't know how much to copy over, and just copying
** over the new space may result in memory faults while reading past the
** end of the old space.
*/

void Ofree(void)
{
    if(Set)
    {
	while(StackSize(Set))
	    free(StackPop(Set).v);
	StackFree(Set);
	Set = NULL;
    }
    if(BigSet)
    {
	while(StackSize(BigSet))
	    free(StackPop(BigSet).v);
	StackFree(BigSet);
	BigSet = NULL;
    }
    numLeft = 0;
}
