Mercurial > hg > xemacs-beta
diff src/hash.c @ 0:376386a54a3c r19-14
Import from CVS: tag r19-14
author | cvs |
---|---|
date | Mon, 13 Aug 2007 08:45:50 +0200 |
parents | |
children | 8eaf7971accc |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/hash.c Mon Aug 13 08:45:50 2007 +0200 @@ -0,0 +1,490 @@ +/* Hash tables. + Copyright (C) 1992, 1993, 1994 Free Software Foundation, Inc. + +This file is part of XEmacs. + +XEmacs is free software; you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by the +Free Software Foundation; either version 2, or (at your option) any +later version. + +XEmacs is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +You should have received a copy of the GNU General Public License +along with XEmacs; see the file COPYING. If not, write to +the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. */ + +/* Synched up with: Not in FSF. */ + +#ifdef emacs +#include <config.h> +#include "lisp.h" + +#define NULL_ENTRY (LISP_TO_VOID (Qnil)) + +#else /* !emacs */ + +#define NULL_ENTRY ((void *) 1) + +#endif /* !emacs */ + +#include <string.h> +#include "hash.h" +#include "elhash.h" + +static CONST int +primes []={ + 13, + 29, 37, 47, 59, 71, 89, 107, 131, 163, 197, 239, 293, 353, 431, 521, 631, + 761, 919, 1103, 1327, 1597, 1931, 2333, 2801, 3371, 4049, 4861, 5839, 7013, + 8419, 10103, 12143, 14591, 17519, 21023, 25229, 30293, 36353, 43627, 52361, + 62851, 75431, 90523, 108631, 130363, 156437, 187751, 225307, 270371, 324449, + 389357, 467237, 560689, 672827, 807403, 968897, 1162687, 1395263, 1674319, + 2009191, 2411033, 2893249 +}; + +/* strings code */ + +/* from base/generic-hash.cc, and hence from Dragon book, p436 */ +unsigned long +string_hash (CONST void *xv) +{ + unsigned int h = 0; + unsigned int g; + unsigned CONST char *x = (unsigned CONST char *) xv; + + if (!x) return 0; + + while (*x != 0) + { + h = (h << 4) + *x++; + if ((g = h & 0xf0000000) != 0) + h = (h ^ (g >> 24)) ^ g; + } + + return h; +} + +unsigned long +memory_hash (CONST void *xv, int size) +{ + unsigned int h = 0; + unsigned int g; + unsigned CONST char *x = (unsigned CONST char *) xv; + + if (!x) return 0; + + while (size > 0) + { + h = (h << 4) + *x++; + if ((g = h & 0xf0000000) != 0) + h = (h ^ (g >> 24)) ^ g; + size--; + } + + return h; +} + +static int +string_eq (CONST void *st1v, CONST void *st2v) +{ + CONST char *st1 = (CONST char *)st1v; + CONST char *st2 = (CONST char *)st2v; + + if (!st1) + return (st2)?0:1; + else if (!st2) + return 0; + else + return !strcmp (st1, st2); +} + + +static unsigned int +prime_size (unsigned int size) +{ + unsigned int i; + CONST int lim = countof (primes); + for (i = 0; i < lim; i++) + if (size <= primes [i]) return primes [i]; + return primes [lim - 1]; +} + +static void rehash (hentry *harray, c_hashtable ht, unsigned int size); + +#define KEYS_DIFFER_P(old, new, testfun) \ + ((testfun)?(((old) == (new))?0:(!(testfun ((old), new)))):((old) != (new))) + +CONST void * +gethash (CONST void *key, c_hashtable hash, CONST void **ret_value) +{ + hentry *harray = hash->harray; + int (*test_function) (CONST void *, CONST void *) = hash->test_function; + unsigned int hsize = hash->size; + unsigned int hcode_initial = + (hash->hash_function)?(hash->hash_function(key)):((unsigned long) key); + unsigned int hcode = hcode_initial % hsize; + hentry *e = &harray [hcode]; + CONST void *e_key = e->key; + + if (!key) + { + *ret_value = hash->zero_entry; + return (void *) hash->zero_set; + } + + if ((e_key)? + (KEYS_DIFFER_P (e_key, key, test_function)): + (e->contents == NULL_ENTRY)) + { + unsigned int h2 = hsize - 2; + unsigned int incr = 1 + (hcode_initial % h2); + do + { + hcode = hcode + incr; + if (hcode >= hsize) hcode = hcode - hsize; + e = &harray [hcode]; + e_key = e->key; + } + while ((e_key)? + (KEYS_DIFFER_P (e_key, key, test_function)): + (e->contents == NULL_ENTRY)); + } + + *ret_value = e->contents; + return e->key; +} + +void +clrhash (c_hashtable hash) +{ + memset (hash->harray, 0, sizeof (hentry) * hash->size); + hash->zero_entry = 0; + hash->zero_set = 0; + hash->fullness = 0; +} + +void +free_hashtable (c_hashtable hash) +{ +#ifdef emacs + if (!NILP (hash->elisp_table)) + return; +#endif + xfree (hash->harray); + xfree (hash); +} + +c_hashtable +make_hashtable (unsigned int hsize) +{ + c_hashtable res = (c_hashtable) xmalloc (sizeof (struct _C_hashtable)); + memset (res, 0, sizeof (struct _C_hashtable)); + res->size = prime_size ((13 * hsize) / 10); + res->harray = (hentry *) xmalloc (sizeof (hentry) * res->size); +#ifdef emacs + res->elisp_table = Qnil; +#endif + clrhash (res); + return res; +} + +c_hashtable +make_general_hashtable (unsigned int hsize, + unsigned long (*hash_function) (CONST void *), + int (*test_function) (CONST void *, CONST void *)) +{ + c_hashtable res = (c_hashtable) xmalloc (sizeof (struct _C_hashtable)); + memset (res, 0, sizeof (struct _C_hashtable)); + res->size = prime_size ((13 * hsize) / 10); + res->harray = (hentry *) xmalloc (sizeof (hentry) * res->size); + res->hash_function = hash_function; + res->test_function = test_function; +#ifdef emacs + res->elisp_table = Qnil; +#endif + clrhash (res); + return res; +} + +c_hashtable +make_strings_hashtable (unsigned int hsize) +{ + return make_general_hashtable (hsize, string_hash, string_eq); +} + +#ifdef emacs +unsigned int +compute_harray_size (unsigned int hsize) +{ + return prime_size ((13 * hsize) / 10); +} +#endif + +void +copy_hash (c_hashtable dest, c_hashtable src) +{ +#ifdef emacs + /* if these are not the same, then we are losing here */ + if ((NILP (dest->elisp_table)) != (NILP (src->elisp_table))) + { + error ("Incompatible hashtable types to copy_hash."); + return; + } +#endif + + if (dest->size != src->size) + { +#ifdef emacs + if (!NILP (dest->elisp_table)) + elisp_hvector_free (dest->harray, dest->elisp_table); + else +#endif + xfree (dest->harray); + + dest->size = src->size; +#ifdef emacs + if (!NILP (dest->elisp_table)) + dest->harray = + (hentry *) elisp_hvector_malloc + (sizeof (hentry) * dest->size, dest->elisp_table); + else +#endif + dest->harray = (hentry *) xmalloc (sizeof (hentry) * dest->size); + } + dest->fullness = src->fullness; + dest->zero_entry = src->zero_entry; + dest->zero_set = src->zero_set; + dest->hash_function = src->hash_function; + dest->test_function = src->test_function; + memcpy (dest->harray, src->harray, sizeof (hentry) * dest->size); +} + +static void +grow_hashtable (c_hashtable hash, unsigned int new_size) +{ + unsigned int old_hsize = hash->size; + hentry *old_harray = hash->harray; + unsigned int new_hsize = prime_size (new_size); + hentry *new_harray; + +#ifdef emacs + /* We test for Qzero to facilitate free-hook.c. That module creates + a hashtable very very early, at which point Qnil has not yet + been set and is thus all zeroes. Qzero is "automatically" + initialized at startup because its correct value is also all + zeroes. */ + if (!NILP (hash->elisp_table) && !ZEROP (hash->elisp_table)) + new_harray = (hentry *) elisp_hvector_malloc (sizeof (hentry) * new_hsize, + hash->elisp_table); + else +#endif + new_harray = + (hentry *) xmalloc (sizeof (hentry) * new_hsize); + + hash->size = new_hsize; + hash->harray = new_harray; + + /* do the rehash on the "grown" table */ + { + long old_zero_set = hash->zero_set; + void *old_zero_entry = hash->zero_entry; + clrhash (hash); + hash->zero_set = old_zero_set; + hash->zero_entry = old_zero_entry; + rehash (old_harray, hash, old_hsize); + } + +#ifdef emacs + if (!NILP (hash->elisp_table) && !ZEROP (hash->elisp_table)) + elisp_hvector_free (old_harray, hash->elisp_table); + else +#endif + xfree (old_harray); +} + +void +expand_hashtable (c_hashtable hash, unsigned int needed_size) +{ + unsigned int hsize = hash->size; + int comfortable_size = (13 * needed_size) / 10; + if (hsize < comfortable_size) + grow_hashtable (hash, comfortable_size + 1); +} + +void +puthash (CONST void *key, void *cont, c_hashtable hash) +{ + unsigned int hsize = hash->size; + int (*test_function) (CONST void *, CONST void *) = hash->test_function; + unsigned int fullness = hash->fullness; + hentry *harray; + CONST void *e_key; + hentry *e; + unsigned int hcode_initial = + (hash->hash_function)?(hash->hash_function(key)):((unsigned long) key); + unsigned int hcode; + unsigned int incr = 0; + unsigned int h2; + CONST void *oldcontents; + + if (!key) + { + hash->zero_entry = cont; + hash->zero_set = 1; + return; + } + + if (hsize < (1 + ((13 * fullness) / 10))) + { + grow_hashtable (hash, hsize + 1); + hsize = hash->size; + fullness = hash->fullness; + } + + harray= hash->harray; + h2 = hsize - 2; + + hcode = hcode_initial % hsize; + + e_key = harray [hcode].key; + if (e_key && (KEYS_DIFFER_P (e_key, key, test_function))) + { + h2 = hsize - 2; + incr = 1 + (hcode_initial % h2); + do + { + hcode = hcode + incr; + if (hcode >= hsize) hcode = hcode - hsize; + e_key = harray [hcode].key; + } + while (e_key && (KEYS_DIFFER_P (e_key, key, test_function))); + } + oldcontents = harray [hcode].contents; + harray [hcode].key = key; + harray [hcode].contents = cont; + /* if the entry that we used was a deleted entry, + check for a non deleted entry of the same key, + then delete it */ + if (!e_key && (oldcontents == NULL_ENTRY)) + { + if (!incr) incr = 1 + ((unsigned long) key % h2); + + do + { + hcode = hcode + incr; + if (hcode >= hsize) hcode = hcode - hsize; + e = &harray [hcode]; + e_key = e->key; + } + while ((e_key)? + (KEYS_DIFFER_P (e_key, key, test_function)): + (e->contents == NULL_ENTRY)); + + if (e_key) + { + e->key = 0; + e->contents = NULL_ENTRY; + } + } + + /* only increment the fullness when we used up a new hentry */ + if (!e_key || (KEYS_DIFFER_P (e_key, key, test_function))) + hash->fullness++; +} + +static void +rehash (hentry *harray, c_hashtable hash, unsigned int size) +{ + hentry *limit = harray + size; + hentry *e; + for (e = harray; e < limit; e++) + { + if (e->key) + puthash (e->key, e->contents, hash); + } +} + +void +remhash (CONST void *key, c_hashtable hash) +{ + hentry *harray = hash->harray; + int (*test_function) (CONST void*, CONST void*) = hash->test_function; + unsigned int hsize = hash->size; + unsigned int hcode_initial = + (hash->hash_function)?(hash->hash_function(key)):((unsigned long) key); + unsigned int hcode = hcode_initial % hsize; + hentry *e = &harray [hcode]; + CONST void *e_key = e->key; + + if (!key) + { + hash->zero_entry = 0; + hash->zero_set = 0; + return; + } + + if ((e_key)? + (KEYS_DIFFER_P (e_key, key, test_function)): + (e->contents == NULL_ENTRY)) + { + unsigned int h2 = hsize - 2; + unsigned int incr = 1 + (hcode_initial % h2); + do + { + hcode = hcode + incr; + if (hcode >= hsize) hcode = hcode - hsize; + e = &harray [hcode]; + e_key = e->key; + } + while ((e_key)? + (KEYS_DIFFER_P (e_key, key, test_function)): + (e->contents == NULL_ENTRY)); + } + if (e_key) + { + e->key = 0; + e->contents = NULL_ENTRY; + /* Note: you can't do fullness-- here, it breaks the world. */ + } +} + +void +maphash (maphash_function mf, c_hashtable hash, void *arg) +{ + hentry *e; + hentry *limit; + + if (hash->zero_set) + ((*mf) (0, hash->zero_entry, arg)); + + for (e = hash->harray, limit = e + hash->size; e < limit; e++) + { + if (e->key) + ((*mf) (e->key, e->contents, arg)); + } +} + +void +map_remhash (remhash_predicate predicate, c_hashtable hash, void *arg) +{ + hentry *e; + hentry *limit; + + if (hash->zero_set && ((*predicate) (0, hash->zero_entry, arg))) + { + hash->zero_set = 0; + hash->zero_entry = 0; + } + + for (e = hash->harray, limit = e + hash->size; e < limit; e++) + if ((*predicate) (e->key, e->contents, arg)) + { + e->key = 0; + e->contents = NULL_ENTRY; + } +}