#include "freecdbmake.h"

void cdbmake_init(cdbm)
struct cdbmake *cdbm;
{
  cdbm->head = 0;
  cdbm->split = 0;
  cdbm->hash = 0;
  cdbm->numentries = 0;
}

int cdbmake_add(cdbm,h,p,alloc)
struct cdbmake *cdbm;
uint32_t h;
uint32_t p;
char *(*alloc)();
{
  struct cdbmake_hplist *head;

  head = cdbm->head;
  if (!head || (head->num >= CDBMAKE_HPLIST)) {
    head = (struct cdbmake_hplist *) alloc(sizeof(struct cdbmake_hplist));
    if (!head) return 0;
    head->num = 0;
    head->next = cdbm->head;
    cdbm->head = head;
  }
  head->hp[head->num].h = h;
  head->hp[head->num].p = p;
  ++head->num;
  ++cdbm->numentries;
  return 1;
}

int cdbmake_split(cdbm,alloc)
struct cdbmake *cdbm;
char *(*alloc)();
{
  int i;
  uint32_t u;
  uint32_t memsize;
  struct cdbmake_hplist *x;

  for (i = 0;i < 256;++i)
    cdbm->count[i] = 0;

  for (x = cdbm->head;x;x = x->next) {
    i = x->num;
    while (i--)
      ++cdbm->count[255 & x->hp[i].h];
  }

  memsize = 1;
  for (i = 0;i < 256;++i) {
    u = cdbm->count[i] * 2;
    if (u > memsize)
      memsize = u;
  }

  memsize += cdbm->numentries; /* no overflow possible up to now */
  u = (uint32_t) 0 - (uint32_t) 1;
  u /= sizeof(struct cdbmake_hp);
  if (memsize > u) return 0;

  cdbm->split = (struct cdbmake_hp *) alloc(memsize * sizeof(struct cdbmake_hp));
  if (!cdbm->split) return 0;

  cdbm->hash = cdbm->split + cdbm->numentries;

  u = 0;
  for (i = 0;i < 256;++i) {
    u += cdbm->count[i]; /* bounded by numentries, so no overflow */
    cdbm->start[i] = u;
  }

  for (x = cdbm->head;x;x = x->next) {
    i = x->num;
    while (i--)
      cdbm->split[--cdbm->start[255 & x->hp[i].h]] = x->hp[i];
  }

  return 1;
}

uint32_t cdbmake_throw(cdbm,pos,b)
struct cdbmake *cdbm;
uint32_t pos;
int b;
{
  uint32_t len;
  uint32_t j;
  uint32_t count;
  struct cdbmake_hp *hp;
  uint32_t where;

  count = cdbm->count[b];

  len = count + count; /* no overflow possible */
  cdbmake_pack(cdbm->final + 8 * b,pos);
  cdbmake_pack(cdbm->final + 8 * b + 4,len);

  if (len) {
    for (j = 0;j < len;++j)
      cdbm->hash[j].h = cdbm->hash[j].p = 0;

    hp = cdbm->split + cdbm->start[b];
    for (j = 0;j < count;++j) {
      where = (hp->h >> 8) % len;
      while (cdbm->hash[where].p)
	if (++where == len)
	  where = 0;
      cdbm->hash[where] = *hp++;
    }
  }

  return len;
}
