/* associative.c: 
 *
 ****************************************************************
 * Copyright (C) 2003 Tom Lord
 * 
 * See the file "COPYING" for further information about
 * the copyright and warranty status of this work.
 */


#include "hackerlab/hash/hashtree.h"
#include "hackerlab/hash/hash-utils.h"
#include "hackerlab/char/str.h"
#include "file-utils/libawk/associative.h"


/* __STDC__ prototypes for static functions */
static t_ulong hash_key (t_uchar * key);
static int str_key_eq (void * va, void * vb, struct hashtree_rules * r);
static void assoc_table_hashfree_fn (struct hashtree_item * it, struct hashtree_rules * rules);



static struct hashtree_rules assoc_rules = { str_key_eq, 0, 0, 0, 0, 0 };



void
assoc_set (assoc_table * vtable, t_uchar * key, t_uchar * value)
{
  struct hashtree * table;
  struct hashtree_item * item;

  table = *(struct hashtree **)vtable;

  if (!table)
    {
      table = hashtree_alloc (&assoc_rules);
      *vtable = (assoc_table)table;
    }
  
  item = hashtree_store (table, hash_key (key), (void *)key, &assoc_rules);
  if (item->key == key)
    {
      item->key = str_save (0, key);
    }

  if (item->binding)
    {
      lim_free (0, item->binding);
    }

  item->binding = str_save (0, value);
}

t_uchar * 
assoc_ref (assoc_table vtable, t_uchar * key)
{
  struct hashtree * table;
  struct hashtree_item * item;

  table = (struct hashtree *)vtable;

  if (!table)
    return 0;

  item = hashtree_find (table, hash_key (key), (void *)key, &assoc_rules);

  if (!item)
    return 0;

  return (t_uchar *)item->binding;
}

void
assoc_del (assoc_table vtable, t_uchar * key)
{
  struct hashtree * table;
  struct hashtree_item * item;

  table = (struct hashtree *)vtable;

  if (!table)
    return;

  item = hashtree_find (table, hash_key (key), (void *)key, &assoc_rules);

  if (!item)
    return;

  if (item->key)
    lim_free (0, item->key);

  if (item->binding)
    lim_free (0, item->binding);

  hashtree_delete (item, &assoc_rules);
}


void
free_assoc_table (assoc_table table)
{
  if (table)
    hashtree_free ((struct hashtree *)table, assoc_table_hashfree_fn, &assoc_rules);
}




static t_ulong
hash_key (t_uchar * key)
{
 /* From GNU Emacs via Stephen Turnbull.   Fair use?
  * (GPL, anyway....)
  */
  t_uchar c;
  t_ulong hash;

  hash = 0;

  if (!key)
    return 0xde7a115L;

  while (*key)
    {
      c = *key;
      ++key;

      if (c >= 0140)
	c -= 40;                /* I dunno why either -tl */

      hash = ((hash << 3) + (hash >> 28) + c);
    }

  return hash;
}


static int
str_key_eq (void * va, void * vb, struct hashtree_rules * r)
{
  t_uchar * a;
  t_uchar * b;

  a = (t_uchar *)va;
  b = (t_uchar *)vb;

  return !str_cmp (a, b);
}

static void
assoc_table_hashfree_fn (struct hashtree_item * it, struct hashtree_rules * rules)
{
  if (it->key)
    lim_free (0, it->key);
  if (it->binding)
    lim_free (0, it->binding);
}


/* tag: Tom Lord Mon May  5 17:29:01 2003 (associative.c)
 */
