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

#include <stdio.h>
#include <stdlib.h>

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

struct pool {
  resource r;
  list inside;
  char *name;
};

static void pool_dump(resource *);
static void pool_free(resource *);
static resource *pool_lookup(resource *, unsigned long);

static struct resclass pool_class = {
  "Pool",
  sizeof(pool),
  pool_free,
  pool_dump,
  pool_lookup
};

pool root_pool;

static int indent;

pool *
rp_new(pool *p, char *name)
{
  pool *z = ralloc(p, &pool_class);
  z->name = name;
  init_list(&z->inside);
  return z;
}

static void
pool_free(resource *P)
{
  pool *p = (pool *) P;
  resource *r, *rr;

  r = HEAD(p->inside);
  while (rr = (resource *) r->n.next)
    {
      r->class->free(r);
      xfree(r);
      r = rr;
    }
}

static void
pool_dump(resource *P)
{
  pool *p = (pool *) P;
  resource *r;

  debug("%s\n", p->name);
  indent += 3;
  WALK_LIST(r, p->inside)
    rdump(r);
  indent -= 3;
}

static resource *
pool_lookup(resource *P, unsigned long a)
{
  pool *p = (pool *) P;
  resource *r, *q;

  WALK_LIST(r, p->inside)
    if (r->class->lookup && (q = r->class->lookup(r, a)))
      return q;
  return NULL;
}

void
rfree(void *res)
{
  resource *r = res;

  if (r)
    {
      if (r->n.next)
	rem_node(&r->n);
      r->class->free(r);
      xfree(r);
    }
}

void
rdump(void *res)
{
  char x[16];
  resource *r = res;

  bsprintf(x, "%%%ds%%08x ", indent);
  debug(x, "", (int) r);
  if (r)
    {
      debug("%s ", r->class->name);
      r->class->dump(r);
    }
  else
    debug("NULL\n");
}

void *
ralloc(pool *p, struct resclass *c)
{
  resource *r = xmalloc(c->size);

  r->class = c;
  add_tail(&p->inside, &r->n);
  return r;
}

void
rlookup(unsigned long a)
{
  resource *r;

  debug("Looking up %08lx\n", a);
  if (r = pool_lookup(&root_pool.r, a))
    rdump(r);
  else
    debug("Not found.\n");
}

void
resource_init(void)
{
  root_pool.r.class = &pool_class;
  root_pool.name = "Root";
  init_list(&root_pool.inside);
}

/*
 *	Memory blocks.
 */

struct mblock {
  resource r;
  unsigned size;
  byte data[0];
};

static void mbl_free(resource *r)
{
}

static void mbl_debug(resource *r)
{
  struct mblock *m = (struct mblock *) r;

  debug("(size=%d)\n", m->size);
}

static resource *
mbl_lookup(resource *r, unsigned long a)
{
  struct mblock *m = (struct mblock *) r;

  if ((unsigned long) m->data <= a && (unsigned long) m->data + m->size > a)
    return r;
  return NULL;
}

static struct resclass mb_class = {
  "Memory",
  0,
  mbl_free,
  mbl_debug,
  mbl_lookup
};

void *
mb_alloc(pool *p, unsigned size)
{
  struct mblock *b = xmalloc(sizeof(struct mblock) + size);

  b->r.class = &mb_class;
  add_tail(&p->inside, &b->r.n);
  b->size = size;
  return b->data;
}

void *
mb_allocz(pool *p, unsigned size)
{
  void *x = mb_alloc(p, size);
  bzero(x, size);
  return x;
}

void
mb_free(void *m)
{
  struct mblock *b = SKIP_BACK(struct mblock, data, m);
  rfree(b);
}