]> mj.ucw.cz Git - libucw.git/commitdiff
Added a generic universal multi-purpose magical hash table module.
authorMartin Mares <mj@ucw.cz>
Sat, 6 Apr 2002 17:57:02 +0000 (17:57 +0000)
committerMartin Mares <mj@ucw.cz>
Sat, 6 Apr 2002 17:57:02 +0000 (17:57 +0000)
Look at introductory comments in lib/hashtable.h to see all the features.

Generic programming in C is a real adventure, but an afternoon spent
with CPP quirks is a holiday when compared with C++ templates :-)

lib/Makefile
lib/hash-istring.c [new file with mode: 0644]
lib/hash-string.c [new file with mode: 0644]
lib/hash-test.c [new file with mode: 0644]
lib/hashfunc.h [new file with mode: 0644]
lib/hashtable.h [new file with mode: 0644]

index 2ccb3d0ae3e0518eb44a2d96dd4a178a61a1eaa3..2c93e1cfdf770c3f1c6011c9129fc9f97bb0be28 100644 (file)
@@ -1,4 +1,4 @@
-# Makefile for the Sherlock Library (c) 1997--2001 Martin Mares <mj@ucw.cz>
+# Makefile for the Sherlock Library (c) 1997--2002 Martin Mares <mj@ucw.cz>
 
 DIRS+=lib
 PROGS+=obj/lib/db-tool obj/lib/buckettool
@@ -7,7 +7,8 @@ SHLIB_OBJS=alloc.o alloc_str.o ctmatch.o db.o fastbuf.o fb-file.o fb-mem.o lists
        log.o log2.o md5.o md5hex.o mmap.o pagecache.o patimatch.o patmatch.o pool.o \
        prime.o random.o realloc.o regex.o timer.o url.o wildmatch.o \
        wordsplit.o str_ctype.o str_upper.o bucket.o conf.o object.o sorter.o \
-       finger.o proctitle.o ipaccess.o profile.o bitsig.o randomkey.o
+       finger.o proctitle.o ipaccess.o profile.o bitsig.o randomkey.o \
+       hash-string.o hash-istring.o
 
 obj/lib/libsh.a: $(addprefix obj/lib/,$(SHLIB_OBJS))
 
@@ -18,3 +19,4 @@ obj/lib/conf-test: obj/lib/conf-test.o obj/lib/libsh.a
 obj/lib/sort-test: obj/lib/sort-test.o obj/lib/libsh.a
 obj/lib/lfs-test: obj/lib/lfs-test.o obj/lib/libsh.a
 obj/lib/regex-test: obj/lib/regex-test.o obj/lib/libsh.a
+obj/lib/hash-test: obj/lib/hash-test.o obj/lib/libsh.a
diff --git a/lib/hash-istring.c b/lib/hash-istring.c
new file mode 100644 (file)
index 0000000..e95de44
--- /dev/null
@@ -0,0 +1,20 @@
+/*
+ *     Sherlock Library -- Case-Insensitive String Hash Function
+ *
+ *     (c) 2002 Martin Mares <mj@ucw.cz>
+ */
+
+#include "lib/lib.h"
+#include "lib/hashfunc.h"
+#include "lib/chartype.h"
+
+#include <string.h>
+
+uns
+hash_string_nocase(byte *k)
+{
+  uns h = strlen(k);
+  while (*k)
+    h = h*37 + Cupcase(*k++);
+  return h;
+}
diff --git a/lib/hash-string.c b/lib/hash-string.c
new file mode 100644 (file)
index 0000000..00623ec
--- /dev/null
@@ -0,0 +1,19 @@
+/*
+ *     Sherlock Library -- String Hash Function
+ *
+ *     (c) 2002 Martin Mares <mj@ucw.cz>
+ */
+
+#include "lib/lib.h"
+#include "lib/hashfunc.h"
+
+#include <string.h>
+
+uns
+hash_string(byte *k)
+{
+  uns h = strlen(k);
+  while (*k)
+    h = h*37 + *k++;
+  return h;
+}
diff --git a/lib/hash-test.c b/lib/hash-test.c
new file mode 100644 (file)
index 0000000..c03d9fa
--- /dev/null
@@ -0,0 +1,245 @@
+/* Tests for hash table routines */
+
+#include "lib/lib.h"
+
+#include <stdio.h>
+#include <string.h>
+
+#if 1
+
+/* TEST 1: integers */
+
+struct node {
+  int key;
+  int data;
+};
+
+#define HASH_NODE struct node
+#define HASH_PREFIX(x) test_##x
+#define HASH_KEY_ATOMIC key
+#define HASH_ATOMIC_TYPE int
+
+#define HASH_GIVE_INIT_DATA
+static inline void test_init_data(struct node *n)
+{
+  n->data = n->key + 123;
+}
+
+#define HASH_WANT_FIND
+//#define HASH_WANT_NEW
+#define HASH_WANT_LOOKUP
+//#define HASH_WANT_DELETE
+#define HASH_WANT_REMOVE
+
+#include "lib/hashtable.h"
+
+static void test(void)
+{
+  int i;
+
+  test_init();
+  for (i=0; i<1024; i++)
+    {
+      struct node *n = test_lookup(i);
+      ASSERT(n->data == i+123);
+    }
+  for (i=1; i<1024; i+=2)
+    {
+#if 0
+      test_delete(i);
+#else
+      struct node *n = test_lookup(i);
+      test_remove(n);
+#endif
+    }
+  for (i=0; i<1024; i++)
+    {
+      struct node *n = test_find(i);
+      if (!n != (i&1) || (n && n->data != i+123))
+       die("Inconsistency at i=%d", i);
+    }
+  i=0;
+  HASH_FOR_ALL(test, n)
+    {
+      i += 1 + 0*n->key;
+      // printf("%d\n", n->key);
+    }
+  HASH_END_FOR;
+  ASSERT(i == 512);
+  log(L_INFO, "OK");
+}
+
+#elif 0
+
+/* TEST 2: external strings */
+
+struct node {
+  char *key;
+  int data;
+};
+
+#define HASH_NODE struct node
+#define HASH_PREFIX(x) test_##x
+#define HASH_KEY_STRING key
+#define HASH_NOCASE
+
+#define HASH_WANT_FIND
+#define HASH_WANT_NEW
+
+#include "lib/hashtable.h"
+
+static void test(void)
+{
+  int i;
+
+  test_init();
+  for (i=0; i<1024; i+=2)
+    {
+      char x[32];
+      sprintf(x, "abc%d", i);
+      test_new(stralloc(x));
+    }
+  for (i=0; i<1024; i++)
+    {
+      char x[32];
+      struct node *n;
+      sprintf(x, "ABC%d", i);
+      n = test_find(x);
+      if (!n != (i&1))
+       die("Inconsistency at i=%d", i);
+    }
+  log(L_INFO, "OK");
+}
+
+#elif 0
+
+/* TEST 3: internal strings + pools */
+
+#include "lib/pools.h"
+
+static struct mempool *pool;
+
+struct node {
+  int data;
+  char key[1];
+};
+
+#define HASH_NODE struct node
+#define HASH_PREFIX(x) test_##x
+#define HASH_KEY_ENDSTRING key
+
+#define HASH_WANT_FIND
+#define HASH_WANT_NEW
+
+#define HASH_USE_POOL pool
+
+#include "lib/hashtable.h"
+
+static void test(void)
+{
+  int i;
+
+  pool = mp_new(16384);
+  test_init();
+  for (i=0; i<1024; i+=2)
+    {
+      char x[32];
+      sprintf(x, "abc%d", i);
+      test_new(x);
+    }
+  for (i=0; i<1024; i++)
+    {
+      char x[32];
+      struct node *n;
+      sprintf(x, "abc%d", i);
+      n = test_find(x);
+      if (!n != (i&1))
+       die("Inconsistency at i=%d", i);
+    }
+  log(L_INFO, "OK");
+}
+
+#elif 1
+
+/* TEST 4: complex keys */
+
+#include "lib/hashfunc.h"
+
+struct node {
+  int port;
+  int data;
+  char host[1];
+};
+
+#define HASH_NODE struct node
+#define HASH_PREFIX(x) test_##x
+#define HASH_KEY_COMPLEX(x) x host, x port
+#define HASH_KEY_DECL char *host, int port
+
+#define HASH_WANT_CLEANUP
+#define HASH_WANT_FIND
+#define HASH_WANT_NEW
+#define HASH_WANT_LOOKUP
+#define HASH_WANT_DELETE
+#define HASH_WANT_REMOVE
+
+#define HASH_GIVE_HASHFN
+static uns test_hash(char *host, int port)
+{
+  return hash_string_nocase(host) ^ hash_int(port);
+}
+
+#define HASH_GIVE_EQ
+static inline int test_eq(char *host1, int port1, char *host2, int port2)
+{
+  return !strcasecmp(host1,host2) && port1 == port2;
+}
+
+#define HASH_GIVE_EXTRA_SIZE
+static inline uns test_extra_size(char *host, int port UNUSED)
+{
+  return strlen(host);
+}
+
+#define HASH_GIVE_INIT_KEY
+static inline void test_init_key(struct node *n, char *host, int port)
+{
+  strcpy(n->host, host);
+  n->port = port;
+}
+
+#include "lib/hashtable.h"
+
+static void test(void)
+{
+  int i;
+
+  test_init();
+  for (i=0; i<1024; i+=2)
+    {
+      char x[32];
+      sprintf(x, "abc%d", i);
+      test_new(x, i%10);
+    }
+  for (i=0; i<1024; i++)
+    {
+      char x[32];
+      struct node *n;
+      sprintf(x, "ABC%d", i);
+      n = test_find(x, i%10);
+      if (!n != (i&1))
+       die("Inconsistency at i=%d", i);
+    }
+  log(L_INFO, "OK");
+  test_cleanup();
+  log(L_INFO, "Cleaned up");
+}
+
+#endif
+
+int
+main(void)
+{
+  test();
+  return 0;
+}
diff --git a/lib/hashfunc.h b/lib/hashfunc.h
new file mode 100644 (file)
index 0000000..126b8f2
--- /dev/null
@@ -0,0 +1,14 @@
+/*
+ *     Sherlock Library -- Hash Functions
+ *
+ *     (c) 2002 Martin Mares <mj@ucw.cz>
+ */
+
+#ifndef _SHERLOCK_HASHFUNC_H
+#define _SHERLOCK_HASHFUNC_H
+
+uns hash_string(byte *x);
+uns hash_string_nocase(byte *x);
+static inline uns hash_int(uns x) { return 6442450967*x; }
+
+#endif
diff --git a/lib/hashtable.h b/lib/hashtable.h
new file mode 100644 (file)
index 0000000..f96a07f
--- /dev/null
@@ -0,0 +1,528 @@
+/*
+ *     Sherlock Library -- Universal Hash Table
+ *
+ *     (c) 2002 Martin Mares <mj@ucw.cz>
+ */
+
+/*
+ *  This is not a normal header file, it's a generator of hash tables.
+ *  Each time you include it with parameters set in the corresponding
+ *  preprocessor macros, it generates a hash table with the parameters
+ *  given.
+ *
+ *  You need to specify:
+ *
+ *  HASH_NODE          data type where a node dwells (usually a struct).
+ *  HASH_PREFIX(x)     macro to add a name prefix (used on all global names
+ *                     defined by the hash table generator).
+ *
+ *  Then decide on type of keys:
+ *
+ *  HASH_KEY_ATOMIC=f  use node->f as a key of an atomic type (i.e.,
+ *                     a type which can be compared using `==')
+ *                     HASH_ATOMIC_TYPE (defaults to int).
+ *  | HASH_KEY_STRING=f        use node->f as a string key, allocated
+ *                     separately from the rest of the node.
+ *  | HASH_KEY_ENDSTRING=f use node->f as a string key, allocated
+ *                     automatically at the end of the node struct
+ *                     (to be declared as "char f[1]" at the end).
+ *  | HASH_KEY_COMPLEX use a multi-component key; as the name suggests,
+ *                     the passing of parameters is a bit complex then.
+ *                     The HASH_KEY_COMPLEX(x) macro should expand to
+ *                     `x k1, x k2, ... x kn' and you should also define:
+ *    HASH_KEY_DECL    declaration of function parameters in which key
+ *                     should be passed to all hash table operations.
+ *                     That is, `type1 k1, type2 k2, ... typen kn'.
+ *                     With complex keys, HASH_GIVE_HASHFN and HASH_GIVE_EQ
+ *                     are mandatory.
+ *
+ *  Then specify what operations you request (all names are automatically
+ *  prefixed by calling HASH_PREFIX):
+ *
+ *  <always defined>   init() -- initialize the hash table.
+ *  HASH_WANT_CLEANUP  cleanup() -- deallocate the hash table.
+ *  HASH_WANT_FIND     node *find(key) -- find node with the specified
+ *                     key, return NULL if no such node exists.
+ *  HASH_WANT_NEW      node *new(key) -- create new node with given key.
+ *                     Doesn't check whether it already exists.
+ *  HASH_WANT_LOOKUP   node *lookup(key) -- find node with given key,
+ *                     if it doesn't exist, create it. Defining
+ *                     HASH_GIVE_INIT_DATA is strongly recommended.
+ *  HASH_WANT_DELETE   int delete(key) -- delete and deallocate node
+ *                     with given key. Returns success.
+ *  HASH_WANT_REMOVE   remove(node *) -- delete and deallocate given node.
+ *
+ *  You can also supply several functions:
+ *
+ *  HASH_GIVE_HASHFN   unsigned int hash(key) -- calculate hash value of key.
+ *                     We have sensible default hash functions for strings
+ *                     and integers.
+ *  HASH_GIVE_EQ       int eq(key1, key2) -- return whether keys are equal.
+ *                     By default, we use == for atomic types and either
+ *                     strcmp or strcasecmp for strings.
+ *  HASH_GIVE_EXTRA_SIZE int extra_size(key) -- returns how many bytes after the
+ *                     node should be allocated for dynamic data. Default=0
+ *                     or length of the string with HASH_KEY_ENDSTRING.
+ *  HASH_GIVE_INIT_KEY void init_key(node *,key) -- initialize key in a newly
+ *                     created node. Defaults: assignment for atomic keys
+ *                     and static strings, strcpy for end-allocated strings.
+ *  HASH_GIVE_INIT_DATA        void init_data(node *) -- initialize data fields in a
+ *                     newly created node. Very useful for lookup operations.
+ *  HASH_GIVE_ALLOC    void *alloc(unsigned int size) -- allocate space for
+ *                     a node. Default is either normal or pooled allocation
+ *                     depending on whether we want deletions.
+ *                     void free(void *) -- the converse.
+ *
+ *  ... and a couple of extra parameters:
+ *
+ *  HASH_NOCASE                string comparisons should be case-insensitive.
+ *  HASH_DEFAULT_SIZE=n        initially, use hash table of `n' entries.
+ *                     The `n' has to be a power of two.
+ *  HASH_CONSERVE_SPACE        use as little space as possible.
+ *  HASH_FN_BITS=n     The hash function gives only `n' significant bits.
+ *  HASH_ATOMIC_TYPE=t Atomic values are of type `t' instead of int.
+ *  HASH_USE_POOL=pool Allocate all nodes from given mempool.
+ *                     Collides with delete/remove functions.
+ *
+ *  You also get a iterator macro at no extra charge:
+ *
+ *  HASH_FOR_ALL(hash_prefix, variable)
+ *    {
+ *      // node *variable gets declared automatically
+ *      do_something_with_node(variable);
+ *      // use HASH_BREAK and HASH_CONTINUE instead of break and continue
+ *     // you must not alter contents of the hash table here
+ *    }
+ *  HASH_END_FOR;
+ *
+ *  Then include <lib/hashtable.h> and voila, you have a hash table
+ *  suiting all your needs (at least those which you've revealed :) ).
+ *
+ *  After including this file, all parameter macros are automatically
+ *  undef'd.
+ */
+
+#ifndef _SHERLOCK_HASHFUNC_H
+#include "lib/hashfunc.h"
+#endif
+
+#include <string.h>
+
+#if !defined(HASH_NODE) || !defined(HASH_PREFIX)
+#error Some of the mandatory configuration macros are missing.
+#endif
+
+#define P(x) HASH_PREFIX(x)
+
+/* Declare buckets and the hash table */
+
+typedef HASH_NODE P(node);
+
+typedef struct P(bucket) {
+  struct P(bucket) *next;
+#ifndef HASH_CONSERVE_SPACE
+  uns hash;
+#endif
+  P(node) n;
+} P(bucket);
+
+struct P(table) {
+  uns hash_size;
+  uns hash_count, hash_max, hash_min, hash_hard_max, hash_mask;
+  P(bucket) **ht;
+} P(table);
+
+#define T P(table)
+
+/* Preset parameters */
+
+#if defined(HASH_KEY_ATOMIC)
+
+#define HASH_KEY(x) x HASH_KEY_ATOMIC
+
+#ifndef HASH_ATOMIC_TYPE
+#  define HASH_ATOMIC_TYPE int
+#endif
+#define HASH_KEY_DECL HASH_ATOMIC_TYPE HASH_KEY( )
+
+#ifndef HASH_GIVE_HASHFN
+#  define HASH_GIVE_HASHFN
+   static inline int P(hash) (HASH_ATOMIC_TYPE x)
+   { return hash_int(x); }
+#endif
+
+#ifndef HASH_GIVE_EQ
+#  define HASH_GIVE_EQ
+   static inline int P(eq) (HASH_ATOMIC_TYPE x, HASH_ATOMIC_TYPE y)
+   { return x == y; }
+#endif
+
+#ifndef HASH_GIVE_INIT_KEY
+#  define HASH_GIVE_INIT_KEY
+   static inline void P(init_key) (P(node) *n, HASH_ATOMIC_TYPE k)
+   { HASH_KEY(n->) = k; }
+#endif
+
+#ifndef HASH_CONSERVE_SPACE
+#define HASH_CONSERVE_SPACE
+#endif
+
+#elif defined(HASH_KEY_STRING) || defined(HASH_KEY_ENDSTRING)
+
+#ifdef HASH_KEY_STRING
+#  define HASH_KEY(x) x HASH_KEY_STRING
+#  ifndef HASH_GIVE_INIT_KEY
+#    define HASH_GIVE_INIT_KEY
+     static inline void P(init_key) (P(node) *n, char *k)
+     { HASH_KEY(n->) = k; }
+#  endif
+#else
+#  define HASH_KEY(x) x HASH_KEY_ENDSTRING
+#  define HASH_GIVE_EXTRA_SIZE
+   static inline int P(extra_size) (char *k)
+   { return strlen(k); }
+#  ifndef HASH_GIVE_INIT_KEY
+#    define HASH_GIVE_INIT_KEY
+     static inline void P(init_key) (P(node) *n, char *k)
+     { strcpy(HASH_KEY(n->), k); }
+#  endif
+#endif
+#define HASH_KEY_DECL char *HASH_KEY( )
+
+#ifndef HASH_GIVE_HASHFN
+#define HASH_GIVE_HASHFN
+  static inline uns P(hash) (char *k)
+   {
+#    ifdef HASH_NOCASE
+       return hash_string_nocase(k);
+#    else
+       return hash_string(k);
+#    endif
+   }
+#endif
+
+#ifndef HASH_GIVE_EQ
+#  define HASH_GIVE_EQ
+   static inline int P(eq) (char *x, char *y)
+   {
+#    ifdef HASH_NOCASE
+       return !strcasecmp(x,y);
+#    else
+       return !strcmp(x,y);
+#    endif
+   }
+#endif
+
+#elif defined(HASH_KEY_COMPLEX)
+
+#define HASH_KEY(x) HASH_KEY_COMPLEX(x)
+
+#else
+#error You forgot to set the hash key type.
+#endif
+
+/* Defaults for missing parameters */
+
+#ifndef HASH_GIVE_HASHFN
+#error Unable to determine which hash function to use.
+#endif
+
+#ifndef HASH_GIVE_EQ
+#error Unable to determine how to compare two keys.
+#endif
+
+#ifdef HASH_GIVE_EXTRA_SIZE
+/* This trickery is needed to avoid `unused parameter' warnings */
+#define HASH_EXTRA_SIZE P(extra_size)
+#else
+/*
+ *  Beware, C macros are expanded iteratively, not recursively,
+ *  hence we get only a _single_ argument, although the expansion
+ *  of HASH_KEY contains commas.
+ */
+#define HASH_EXTRA_SIZE(x) 0
+#endif
+
+#ifndef HASH_GIVE_INIT_KEY
+#error Unable to determine how to initialize keys.
+#endif
+
+#ifndef HASH_GIVE_INIT_DATA
+static inline void P(init_data) (P(node) *n UNUSED)
+{
+}
+#endif
+
+#include <stdlib.h>
+
+#ifndef HASH_GIVE_ALLOC
+#ifdef HASH_USE_POOL
+
+static inline void * P(alloc) (unsigned int size)
+{ return mp_alloc_fast(HASH_USE_POOL, size); }
+
+#else
+
+static inline void * P(alloc) (unsigned int size)
+{ return xmalloc(size); }
+
+static inline void P(free) (void *x)
+{ xfree(x); }
+
+#endif
+#endif
+
+#ifndef HASH_DEFAULT_SIZE
+#define HASH_DEFAULT_SIZE 32
+#endif
+
+#ifndef HASH_FN_BITS
+#define HASH_FN_BITS 32
+#endif
+
+/* Now the operations */
+
+static void P(alloc_table) (void)
+{
+  T.ht = xmalloc(sizeof(void *) * T.hash_size);
+  bzero(T.ht, sizeof(void *) * T.hash_size);
+  T.hash_max = T.hash_size * 2;
+  if (T.hash_max > T.hash_hard_max)
+    T.hash_max = T.hash_hard_max;
+  T.hash_min = T.hash_size / 4;
+  T.hash_mask = T.hash_size - 1;
+}
+
+static void P(init) (void)
+{
+  T.hash_count = 0;
+  T.hash_size = HASH_DEFAULT_SIZE;
+#if HASH_FN_BITS < 28
+  T.hash_hard_max = 1 << HASH_FN_BITS;
+#else
+  T.hash_hard_max = 1 << 28;
+#endif
+  P(alloc_table)();
+}
+
+#ifdef HASH_WANT_CLEANUP
+static void P(cleanup) (void)
+{
+#ifndef HASH_USE_POOL
+  uns i;
+  P(bucket) *b, *bb;
+
+  for (i=0; i<T.hash_size; i++)
+    for (b=T.ht[i]; b; b=bb)
+      {
+       bb = b->next;
+       P(free)(b);
+      }
+#endif
+  xfree(T.ht);
+}
+#endif
+
+static inline uns P(bucket_hash) (P(bucket) *b)
+{
+#ifdef HASH_CONSERVE_SPACE
+  return P(hash)(HASH_KEY(b->n.));
+#else
+  return b->hash;
+#endif
+}
+
+static void P(rehash) (uns size)
+{
+  P(bucket) *b, *nb;
+  P(bucket) **oldt = T.ht, **newt;
+  uns oldsize = T.hash_size;
+  uns i, h;
+
+  // log(L_DEBUG, "Rehashing %d->%d at count %d", oldsize, size, T.hash_count);
+  T.hash_size = size;
+  P(alloc_table)();
+  newt = T.ht;
+  for (i=0; i<oldsize; i++)
+    {
+      b = oldt[i];
+      while (b)
+       {
+         nb = b->next;
+         h = P(bucket_hash)(b) & T.hash_mask;
+         b->next = newt[h];
+         newt[h] = b;
+         b = nb;
+       }
+    }
+  xfree(oldt);
+}
+
+#ifdef HASH_WANT_FIND
+static P(node) * P(find) (HASH_KEY_DECL)
+{
+  uns h0 = P(hash) (HASH_KEY( ));
+  uns h = h0 & T.hash_mask;
+  P(bucket) *b;
+
+  for (b=T.ht[h]; b; b=b->next)
+    {
+      if (
+#ifndef HASH_CONSERVE_SPACE
+         b->hash == h0 &&
+#endif
+         P(eq)(HASH_KEY( ), HASH_KEY(b->n.)))
+       return &b->n;
+    }
+  return NULL;
+}
+#endif
+
+#ifdef HASH_WANT_NEW
+static P(node) * P(new) (HASH_KEY_DECL)
+{
+  uns h0, h;
+  P(bucket) *b;
+
+  h0 = P(hash) (HASH_KEY( ));
+  h = h0 & T.hash_mask;
+  b = P(alloc) (sizeof(struct P(bucket)) + HASH_EXTRA_SIZE(HASH_KEY( )));
+  b->next = T.ht[h];
+  T.ht[h] = b;
+#ifndef HASH_CONSERVE_SPACE
+  b->hash = h0;
+#endif
+  P(init_key)(&b->n, HASH_KEY( ));
+  P(init_data)(&b->n);
+  if (T.hash_count++ >= T.hash_max)
+    P(rehash)(2*T.hash_size);
+  return &b->n;
+}
+#endif
+
+#ifdef HASH_WANT_LOOKUP
+static P(node) * P(lookup) (HASH_KEY_DECL)
+{
+  uns h0 = P(hash) (HASH_KEY( ));
+  uns h = h0 & T.hash_mask;
+  P(bucket) *b;
+
+  for (b=T.ht[h]; b; b=b->next)
+    {
+      if (
+#ifndef HASH_CONSERVE_SPACE
+         b->hash == h0 &&
+#endif
+         P(eq)(HASH_KEY( ), HASH_KEY(b->n.)))
+       return &b->n;
+    }
+
+  b = P(alloc) (sizeof(struct P(bucket)) + HASH_EXTRA_SIZE(HASH_KEY( )));
+  b->next = T.ht[h];
+  T.ht[h] = b;
+#ifndef HASH_CONSERVE_SPACE
+  b->hash = h0;
+#endif
+  P(init_key)(&b->n, HASH_KEY( ));
+  P(init_data)(&b->n);
+  if (T.hash_count++ >= T.hash_max)
+    P(rehash)(2*T.hash_size);
+  return &b->n;
+}
+#endif
+
+#ifdef HASH_WANT_DELETE
+static int P(delete) (HASH_KEY_DECL)
+{
+  uns h0 = P(hash) (HASH_KEY( ));
+  uns h = h0 & T.hash_mask;
+  P(bucket) *b, **bb;
+
+  for (bb=&T.ht[h]; b=*bb; bb=&b->next)
+    {
+      if (
+#ifndef HASH_CONSERVE_SPACE
+         b->hash == h0 &&
+#endif
+         P(eq)(HASH_KEY( ), HASH_KEY(b->n.)))
+       {
+         *bb = b->next;
+         P(free)(b);
+         if (--T.hash_count < T.hash_min)
+           P(rehash)(T.hash_size/2);
+         return 1;
+       }
+    }
+  return 0;
+}
+#endif
+
+#ifdef HASH_WANT_REMOVE
+static void P(remove) (P(node) *n)
+{
+  P(bucket) *x = SKIP_BACK(struct P(bucket), n, n);
+  uns h0 = P(bucket_hash)(x);
+  uns h = h0 & T.hash_mask;
+  P(bucket) *b, **bb;
+
+  for (bb=&T.ht[h]; (b=*bb) && b != x; bb=&b->next)
+    ;
+  ASSERT(b);
+  *bb = b->next;
+  P(free)(b);
+  if (--T.hash_count < T.hash_min)
+    P(rehash)(T.hash_size/2);
+}
+#endif
+
+/* And the iterator */
+
+#ifndef HASH_FOR_ALL
+
+#define HASH_FOR_ALL(h_px, h_var)                                                      \
+do {                                                                                   \
+  uns h_slot;                                                                          \
+  struct HASH_GLUE(h_px,bucket) *h_buck;                                               \
+  for (h_slot=0; h_slot < HASH_GLUE(h_px,table).hash_size; h_slot++)                   \
+    for (h_buck = HASH_GLUE(h_px,table).ht[h_slot]; h_buck; h_buck = h_buck->next)     \
+      {                                                                                        \
+       HASH_GLUE(h_px,node) *h_var = &h_buck->n;
+#define HASH_END_FOR } } while(0)
+#define HASH_BREAK 
+#define HASH_CONTINUE continue
+#define HASH_GLUE(x,y) x##_##y
+
+#endif
+
+/* Finally, undefine all the parameters */
+
+#undef P
+#undef T
+
+#undef HASH_ATOMIC_TYPE
+#undef HASH_CONSERVE_SPACE
+#undef HASH_DEFAULT_SIZE
+#undef HASH_EXTRA_SIZE
+#undef HASH_FN_BITS
+#undef HASH_GIVE_ALLOC
+#undef HASH_GIVE_EQ
+#undef HASH_GIVE_EXTRA_SIZE
+#undef HASH_GIVE_HASHFN
+#undef HASH_GIVE_INIT_DATA
+#undef HASH_GIVE_INIT_KEY
+#undef HASH_KEY
+#undef HASH_KEY_ATOMIC
+#undef HASH_KEY_COMPLEX
+#undef HASH_KEY_DECL
+#undef HASH_KEY_ENDSTRING
+#undef HASH_KEY_STRING
+#undef HASH_NOCASE
+#undef HASH_NODE
+#undef HASH_PREFIX
+#undef HASH_USE_POOL
+#undef HASH_WANT_CLEANUP
+#undef HASH_WANT_DELETE
+#undef HASH_WANT_FIND
+#undef HASH_WANT_LOOKUP
+#undef HASH_WANT_NEW
+#undef HASH_WANT_REMOVE