X-Git-Url: http://git.sourceforge.jp/view?a=blobdiff_plain;f=libiberty%2Fhashtab.c;h=32067afb5b8f94ef3bf312e979dfc90ba90eb9d8;hb=d5ab03ba495cb7517f166b881eec7251ab7589e7;hp=fa0ffac22fb95a4330d34b7a63e0f1f65417875a;hpb=8afd414563ab5197c91f18051557e7c8b33d9eee;p=pf3gnuchains%2Fgcc-fork.git diff --git a/libiberty/hashtab.c b/libiberty/hashtab.c index fa0ffac22fb..32067afb5b8 100644 --- a/libiberty/hashtab.c +++ b/libiberty/hashtab.c @@ -1,5 +1,5 @@ /* An expandable hash tables datatype. - Copyright (C) 1999, 2000 Free Software Foundation, Inc. + Copyright (C) 1999, 2000, 2001, 2002, 2003 Free Software Foundation, Inc. Contributed by Vladimir Makarov (vmakarov@cygnus.com). This file is part of the libiberty library. @@ -41,6 +41,10 @@ Boston, MA 02111-1307, USA. */ #include #endif +#ifdef HAVE_STRING_H +#include +#endif + #include #include "libiberty.h" @@ -48,16 +52,18 @@ Boston, MA 02111-1307, USA. */ /* This macro defines reserved value for empty table entry. */ -#define EMPTY_ENTRY ((void *) 0) +#define EMPTY_ENTRY ((PTR) 0) /* This macro defines reserved value for table entry which contained a deleted element. */ -#define DELETED_ENTRY ((void *) 1) +#define DELETED_ENTRY ((PTR) 1) static unsigned long higher_prime_number PARAMS ((unsigned long)); static hashval_t hash_pointer PARAMS ((const void *)); static int eq_pointer PARAMS ((const void *, const void *)); +static int htab_expand PARAMS ((htab_t)); +static PTR *find_empty_slot_for_expand PARAMS ((htab_t, hashval_t)); /* At some point, we could make these be NULL, and modify the hash-table routines to handle NULL specially; that would avoid @@ -65,52 +71,86 @@ static int eq_pointer PARAMS ((const void *, const void *)); htab_hash htab_hash_pointer = hash_pointer; htab_eq htab_eq_pointer = eq_pointer; -/* The following function returns the nearest prime number which is - greater than a given source number, N. */ +/* The following function returns a nearest prime number which is + greater than N, and near a power of two. */ static unsigned long higher_prime_number (n) unsigned long n; { - unsigned long i; - - /* Ensure we have a larger number and then force to odd. */ - n++; - n |= 0x01; - - /* All odd numbers < 9 are prime. */ - if (n < 9) - return n; - - /* Otherwise find the next prime using a sieve. */ - - next: + /* These are primes that are near, but slightly smaller than, a + power of two. */ + static const unsigned long primes[] = { + (unsigned long) 7, + (unsigned long) 13, + (unsigned long) 31, + (unsigned long) 61, + (unsigned long) 127, + (unsigned long) 251, + (unsigned long) 509, + (unsigned long) 1021, + (unsigned long) 2039, + (unsigned long) 4093, + (unsigned long) 8191, + (unsigned long) 16381, + (unsigned long) 32749, + (unsigned long) 65521, + (unsigned long) 131071, + (unsigned long) 262139, + (unsigned long) 524287, + (unsigned long) 1048573, + (unsigned long) 2097143, + (unsigned long) 4194301, + (unsigned long) 8388593, + (unsigned long) 16777213, + (unsigned long) 33554393, + (unsigned long) 67108859, + (unsigned long) 134217689, + (unsigned long) 268435399, + (unsigned long) 536870909, + (unsigned long) 1073741789, + (unsigned long) 2147483647, + /* 4294967291L */ + ((unsigned long) 2147483647) + ((unsigned long) 2147483644), + }; + + const unsigned long *low = &primes[0]; + const unsigned long *high = &primes[sizeof(primes) / sizeof(primes[0])]; + + while (low != high) + { + const unsigned long *mid = low + (high - low) / 2; + if (n > *mid) + low = mid + 1; + else + high = mid; + } - for (i = 3; i * i <= n; i += 2) - if (n % i == 0) - { - n += 2; - goto next; - } + /* If we've run out of primes, abort. */ + if (n > *low) + { + fprintf (stderr, "Cannot find prime bigger than %lu\n", n); + abort (); + } - return n; + return *low; } /* Returns a hash code for P. */ static hashval_t hash_pointer (p) - const void *p; + const PTR p; { - return (hashval_t) p; + return (hashval_t) ((long)p >> 3); } /* Returns non-zero if P1 and P2 are equal. */ static int eq_pointer (p1, p2) - const void *p1; - const void *p2; + const PTR p1; + const PTR p2; { return p1 == p2; } @@ -118,27 +158,119 @@ eq_pointer (p1, p2) /* This function creates table with length slightly longer than given source length. Created hash table is initiated as empty (all the hash table entries are EMPTY_ENTRY). The function returns the - created hash table. */ + created hash table, or NULL if memory allocation fails. */ htab_t -htab_create (size, hash_f, eq_f, del_f) +htab_create_alloc (size, hash_f, eq_f, del_f, alloc_f, free_f) size_t size; htab_hash hash_f; htab_eq eq_f; htab_del del_f; + htab_alloc alloc_f; + htab_free free_f; { htab_t result; size = higher_prime_number (size); - result = (htab_t) xcalloc (1, sizeof (struct htab)); - result->entries = (void **) xcalloc (size, sizeof (void *)); + result = (htab_t) (*alloc_f) (1, sizeof (struct htab)); + if (result == NULL) + return NULL; + result->entries = (PTR *) (*alloc_f) (size, sizeof (PTR)); + if (result->entries == NULL) + { + if (free_f != NULL) + (*free_f) (result); + return NULL; + } result->size = size; result->hash_f = hash_f; result->eq_f = eq_f; result->del_f = del_f; + result->alloc_f = alloc_f; + result->free_f = free_f; return result; } +/* As above, but use the variants of alloc_f and free_f which accept + an extra argument. */ + +htab_t +htab_create_alloc_ex (size, hash_f, eq_f, del_f, alloc_arg, alloc_f, + free_f) + size_t size; + htab_hash hash_f; + htab_eq eq_f; + htab_del del_f; + PTR alloc_arg; + htab_alloc_with_arg alloc_f; + htab_free_with_arg free_f; +{ + htab_t result; + + size = higher_prime_number (size); + result = (htab_t) (*alloc_f) (alloc_arg, 1, sizeof (struct htab)); + if (result == NULL) + return NULL; + result->entries = (PTR *) (*alloc_f) (alloc_arg, size, sizeof (PTR)); + if (result->entries == NULL) + { + if (free_f != NULL) + (*free_f) (alloc_arg, result); + return NULL; + } + result->size = size; + result->hash_f = hash_f; + result->eq_f = eq_f; + result->del_f = del_f; + result->alloc_arg = alloc_arg; + result->alloc_with_arg_f = alloc_f; + result->free_with_arg_f = free_f; + return result; +} + +/* Update the function pointers and allocation parameter in the htab_t. */ + +void +htab_set_functions_ex (htab, hash_f, eq_f, del_f, alloc_arg, alloc_f, free_f) + htab_t htab; + htab_hash hash_f; + htab_eq eq_f; + htab_del del_f; + PTR alloc_arg; + htab_alloc_with_arg alloc_f; + htab_free_with_arg free_f; +{ + htab->hash_f = hash_f; + htab->eq_f = eq_f; + htab->del_f = del_f; + htab->alloc_arg = alloc_arg; + htab->alloc_with_arg_f = alloc_f; + htab->free_with_arg_f = free_f; +} + +/* These functions exist solely for backward compatibility. */ + +#undef htab_create +htab_t +htab_create (size, hash_f, eq_f, del_f) + size_t size; + htab_hash hash_f; + htab_eq eq_f; + htab_del del_f; +{ + return htab_create_alloc (size, hash_f, eq_f, del_f, xcalloc, free); +} + +htab_t +htab_try_create (size, hash_f, eq_f, del_f) + size_t size; + htab_hash hash_f; + htab_eq eq_f; + htab_del del_f; +{ + return htab_create_alloc (size, hash_f, eq_f, del_f, calloc, free); +} + /* This function frees all memory allocated for given hash table. Naturally the hash table must already exist. */ @@ -154,8 +286,16 @@ htab_delete (htab) && htab->entries[i] != DELETED_ENTRY) (*htab->del_f) (htab->entries[i]); - free (htab->entries); - free (htab); + if (htab->free_f != NULL) + { + (*htab->free_f) (htab->entries); + (*htab->free_f) (htab); + } + else if (htab->free_with_arg_f != NULL) + { + (*htab->free_with_arg_f) (htab->alloc_arg, htab->entries); + (*htab->free_with_arg_f) (htab->alloc_arg, htab); + } } /* This function clears all entries in the given hash table. */ @@ -172,7 +312,7 @@ htab_empty (htab) && htab->entries[i] != DELETED_ENTRY) (*htab->del_f) (htab->entries[i]); - memset (htab->entries, 0, htab->size * sizeof (void *)); + memset (htab->entries, 0, htab->size * sizeof (PTR)); } /* Similar to htab_find_slot, but without several unwanted side effects: @@ -182,27 +322,33 @@ htab_empty (htab) This function also assumes there are no deleted entries in the table. HASH is the hash value for the element to be inserted. */ -static void ** +static PTR * find_empty_slot_for_expand (htab, hash) htab_t htab; hashval_t hash; { size_t size = htab->size; - hashval_t hash2 = 1 + hash % (size - 2); unsigned int index = hash % size; + PTR *slot = htab->entries + index; + hashval_t hash2; + + if (*slot == EMPTY_ENTRY) + return slot; + else if (*slot == DELETED_ENTRY) + abort (); + hash2 = 1 + hash % (size - 2); for (;;) { - void **slot = htab->entries + index; + index += hash2; + if (index >= size) + index -= size; + slot = htab->entries + index; if (*slot == EMPTY_ENTRY) return slot; else if (*slot == DELETED_ENTRY) abort (); - - index += hash2; - if (index >= size) - index -= size; } } @@ -210,21 +356,41 @@ find_empty_slot_for_expand (htab, hash) entries and repeatedly inserts the table elements. The occupancy of the table after the call will be about 50%. Naturally the hash table must already exist. Remember also that the place of the - table entries is changed. */ + table entries is changed. If memory allocation failures are allowed, + this function will return zero, indicating that the table could not be + expanded. If all goes well, it will return a non-zero value. */ -static void +static int htab_expand (htab) htab_t htab; { - void **oentries; - void **olimit; - void **p; + PTR *oentries; + PTR *olimit; + PTR *p; + PTR *nentries; + size_t nsize; oentries = htab->entries; olimit = oentries + htab->size; - htab->size = higher_prime_number (htab->size * 2); - htab->entries = (void **) xcalloc (htab->size, sizeof (void **)); + /* Resize only when table after removal of unused elements is either + too full or too empty. */ + if ((htab->n_elements - htab->n_deleted) * 2 > htab->size + || ((htab->n_elements - htab->n_deleted) * 8 < htab->size + && htab->size > 32)) + nsize = higher_prime_number ((htab->n_elements - htab->n_deleted) * 2); + else + nsize = htab->size; + + if (htab->alloc_with_arg_f != NULL) + nentries = (PTR *) (*htab->alloc_with_arg_f) (htab->alloc_arg, nsize, + sizeof (PTR *)); + else + nentries = (PTR *) (*htab->alloc_f) (nsize, sizeof (PTR *)); + if (nentries == NULL) + return 0; + htab->entries = nentries; + htab->size = nsize; htab->n_elements -= htab->n_deleted; htab->n_deleted = 0; @@ -232,11 +398,11 @@ htab_expand (htab) p = oentries; do { - void *x = *p; + PTR x = *p; if (x != EMPTY_ENTRY && x != DELETED_ENTRY) { - void **q = find_empty_slot_for_expand (htab, (*htab->hash_f) (x)); + PTR *q = find_empty_slot_for_expand (htab, (*htab->hash_f) (x)); *q = x; } @@ -245,22 +411,26 @@ htab_expand (htab) } while (p < olimit); - free (oentries); + if (htab->free_f != NULL) + (*htab->free_f) (oentries); + else if (htab->free_with_arg_f != NULL) + (*htab->free_with_arg_f) (htab->alloc_arg, oentries); + return 1; } /* This function searches for a hash table entry equal to the given element. It cannot be used to insert or delete an element. */ -void * +PTR htab_find_with_hash (htab, element, hash) htab_t htab; - const void *element; + const PTR element; hashval_t hash; { unsigned int index; hashval_t hash2; size_t size; - void *entry; + PTR entry; htab->searches++; size = htab->size; @@ -290,10 +460,10 @@ htab_find_with_hash (htab, element, hash) /* Like htab_find_slot_with_hash, but compute the hash value from the element. */ -void * +PTR htab_find (htab, element) htab_t htab; - const void *element; + const PTR element; { return htab_find_with_hash (htab, element, (*htab->hash_f) (element)); } @@ -302,71 +472,83 @@ htab_find (htab, element) equal to the given element. To delete an entry, call this with INSERT = 0, then call htab_clear_slot on the slot returned (possibly after doing some checks). To insert an entry, call this with - INSERT = 1, then write the value you want into the returned slot. */ + INSERT = 1, then write the value you want into the returned slot. + When inserting an entry, NULL may be returned if memory allocation + fails. */ -void ** +PTR * htab_find_slot_with_hash (htab, element, hash, insert) htab_t htab; - const void *element; + const PTR element; hashval_t hash; enum insert_option insert; { - void **first_deleted_slot; + PTR *first_deleted_slot; unsigned int index; hashval_t hash2; size_t size; + PTR entry; - if (insert == INSERT && htab->size * 3 <= htab->n_elements * 4) - htab_expand (htab); + if (insert == INSERT && htab->size * 3 <= htab->n_elements * 4 + && htab_expand (htab) == 0) + return NULL; size = htab->size; - hash2 = 1 + hash % (size - 2); index = hash % size; htab->searches++; first_deleted_slot = NULL; + entry = htab->entries[index]; + if (entry == EMPTY_ENTRY) + goto empty_entry; + else if (entry == DELETED_ENTRY) + first_deleted_slot = &htab->entries[index]; + else if ((*htab->eq_f) (entry, element)) + return &htab->entries[index]; + + hash2 = 1 + hash % (size - 2); for (;;) { - void *entry = htab->entries[index]; + htab->collisions++; + index += hash2; + if (index >= size) + index -= size; + + entry = htab->entries[index]; if (entry == EMPTY_ENTRY) - { - if (insert == NO_INSERT) - return NULL; - - htab->n_elements++; - - if (first_deleted_slot) - { - *first_deleted_slot = EMPTY_ENTRY; - return first_deleted_slot; - } - - return &htab->entries[index]; - } - - if (entry == DELETED_ENTRY) + goto empty_entry; + else if (entry == DELETED_ENTRY) { if (!first_deleted_slot) first_deleted_slot = &htab->entries[index]; } - else if ((*htab->eq_f) (entry, element)) + else if ((*htab->eq_f) (entry, element)) return &htab->entries[index]; - - htab->collisions++; - index += hash2; - if (index >= size) - index -= size; } + + empty_entry: + if (insert == NO_INSERT) + return NULL; + + htab->n_elements++; + + if (first_deleted_slot) + { + *first_deleted_slot = EMPTY_ENTRY; + return first_deleted_slot; + } + + return &htab->entries[index]; } /* Like htab_find_slot_with_hash, but compute the hash value from the element. */ -void ** +PTR * htab_find_slot (htab, element, insert) htab_t htab; - const void *element; + const PTR element; enum insert_option insert; { return htab_find_slot_with_hash (htab, element, (*htab->hash_f) (element), @@ -380,9 +562,9 @@ htab_find_slot (htab, element, insert) void htab_remove_elt (htab, element) htab_t htab; - void *element; + PTR element; { - void **slot; + PTR *slot; slot = htab_find_slot (htab, element, NO_INSERT); if (*slot == EMPTY_ENTRY) @@ -402,7 +584,7 @@ htab_remove_elt (htab, element) void htab_clear_slot (htab, slot) htab_t htab; - void **slot; + PTR *slot; { if (slot < htab->entries || slot >= htab->entries + htab->size || *slot == EMPTY_ENTRY || *slot == DELETED_ENTRY) @@ -421,17 +603,20 @@ htab_clear_slot (htab, slot) argument. */ void -htab_traverse (htab, callback, info) +htab_traverse_noresize (htab, callback, info) htab_t htab; htab_trav callback; - void *info; + PTR info; { - void **slot = htab->entries; - void **limit = slot + htab->size; + PTR *slot; + PTR *limit; + + slot = htab->entries; + limit = slot + htab->size; do { - void *x = *slot; + PTR x = *slot; if (x != EMPTY_ENTRY && x != DELETED_ENTRY) if (!(*callback) (slot, info)) @@ -440,6 +625,21 @@ htab_traverse (htab, callback, info) while (++slot < limit); } +/* Like htab_traverse_noresize, but does resize the table when it is + too empty to improve effectivity of subsequent calls. */ + +void +htab_traverse (htab, callback, info) + htab_t htab; + htab_trav callback; + PTR info; +{ + if ((htab->n_elements - htab->n_deleted) * 8 < htab->size) + htab_expand (htab); + + htab_traverse_noresize (htab, callback, info); +} + /* Return the current size of given hash table. */ size_t @@ -470,3 +670,42 @@ htab_collisions (htab) return (double) htab->collisions / (double) htab->searches; } + +/* Hash P as a null-terminated string. + + Copied from gcc/hashtable.c. Zack had the following to say with respect + to applicability, though note that unlike hashtable.c, this hash table + implementation re-hashes rather than chain buckets. + + http://gcc.gnu.org/ml/gcc-patches/2001-08/msg01021.html + From: Zack Weinberg + Date: Fri, 17 Aug 2001 02:15:56 -0400 + + I got it by extracting all the identifiers from all the source code + I had lying around in mid-1999, and testing many recurrences of + the form "H_n = H_{n-1} * K + c_n * L + M" where K, L, M were either + prime numbers or the appropriate identity. This was the best one. + I don't remember exactly what constituted "best", except I was + looking at bucket-length distributions mostly. + + So it should be very good at hashing identifiers, but might not be + as good at arbitrary strings. + + I'll add that it thoroughly trounces the hash functions recommended + for this use at http://burtleburtle.net/bob/hash/index.html, both + on speed and bucket distribution. I haven't tried it against the + function they just started using for Perl's hashes. */ + +hashval_t +htab_hash_string (p) + const PTR p; +{ + const unsigned char *str = (const unsigned char *) p; + hashval_t r = 0; + unsigned char c; + + while ((c = *str++) != 0) + r = r * 67 + c - 113; + + return r; +}