/* alloc.c - memory allocator
 *
 * The point of this is that it is about 4 times faster than MANX malloc/free
 * in c.lib.  Malloc in heapmem.o will not allocate a second heap if the first
 * is exhausted.
 * :ts=8
 */


#define NULL		0L
#define HOWMUCH		4096L	/* how much memory to AllocMem() at a time */

typedef struct header {
      struct header *next;	/* next block */
      unsigned long size;	/* block size */
};

struct header first;

struct header *head = NULL;	/* start of list */


char *alloc (bytes)
unsigned bytes;
{
   struct header *getmem ();
   register struct header *p, *q;
   register long size;

   size = (((bytes + 2 * sizeof (struct header) - 1) >> 3) << 3);
   if ((q = head) == NULL) {
      first.next = head = q = &first;
      first.size = 0L;
   }
   for (p = q->next; ; q = p, p = p->next) {
      if (p->size >= size) {	/* if this block is large enough */
         if (p->size == size)
	    q ->next = p->next;
	 else {		/* remove memory from tail of block */
	    p->size -= size;
	    p = (struct header *) ((char *) p + p->size);
	       p->size = size;
	 }
	 head = q;
	 p->next = NULL;
	 return ((char *) (p+1));
      }
      if (p == head)	/* back where we started */
	 if ((p = getmem ()) == NULL)
	    return (NULL);	/* cannot allocate memory */
   }
}


/* freeit - put block back in free list */

freeit (ptr)
char *ptr;
{
   register struct header *p, *q;

   p = (struct header *) ptr - 1;
   for (q = head; ; q = q->next) {
      if (p > q && p < q->next)
	 break;		/* new block goes between q & q->next */
      if (q >= q->next && p > q)
	 break;		/* new block goes at end of list */
      if (q >= q->next && p < q->next)
	 break;		/* new block goes at beginning of list */
   }

   if ((struct header *) ((char *) p + p->size) == q->next) {
      p->size += q->next->size;
      p->next = q->next->next;
   } else {
      p->next = q->next;
   }
   if ((struct header *) ((char *) q + q->size) == p) {
      q->size += p->size;
      q->next = p->next;
   } else {
      q->next = p;
   }
   head = q;
}


/* getmem - request more memory from system */

struct header *getmem ()
{
   char *AllocMem ();
   register struct header *up;

   if ((up = (struct header *) AllocMem (HOWMUCH, 0L)) == NULL)
      return (NULL);	/* can't get more memory */
   up->next = NULL;
   up->size = HOWMUCH;
   freeit ((char *) (up + 1));
   return (head);
}


quit (code)
int code;
{
   register struct header *p, *q;
   unsigned long size;

   p = head;
   do {
      if (p == NULL)
         break;
      q = p->next;
      if ((size = p->size) > 0L) {
	 p->size = 0L;
         FreeMem (p, size);
      }
      p = q;
   } while (p != head);
   exit (code);
}
