#ifndef HAVE_SEARCH_H
/*
* hsearch() on Mac OS X 10.1.2 appears to be broken: there is no
* search.h; there is a search() in the C library, but it doesn't work
* properly. We include some hash functions here, protected by
* HAVE_SEARCH_H. Hopefully when search.h appears in Mac OS X,
* hsearch() will be fixed at the same time...
*
* Part of HTML-XML-utils, see:
* http://www.w3.org/Tools/HTML-XML-utils/
*/
#include "config.h"
#include <stdlib.h>
#include <assert.h>
#include <string.h>
#include "export.h"
#include "heap.e"
EXPORT typedef struct entry {char *key; void *data;} ENTRY;
EXPORT typedef enum {FIND, ENTER} ACTION;
static ENTRY *htab;
static int *htab_index1, *htab_index2;
static unsigned int htab_size = 0;
static unsigned int htab_inited;
/* isprime -- test if n is a prime number */
static int isprime(unsigned int n)
{
/* Simplistic algorithm, probably good enough for now */
unsigned int i;
assert(n % 2); /* n not even */
for (i = 3; i * i < n; i += 2) if (n % i == 0) return 0;
return 1;
}
/* hcreate -- create a hash table for at least nel entries */
EXPORT int hcreate(size_t nel)
{
/* Change nel to next higher prime */
for (nel |= 1; !isprime(nel); nel += 2) ;
/* Allocate hash table and array to keep track of initialized entries */
newarray(htab, nel);
newarray(htab_index1, nel);
newarray(htab_index2, nel);
if (!htab || !htab_index1 || !htab_index2) {
dispose(htab);
dispose(htab_index1);
dispose(htab_index2);
return 0; /* Out of memory */
}
htab_inited = 0;
htab_size = nel;
return 1;
}
/* hdestroy -- deallocate hash table */
EXPORT void hdestroy(void)
{
assert(htab_size);
dispose(htab_index1);
dispose(htab_index2);
dispose(htab);
htab_size = 0;
}
/* hsearch -- search for and/or insert an entry in the hash table */
EXPORT ENTRY *hsearch(ENTRY item, ACTION action)
{
unsigned int hval, i;
char *p;
assert(htab_size); /* There must be a hash table */
/* Compute a hash value */
#if 1
/* This function suggested by Dan Bernstein */
for (hval = 5381, p = item.key; *p; p++) hval = (hval * 33) ^ *p;
#else
i = hval = strlen(item.key);
do {i--; hval = (hval << 1) + item.key[i];} while (i > 0);
#endif
hval %= htab_size;
/* if (action == ENTER) debug("%d\n", hval); */
/* Look for either an empty slot or an entry with the wanted key */
i = hval;
while (htab_index1[i] < htab_inited
&& htab_index2[htab_index1[i]] == i
&& strcmp(htab[i].key, item.key) != 0) {
i = (i + 1) % htab_size; /* "Open" hash method */
if (i == hval) return NULL; /* Made full round */
}
/* Now we either have an empty slot or an entry with the same key */
if (action == ENTER) {
htab[i].key = item.key; /* Put the item in this slot */
htab[i].data = item.data;
if (htab_index1[i] >= htab_inited || htab_index2[htab_index1[i]] != i) {
/* Item was not yet used, mark it as used */
htab_index1[i] = htab_inited;
htab_index2[htab_inited] = i;
htab_inited++;
}
return &htab[i];
} else if (htab_index1[i] < htab_inited && htab_index2[htab_index1[i]] == i)
return &htab[i]; /* action == FIND, found key */
return NULL; /* Found empty slot */
}
#endif /* HAVE_SEARCH_H */