/* ,file-id archive://[lord]/390/vu/./hashtab.c/1998-05-18
*/
/*	Copyright (C) 1997 Tom Lord
 * 
 * This program is provided to you under the terms of the Liberty Software
 * License.  You are NOT permitted to redistribute, modify, or use it
 * except in very specific ways described by that license.
 *
 * This software comes with NO WARRANTY.
 * 
 * You should have received a copy of the Liberty Software License
 * along with this software; see the file =LICENSE.  If not, write to
 * the Tom Lord, 1810 Francisco St. #2, Berkeley CA, 94703, USA.  
 */





#include "bitset.h"
#include "hashtab.h"



static int
default_hash_alloc (int * errn, struct hashtab ** retv, struct hashtab_rules * rules)
{
  *retv = (struct hashtab *)xmalloc (sizeof (struct hashtab));
  return (*retv ? 0 : -1);
}

static int
default_hash_item_alloc (int * errn,
			 struct hashtab_item ** retv,
			 struct hashtab_rules * rules,
			 void * value)
{
  struct hashtab_item * it;
  it = (struct hashtab_item *)xmalloc (sizeof (*it));
  it->data = value;
  it->binding = 0;
  *retv = it;
  return it ? 0 : -1;
}

static void
default_free_hash (struct hashtab * tab, struct hashtab_rules * rules)
{
  xfree ((char *)tab);
}

static void
default_free_hash_item (struct hashtab_item * item, struct hashtab_rules * rules)
{
  xfree ((char *)item);
}

static int 
default_eq (void * va, void * vb)
{
  return va == vb;
}



#define EQ(rules) ((rules && rules->eq) ? rules->eq : default_eq)
#define HASH_ALLOC(rules) ((rules && rules->hash_alloc) ? rules->hash_alloc : default_hash_alloc)
#define FREE_HASH(rules) ((rules && rules->free_hash) ? rules->free_hash : default_free_hash)
#define ITEM_ALLOC(rules) ((rules && rules->hash_item_alloc) \
			   ? rules->hash_item_alloc : default_hash_item_alloc)
#define FREE_HASH_ITEM(rules) ((rules && rules->free_hash_item) \
			       ? rules->free_hash_item : default_free_hash_item)




int
hashtab_new (int * errn, struct hashtab ** retv, struct hashtab_rules * rules)
{
  struct hashtab * it;

  if (0 > HASH_ALLOC(rules) (errn, &it, rules))
    return -1;
  memset (0, it, sizeof (*it));
  *retv = it;
  return 0;
}

void
hashtab_free (struct hashtab * it,
	      hashtab_freefn freefn,
	      struct hashtab_rules * rules)
{
  hashtab_free_static (it, freefn, rules);
  FREE_HASH(rules) (it, rules);
}

void
hashtab_free_static (struct hashtab * tab,
		     hashtab_freefn freefn,
		     struct hashtab_rules * rules)
{
  int x;

  for (x = 0; x < 16; ++x)
    if (bitset_member (&tab->nested_p, x))
      {
	hashtab_free_static ((struct hashtab *)tab->children[x], freefn, rules);
	FREE_HASH (rules) ((struct hashtab *)tab->children[x], rules);
      }
    else
      {
	struct hashtab_item * them = (struct hashtab_item *)tab->children[x];
	while (them)
	  {
	    struct hashtab_item * that;
	    that = them;
	    them = that->next_same_hash;
	    freefn (that);
	    FREE_HASH_ITEM (rules) (that, rules);
	  }
      }
}



static unsigned long hashtab_masks[4] =
{
  0x12488421,
  0x96699669,
  0xbe7dd7eb,
  0xffffffff
};


static int shuffled_bytes[] =
{
  245, 184, 171, 36, 93, 194, 192, 143, 207, 89, 63, 175, 203, 231, 47, 238,
  103, 67, 176, 102, 80, 133, 24, 155, 91, 141, 234, 58, 44, 191, 218, 157,
  13, 168, 160, 113, 211, 213, 252, 236, 2, 19, 21, 148, 111, 251, 165, 74,
  124, 25, 181, 210, 250, 195, 235, 97, 185, 1, 179, 198, 105, 101, 5, 220,
  35, 162, 142, 41, 200, 209, 224, 71, 201, 134, 69, 48, 65, 170, 72, 167,
  145, 205, 28, 88, 215, 81, 214, 78, 118, 26, 123, 84, 140, 49, 45, 8,
  7, 107, 227, 60, 59, 32, 30, 82, 31, 189, 131, 17, 66, 239, 64, 10,
  149, 40, 130, 146, 54, 147, 9, 114, 4, 254, 241, 116, 110, 249, 57, 233,
  37, 55, 206, 100, 177, 119, 139, 158, 108, 75, 94, 23, 186, 152, 244, 27,
  38, 33, 188, 87, 76, 166, 228, 52, 120, 99, 247, 174, 51, 183, 3, 161,
  246, 135, 14, 178, 11, 216, 77, 172, 122, 154, 39, 253, 104, 34, 164, 230,
  219, 242, 68, 151, 180, 115, 173, 73, 212, 90, 125, 29, 22, 221, 56, 121,
  255, 204, 83, 169, 182, 112, 96, 187, 20, 106, 79, 15, 61, 223, 70, 85,
  53, 197, 217, 232, 196, 95, 136, 150, 243, 109, 129, 202, 208, 237, 144, 156,
  86, 127, 62, 248, 138, 229, 153, 226, 240, 199, 50, 12, 193, 98, 137, 126,
  0, 159, 222, 18, 163, 117, 190, 46, 225, 132, 16, 43, 128, 42, 92, 6
};

/* hash to bucket */

#define H2B(X) \
  (0xf & (  shuffled_bytes[X & 0xff] \
	  ^ shuffled_bytes[((X) >> 4) & 0xff] \
  	  ^ shuffled_bytes[((X) >> 8) & 0xff] \
	  ^ shuffled_bytes[((X) >> 12) & 0xff] \
	  ^ shuffled_bytes[((X) >> 16) & 0xff] \
	  ^ shuffled_bytes[((X) >> 20) & 0xff] \
	  ^ shuffled_bytes[((X) >> 24) & 0xff] \
	  ^ shuffled_bytes[((X) >> 28) & 0xff]))


struct hashtab_item * 
hashtab_find (struct hashtab * table,
	      unsigned long hash,
	      void * key,
	      struct hashtab_rules * rules)
{
  hashtab_eq eq;
  int maskc;
  unsigned long mask;
  int bucket;

  eq = EQ (rules);
  maskc = 0;
  mask = hashtab_masks [0];
  bucket = H2B(hash & mask);

  while (bitset_member (&table->nested_p, bucket))
    {
      table = (struct hashtab *)(table->children [bucket]);
      ++maskc;
      mask = hashtab_masks[maskc];
      bucket = H2B (hash & mask);
    }

  {
    struct hashtab_item * it;
    it = (struct hashtab_item *)(table->children[bucket]);
    while (it)
      if (eq (it->data, key))
	return it;
      else
	it = it->next_same_hash;
  }

  return 0;
}

static int
overflows (struct hashtab_item * bucket)
{
  return !(   bucket
	   && bucket->next_same_hash
	   && bucket->next_same_hash->next_same_hash
	   && bucket->next_same_hash->next_same_hash->next_same_hash);
}

struct hashtab_item *
hashtab_store (struct hashtab * table,
	       unsigned long hash,
	       void * key,
	       struct hashtab_rules * rules)
{
  hashtab_eq eq;
  int maskc;
  long mask;
  int bucket;
  int depth;
  
  eq = EQ (rules);
  maskc = 0;
  mask = hashtab_masks [0];
  bucket = H2B(hash & mask);
  depth = 0;
  
  while (bitset_member (&table->nested_p, bucket))
    {
      table = (struct hashtab *)(table->children [bucket]);
      ++maskc;
      mask = hashtab_masks[maskc];
      bucket = H2B(hash & mask);
      ++depth;
    }
  
  {
    struct hashtab_item * it;
    it = (struct hashtab_item *)(table->children[bucket]);
    while (it)
      if (eq (it->data, key))
	return it;
      else
	it = it->next_same_hash;
  }
  
  {
    if (   (depth < 3)
	&& (overflows ((struct hashtab_item *)table->children [bucket])))
      {
	int errn;
	struct hashtab * newtab;

	if (0 > HASH_ALLOC(rules) (&errn, &newtab, rules))
	  return 0;
	memset0 ((char *)newtab, sizeof (*newtab));
	newtab->parent = table;
	{
	  struct hashtab_item * them;
	  unsigned long newmask;
	  them = (struct hashtab_item *)table->children[bucket];
	  newmask = hashtab_masks[maskc + 1];
	  while (them)
	    {
	      struct hashtab_item * save = them->next_same_hash;
	      int new_buck = H2B(them->hash & newmask);
	      them->next_same_hash = ((struct hashtab_item *)
				      newtab->children[new_buck]);
	      ((struct hashtab_item **)newtab->children)[new_buck] = them;
	      them->table = newtab;
	      them = save;
	      ++newtab->refs;
	      --table->refs;
	    }
	  ((struct hashtab **)table->children)[bucket] = newtab;
	  bitset_adjoin (&table->nested_p, bucket);
	  ++table->refs;
	  table = newtab;
	  bucket = H2B(hash & newmask);
	}
      }
  }

 add_to_bucket:
  {
    int errn;
    struct hashtab_item  * it;

    if (0 > ITEM_ALLOC(rules) (&errn, &it, rules, key))
      return 0;
    it->hash = hash;
    it->table = table;
    /* DATA and BINDING are to be set in hash_item_alloc */
    it->next_same_hash = (struct hashtab_item *)table->children [bucket];
    ((struct hashtab_item **)table->children)[bucket] = it;
    ++table->refs;
    return it;
  }
}

void
hashtab_delete (struct hashtab_item * it, struct hashtab_rules * rules)
{
  if (it)
    {
      struct hashtab * table;
      unsigned long hash;
      int depth;
      int bucket;
      struct hashtab_item ** pos;
      
      table = it->table;
      hash = it->hash;
      depth = (table->parent
	       ? (table->parent->parent
		  ? (table->parent->parent->parent
		     ? 3
		     : 2)
		  : 1)
	       : 0);
      bucket = H2B (hash & hashtab_masks [depth]);
      pos = (struct hashtab_item **)&table->children [bucket];
      
      while (*pos != it)
	pos = &(*pos)->next_same_hash;

      *pos = it->next_same_hash;

      FREE_HASH_ITEM(rules) (it, rules);

      --table->refs;
      while (!table->refs && depth)
	{
	  struct hashtab * empty_table;
	  empty_table = table;
	  table = table->parent;
	  --depth;
	  bucket = H2B(hash & hashtab_masks [depth]);
	  --table->refs;
	  table->children[bucket] = 0;
	  bitset_remove (&table->nested_p, bucket);
	  FREE_HASH (rules) (empty_table, rules);
	}
    }
}

