#include "c.h"
struct block {
  struct block *next;
  char *limit;
  char *avail;
};
union align {
  long l;
  char *p;
  double d;
  int (*f)(void);
};
union header {
  struct block b;
  union align a;
};
#ifdef PURIFY
union header *arena[3];

void *allocate(unsigned long n, unsigned a) {
  union header *new = malloc(sizeof *new + n);

  assert(a < NELEMS(arena));
  if (new == NULL) {
    error("insufficient memory\n");
    exit(1);
  }
  new->b.next = (void *)arena[a];
  arena[a] = new;
  return new + 1;
}

void deallocate(unsigned a) {
  union header *p, *q;

  assert(a < NELEMS(arena));
  for (p = arena[a]; p; p = q) {
    q = (void *)p->b.next;
    free(p);
  }
  arena[a] = NULL;
}

void *newarray(unsigned long m, unsigned long n, unsigned a) {
  return allocate(m*n, a);
}
#else
static struct block
   first[] = {  { NULL },  { NULL },  { NULL } },
  *arena[] = { &first[0], &first[1], &first[2] };
static struct block *freeblocks;

void *allocate(unsigned long n, unsigned a) {
  struct block *ap;

  assert(a < NELEMS(arena));
  assert(n > 0);
  ap = arena[a];
  n = roundup(n, sizeof (union align));
  while (n > ap->limit - ap->avail) {
    if ((ap->next = freeblocks) != NULL) {
      freeblocks = freeblocks->next;
      ap = ap->next;
    } else
      {
        unsigned m = sizeof (union header) + n + 10*1024;
        ap->next = malloc(m);
        ap = ap->next;
        if (ap == NULL) {
          error("insufficient memory\n");
          exit(1);
        }
        ap->limit = (char *)ap + m;
      }
    ap->avail = (char *)((union header *)ap + 1);
    ap->next = NULL;
    arena[a] = ap;

  }
  ap->avail += n;
  return ap->avail - n;
}

void *newarray(unsigned long m, unsigned long n, unsigned a) {
  return allocate(m*n, a);
}
void deallocate(unsigned a) {
  assert(a < NELEMS(arena));
  arena[a]->next = freeblocks;
  freeblocks = first[a].next;
  first[a].next = NULL;
  arena[a] = &first[a];
}
#endif
