X-Git-Url: http://git.sourceforge.jp/view?a=blobdiff_plain;f=libiberty%2Fhashtab.c;h=32067afb5b8f94ef3bf312e979dfc90ba90eb9d8;hb=d5ab03ba495cb7517f166b881eec7251ab7589e7;hp=4bcccee3aaf5cd23098720ea019958975860b335;hpb=696d659364a0c96aac78ccd86df45acfebdfa69c;p=pf3gnuchains%2Fgcc-fork.git diff --git a/libiberty/hashtab.c b/libiberty/hashtab.c index 4bcccee3aaf..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. @@ -62,7 +62,7 @@ Boston, MA 02111-1307, USA. */ 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 void htab_expand PARAMS ((htab_t)); +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 @@ -71,35 +71,69 @@ static PTR *find_empty_slot_for_expand PARAMS ((htab_t, hashval_t)); 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. */ @@ -124,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 = (PTR *) xcalloc (size, sizeof (PTR)); + 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. */ @@ -160,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. */ @@ -194,21 +328,27 @@ find_empty_slot_for_expand (htab, hash) 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 (;;) { - PTR *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; } } @@ -216,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; { 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 = (PTR *) xcalloc (htab->size, sizeof (PTR *)); + /* 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; @@ -251,7 +411,11 @@ 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 @@ -308,7 +472,9 @@ 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. */ PTR * htab_find_slot_with_hash (htab, element, hash, insert) @@ -321,49 +487,59 @@ htab_find_slot_with_hash (htab, element, hash, insert) 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 (;;) { - PTR 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 @@ -427,13 +603,16 @@ htab_clear_slot (htab, slot) argument. */ void -htab_traverse (htab, callback, info) +htab_traverse_noresize (htab, callback, info) htab_t htab; htab_trav callback; PTR info; { - PTR *slot = htab->entries; - PTR *limit = slot + htab->size; + PTR *slot; + PTR *limit; + + slot = htab->entries; + limit = slot + htab->size; do { @@ -446,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 @@ -476,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; +}