From: Robert Anisko Date: Sat, 25 Aug 2001 14:15:11 +0000 (+0000) Subject: * lib/hash.c: New file. X-Git-Tag: pre-merge-of-1-29-branch~66 X-Git-Url: https://git.saurik.com/bison.git/commitdiff_plain/68bd3b6bfa120d0775bbac310c8a047a31c07886?hp=bbb5bcc6f85b9ae3f951ab062455767318b827ad * lib/hash.c: New file. * lib/hash.h: New file. * lib/Makefile.am: Update. --- diff --git a/ChangeLog b/ChangeLog index 49cb99e1..75bdd858 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,9 @@ +2001-08-25 Robert Anisko + + * lib/hash.c: New file. + * lib/hash.h: New file. + * lib/Makefile.am: Update. + 2001-08-15 Akim Demaille Version 1.28c. diff --git a/lib/Makefile.am b/lib/Makefile.am index 529ac67a..a5711bc3 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -14,6 +14,7 @@ EXTRA_DIST = malloc.c realloc.c libbison_a_SOURCES = \ error.c error.h \ getopt.h getopt.c getopt1.c \ + hash.h hash.c \ obstack.h obstack.c \ quote.h quote.c quotearg.h quotearg.c \ xalloc.h xmalloc.c xstrdup.c diff --git a/lib/hash.c b/lib/hash.c new file mode 100644 index 00000000..c066c639 --- /dev/null +++ b/lib/hash.c @@ -0,0 +1,293 @@ +/* hash.c -- hash table maintenance + Copyright (C) 1995 Free Software Foundation, Inc. + Written by Greg McGary + + This program 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. + + This program 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 this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include +#include +#include "hash.h" +#include "error.h" +#include "system.h" +#include "xalloc.h" + +static void hash_rehash __P((struct hash_table* ht)); +static unsigned long round_up_2 __P((unsigned long rough)); + +/* Implement double hashing with open addressing. The table size is + always a power of two. The secondary (`increment') hash function + is forced to return an odd-value, in order to be relatively prime + to the table size. This guarantees that the increment can + potentially hit every slot in the table during collision + resolution. */ + +void *hash_deleted_item = &hash_deleted_item; + +/* Force the table size to be a power of two, possibly rounding up the + given size. */ + +void +hash_init (struct hash_table* ht, unsigned long size, + hash_func_t hash_1, hash_func_t hash_2, hash_cmp_func_t hash_cmp) +{ + ht->ht_size = round_up_2 (size); + if (ht->ht_size > (128 * 1024)) /* prevent size from getting out of hand */ + ht->ht_size /= 2; + ht->ht_vec = (void**) XCALLOC (struct token *, ht->ht_size); + if (ht->ht_vec == 0) + error (1, 0, _("can't allocate %ld bytes for hash table: memory exhausted"), + ht->ht_size * sizeof(struct token *)); + ht->ht_capacity = ht->ht_size * 15 / 16; /* 93.75% loading factor */ + ht->ht_fill = 0; + ht->ht_collisions = 0; + ht->ht_lookups = 0; + ht->ht_rehashes = 0; + ht->ht_hash_1 = hash_1; + ht->ht_hash_2 = hash_2; + ht->ht_compare = hash_cmp; +} + +/* Load an array of items into `ht'. */ + +void +hash_load (struct hash_table* ht, void *item_table, unsigned long cardinality, unsigned long size) +{ + char *items = (char *) item_table; + while (cardinality--) + { + hash_insert (ht, items); + items += size; + } +} + +/* Returns the address of the table slot matching `key'. If `key' is + not found, return the address of an empty slot suitable for + inserting `key'. The caller is responsible for incrementing + ht_fill on insertion. */ + +void ** +hash_find_slot (struct hash_table* ht, void const *key) +{ + void **slot; + void **deleted_slot = 0; + unsigned int hash_2 = 0; + unsigned int hash_1 = (*ht->ht_hash_1) (key); + + ht->ht_lookups++; + for (;;) + { + hash_1 %= ht->ht_size; + slot = &ht->ht_vec[hash_1]; + + if (*slot == 0) + return slot; + if (*slot == hash_deleted_item) + { + if (deleted_slot == 0) + deleted_slot = slot; + } + else + { + if (key == *slot) + return slot; + if ((*ht->ht_compare) (key, *slot) == 0) + return slot; + ht->ht_collisions++; + } + if (!hash_2) + hash_2 = (*ht->ht_hash_2) (key) | 1; + hash_1 += hash_2; + } +} + +void * +hash_find_item (struct hash_table* ht, void const *key) +{ + void **slot = hash_find_slot (ht, key); + return ((HASH_VACANT (*slot)) ? 0 : *slot); +} + +void * +hash_insert (struct hash_table* ht, void *item) +{ + void **slot = hash_find_slot (ht, item); + return hash_insert_at (ht, item, slot); +} + +void * +hash_insert_at (struct hash_table* ht, void *item, void const *slot) +{ + void *old_item = *(void **) slot; + if (HASH_VACANT (old_item)) + { + ht->ht_fill++; + old_item = item; + } + *(void const **) slot = item; + if (ht->ht_fill >= ht->ht_capacity) + hash_rehash (ht); + return old_item; +} + +void * +hash_delete (struct hash_table* ht, void const *item) +{ + void **slot = hash_find_slot (ht, item); + return hash_delete_at (ht, slot); +} + +void * +hash_delete_at (struct hash_table* ht, void const *slot) +{ + void *item = *(void **) slot; + if (!HASH_VACANT (item)) + { + *(void const **) slot = hash_deleted_item; + ht->ht_fill--; + return item; + } + else + return 0; +} + +void +hash_free_items (struct hash_table* ht) +{ + void **vec = ht->ht_vec; + void **end = &vec[ht->ht_size]; + for (; vec < end; vec++) + { + void *item = *vec; + if (!HASH_VACANT (item)) + free (item); + *vec = 0; + } + ht->ht_fill = 0; +} + +void +hash_delete_items (struct hash_table* ht) +{ + void **vec = ht->ht_vec; + void **end = &vec[ht->ht_size]; + for (; vec < end; vec++) + *vec = 0; + ht->ht_fill = 0; + ht->ht_collisions = 0; + ht->ht_lookups = 0; + ht->ht_rehashes = 0; +} + +void +hash_free (struct hash_table* ht, int free_items) +{ + if (free_items) + hash_free_items (ht); + free (ht->ht_vec); + ht->ht_vec = 0; + ht->ht_fill = 0; + ht->ht_capacity = 0; +} + +void +hash_map (struct hash_table *ht, hash_map_func_t map) +{ + void **slot; + void **end = &ht->ht_vec[ht->ht_size]; + + for (slot = ht->ht_vec; slot < end; slot++) + { + if (!HASH_VACANT (*slot)) + (*map) (*slot); + } +} + +/* Double the size of the hash table in the event of overflow... */ + +static void +hash_rehash (struct hash_table* ht) +{ + unsigned long old_ht_size = ht->ht_size; + void **old_vec = ht->ht_vec; + void **ovp; + void **slot; + + ht->ht_size *= 2; + ht->ht_rehashes++; + ht->ht_capacity = ht->ht_size - (ht->ht_size >> 4); + ht->ht_vec = (void **) XCALLOC (struct token *, ht->ht_size); + + for (ovp = old_vec; ovp < &old_vec[old_ht_size]; ovp++) + { + if (*ovp == 0) + continue; + slot = hash_find_slot (ht, *ovp); + *slot = *ovp; + } + free (old_vec); +} + +void +hash_print_stats (struct hash_table *ht, FILE *out_FILE) +{ + fprintf (out_FILE, _("Load=%ld/%ld=%.0f%%, "), ht->ht_fill, ht->ht_size, + 100.0 * (double) ht->ht_fill / (double) ht->ht_size); + fprintf (out_FILE, _("Rehash=%d, "), ht->ht_rehashes); + fprintf (out_FILE, _("Collisions=%ld/%ld=%.0f%%"), ht->ht_collisions, ht->ht_lookups, + (ht->ht_lookups + ? (100.0 * (double) ht->ht_collisions / (double) ht->ht_lookups) + : 0)); +} + +/* Dump all items into a NULL-terminated vector. Use the + user-supplied vector, or malloc one. */ + +void** +hash_dump (struct hash_table *ht, void **vector_0, qsort_cmp_t compare) +{ + void **vector; + void **slot; + void **end = &ht->ht_vec[ht->ht_size]; + + if (vector_0 == 0) + vector_0 = XMALLOC (void *, ht->ht_fill + 1); + vector = vector_0; + + for (slot = ht->ht_vec; slot < end; slot++) + if (!HASH_VACANT (*slot)) + *vector++ = *slot; + *vector = 0; + + if (compare) + qsort (vector_0, ht->ht_fill, sizeof (void *), compare); + return vector_0; +} + +/* Round a given number up to the nearest power of 2. */ + +static unsigned long +round_up_2 (unsigned long rough) +{ + int round; + + round = 1; + while (rough) + { + round <<= 1; + rough >>= 1; + } + return round; +} diff --git a/lib/hash.h b/lib/hash.h new file mode 100644 index 00000000..56618931 --- /dev/null +++ b/lib/hash.h @@ -0,0 +1,144 @@ +/* hash.h -- decls for hash table + Copyright (C) 1995 Free Software Foundation, Inc. + Written by Greg McGary + + This program 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. + + This program 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 this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#ifndef _hash_h_ +#define _hash_h_ + +#include + +typedef unsigned long (*hash_func_t) __P((void const *key)); +typedef int (*hash_cmp_func_t) __P((void const *x, void const *y)); +typedef void (*hash_map_func_t) __P((void const *item)); + +struct hash_table +{ + void **ht_vec; + unsigned long ht_size; /* total number of slots (power of 2) */ + unsigned long ht_capacity; /* usable slots, limited by loading-factor */ + unsigned long ht_fill; /* items in table */ + unsigned long ht_collisions; /* # of failed calls to comparison function */ + unsigned long ht_lookups; /* # of queries */ + unsigned int ht_rehashes; /* # of times we've expanded table */ + hash_func_t ht_hash_1; /* primary hash function */ + hash_func_t ht_hash_2; /* secondary hash function */ + hash_cmp_func_t ht_compare; /* comparison function */ +}; + +typedef int (*qsort_cmp_t) __P((void const *, void const *)); + +void hash_init __P((struct hash_table *ht, unsigned long size, + hash_func_t hash_1, hash_func_t hash_2, hash_cmp_func_t hash_cmp)); +void hash_load __P((struct hash_table *ht, void *item_table, + unsigned long cardinality, unsigned long size)); +void **hash_find_slot __P((struct hash_table *ht, void const *key)); +void *hash_find_item __P((struct hash_table *ht, void const *key)); +void *hash_insert __P((struct hash_table *ht, void *item)); +void *hash_insert_at __P((struct hash_table *ht, void *item, void const *slot)); +void *hash_delete __P((struct hash_table *ht, void const *item)); +void *hash_delete_at __P((struct hash_table *ht, void const *slot)); +void hash_delete_items __P((struct hash_table *ht)); +void hash_free_items __P((struct hash_table *ht)); +void hash_free __P((struct hash_table *ht, int free_items)); +void hash_map __P((struct hash_table *ht, hash_map_func_t map)); +void hash_print_stats __P((struct hash_table *ht, FILE *out_FILE)); +void **hash_dump __P((struct hash_table *ht, void **vector_0, qsort_cmp_t compare)); + +extern void *hash_deleted_item; +#define HASH_VACANT(item) ((item) == 0 || (void *) (item) == hash_deleted_item) + + +/* hash and comparison macros for string keys. */ + +#define STRING_HASH_1(_key_, _result_) { \ + unsigned char const *kk = (unsigned char const *) (_key_) - 1; \ + while (*++kk) \ + (_result_) += (*kk << (kk[1] & 0xf)); \ +} while (0) +#define return_STRING_HASH_1(_key_) do { \ + unsigned long result = 0; \ + STRING_HASH_1 ((_key_), result); \ + return result; \ +} while (0) + +#define STRING_HASH_2(_key_, _result_) do { \ + unsigned char const *kk = (unsigned char const *) (_key_) - 1; \ + while (*++kk) \ + (_result_) += (*kk << (kk[1] & 0x7)); \ +} while (0) +#define return_STRING_HASH_2(_key_) do { \ + unsigned long result = 0; \ + STRING_HASH_2 ((_key_), result); \ + return result; \ +} while (0) + +#define STRING_COMPARE(_x_, _y_, _result_) do { \ + unsigned char const *xx = (unsigned char const *) (_x_) - 1; \ + unsigned char const *yy = (unsigned char const *) (_y_) - 1; \ + do { \ + if (*++xx == '\0') { \ + yy++; \ + break; \ + } \ + } while (*xx == *++yy); \ + (_result_) = *xx - *yy; \ +} while (0) +#define return_STRING_COMPARE(_x_, _y_) do { \ + int result; \ + STRING_COMPARE (_x_, _y_, result); \ + return result; \ +} while (0) + +/* hash and comparison macros for integer keys. */ + +#define INTEGER_HASH_1(_key_, _result_) do { \ + (_result_) += ((unsigned long)(_key_)); \ +} while (0) +#define return_INTEGER_HASH_1(_key_) do { \ + unsigned long result = 0; \ + INTEGER_HASH_1 ((_key_), result); \ + return result; \ +} while (0) + +#define INTEGER_HASH_2(_key_, _result_) do { \ + (_result_) += ~((unsigned long)(_key_)); \ +} while (0) +#define return_INTEGER_HASH_2(_key_) do { \ + unsigned long result = 0; \ + INTEGER_HASH_2 ((_key_), result); \ + return result; \ +} while (0) + +#define INTEGER_COMPARE(_x_, _y_, _result_) do { \ + (_result_) = _x_ - _y_; \ +} while (0) +#define return_INTEGER_COMPARE(_x_, _y_) do { \ + int result; \ + INTEGER_COMPARE (_x_, _y_, result); \ + return result; \ +} while (0) + +/* hash and comparison macros for address keys. */ + +#define ADDRESS_HASH_1(_key_, _result_) INTEGER_HASH_1 (((unsigned long)(_key_)) >> 3, (_result_)) +#define ADDRESS_HASH_2(_key_, _result_) INTEGER_HASH_2 (((unsigned long)(_key_)) >> 3, (_result_)) +#define ADDRESS_COMPARE(_x_, _y_, _result_) INTEGER_COMPARE ((_x_), (_y_), (_result_)) +#define return_ADDRESS_HASH_1(_key_) return_INTEGER_HASH_1 (((unsigned long)(_key_)) >> 3) +#define return_ADDRESS_HASH_2(_key_) return_INTEGER_HASH_2 (((unsigned long)(_key_)) >> 3) +#define return_ADDRESS_COMPARE(_x_, _y_) return_INTEGER_COMPARE ((_x_), (_y_)) + +#endif /* not _hash_h_ */