/* 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 "tla/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 };


/************************************************************************
 *(h1 "Associative Tables")
 * 
 * Tla makes heavy use of a simple data structure for storing an
 * associative array of keys and value which are string: the
 * `assoc_table' type.
 * 
 * In general, these functions will cause the process to exit with 
 * non-0 status and a error message to the standard error descriptor
 * if an allocation failure occurs.
 */
/*(menu)
 */


/************************************************************************
 *(h1 "Associative Table Types")
 * 
 * Associative tables should be declared to be of type `assoc_table' and
 * initialized to 0, as in:
 *  
 *    assoc_table assoc = 0;
 */

/*(c assoc_table :category type)
 * typedef <unspecified> assoc_table;
 * 
 * An associative table.   Although the definition of the typedef
 * is unspecified, `assoc_table' values should be initialized to 0.
 */


/************************************************************************
 *(h1 "Associative Table Functions")
 * 
 */


/*(c assoc_set)
 * void assoc_set (assoc_table * table,
 *                 t_uchar * key,
 *                 t_uchar * value);
 * 
 * Record a binding of `key' to `value' in `*table'.
 * 
 * The value of `*table' might change as a result.
 * 
 * New copies of `key' and `value' are allocated for the
 * table.
 */
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);
}


/*(c assoc_ref)
 * t_uchar * assoc_ref (assoc_table table,
 *                      t_uchar * key);
 * 
 * Return the value (if any, 0 otherwise) associated
 * with `key' in `table'.
 * 
 * The value retured is _not_ newly allocated.  Callers 
 * should neither free the value, nor presume that it 
 * will remain value after later `assoc_set' or `assoc_del'
 * operations on this table.
 */
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;
}


/*(c assoc_del)
 * void assoc_del (assoc_table table,
 *                 t_uchar * key);
 * 
 * Remove the binding of `key' (if any) in `table'.
 */
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);
}


/*(c free_assoc_table)
 * void free_assoc_table (assoc_table table);
 * 
 * Free all storage associated with `table'.
 */
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)
 */
