/*
 *	BIRD Resource Manager -- Memory Pools
 *
 *	(c) 1998 Martin Mares <mj@ucw.cz>
 *
 *	Can be freely distributed and used under the terms of the GNU GPL.
 */

#include <stdlib.h>
#include <string.h>

#include "nest/bird.h"
#include "lib/resource.h"

struct mp_chunk {
  struct mp_chunk *next;
  byte data[0];
};

struct mempool {
  resource r;
  byte *ptr, *end;
  struct mp_chunk *first, **plast;
  unsigned chunk_size, threshold, total;
};

void mp_free(resource *);
void mp_dump(resource *);

static struct resclass mp_class = {
  "MemPool",
  sizeof(struct mempool),
  mp_free,
  mp_dump
};

mempool
*mp_new(pool *p, unsigned blk)
{
  mempool *m = ralloc(p, &mp_class);
  m->ptr = m->end = NULL;
  m->first = NULL;
  m->plast = &m->first;
  m->chunk_size = blk;
  m->threshold = 3*blk/4;
  m->total = 0;
  return m;
}

void *
mp_alloc(mempool *m, unsigned size)
{
  byte *a = (byte *) ALIGN((unsigned long) m->ptr, CPU_STRUCT_ALIGN);
  byte *e = a + size;

  if (e <= m->end)
    {
      m->ptr = e;
      return a;
    }
  else
    {
      struct mp_chunk *c;
      if (size >= m->threshold)
	{
	  c = xmalloc(sizeof(struct mp_chunk) + size);
	  m->total += size;
	}
      else
	{
	  c = xmalloc(sizeof(struct mp_chunk) + m->chunk_size);
	  m->ptr = c->data + size;
	  m->end = c->data + m->chunk_size;
	  m->total += m->chunk_size;
	}
      *m->plast = c;
      m->plast = &c->next;
      c->next = NULL;
      return c->data;
    }
}

void *
mp_allocu(mempool *m, unsigned size)
{
  byte *a = m->ptr;
  byte *e = a + size;

  if (e <= m->end)
    {
      m->ptr = e;
      return a;
    }
  return mp_alloc(m, size);
}

void *
mp_allocz(mempool *m, unsigned size)
{
  void *z = mp_alloc(m, size);

  bzero(z, size);
  return z;
}

void
mp_free(resource *r)
{
  mempool *m = (mempool *) r;
  struct mp_chunk *c, *d;

  for(d=m->first; d; d = c)
    {
      c = d->next;
      xfree(d);
    }
}

void
mp_dump(resource *r)
{
  mempool *m = (mempool *) r;
  struct mp_chunk *c;
  int cnt;

  for(cnt=0, c=m->first; c; c=c->next, cnt++)
    ;
  debug("(chunk=%d threshold=%d count=%d total=%d)\n",
	m->chunk_size,
	m->threshold,
	cnt,
	m->total);
}