| /* A type-safe hash table template. |
| Copyright (C) 2012-2014 Free Software Foundation, Inc. |
| Contributed by Lawrence Crowl <crowl@google.com> |
| |
| This file is part of GCC. |
| |
| GCC 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 3, or (at your option) any later |
| version. |
| |
| GCC 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 GCC; see the file COPYING3. If not see |
| <http://www.gnu.org/licenses/>. */ |
| |
| |
| /* This file implements a typed hash table. |
| The implementation borrows from libiberty's htab_t in hashtab.h. |
| |
| |
| INTRODUCTION TO TYPES |
| |
| Users of the hash table generally need to be aware of three types. |
| |
| 1. The type being placed into the hash table. This type is called |
| the value type. |
| |
| 2. The type used to describe how to handle the value type within |
| the hash table. This descriptor type provides the hash table with |
| several things. |
| |
| - A typedef named 'value_type' to the value type (from above). |
| |
| - A static member function named 'hash' that takes a value_type |
| pointer and returns a hashval_t value. |
| |
| - A typedef named 'compare_type' that is used to test when an value |
| is found. This type is the comparison type. Usually, it will be the |
| same as value_type. If it is not the same type, you must generally |
| explicitly compute hash values and pass them to the hash table. |
| |
| - A static member function named 'equal' that takes a value_type |
| pointer and a compare_type pointer, and returns a bool. |
| |
| - A static function named 'remove' that takes an value_type pointer |
| and frees the memory allocated by it. This function is used when |
| individual elements of the table need to be disposed of (e.g., |
| when deleting a hash table, removing elements from the table, etc). |
| |
| 3. The type of the hash table itself. (More later.) |
| |
| In very special circumstances, users may need to know about a fourth type. |
| |
| 4. The template type used to describe how hash table memory |
| is allocated. This type is called the allocator type. It is |
| parameterized on the value type. It provides four functions. |
| |
| - A static member function named 'control_alloc'. This function |
| allocates the control data blocks for the table. |
| |
| - A static member function named 'control_free'. This function |
| frees the control data blocks for the table. |
| |
| - A static member function named 'data_alloc'. This function |
| allocates the data elements in the table. |
| |
| - A static member function named 'data_free'. This function |
| deallocates the data elements in the table. |
| |
| Hash table are instantiated with two type arguments. |
| |
| * The descriptor type, (2) above. |
| |
| * The allocator type, (4) above. In general, you will not need to |
| provide your own allocator type. By default, hash tables will use |
| the class template xcallocator, which uses malloc/free for allocation. |
| |
| |
| DEFINING A DESCRIPTOR TYPE |
| |
| The first task in using the hash table is to describe the element type. |
| We compose this into a few steps. |
| |
| 1. Decide on a removal policy for values stored in the table. |
| This header provides class templates for the two most common |
| policies. |
| |
| * typed_free_remove implements the static 'remove' member function |
| by calling free(). |
| |
| * typed_noop_remove implements the static 'remove' member function |
| by doing nothing. |
| |
| You can use these policies by simply deriving the descriptor type |
| from one of those class template, with the appropriate argument. |
| |
| Otherwise, you need to write the static 'remove' member function |
| in the descriptor class. |
| |
| 2. Choose a hash function. Write the static 'hash' member function. |
| |
| 3. Choose an equality testing function. In most cases, its two |
| arguments will be value_type pointers. If not, the first argument must |
| be a value_type pointer, and the second argument a compare_type pointer. |
| |
| |
| AN EXAMPLE DESCRIPTOR TYPE |
| |
| Suppose you want to put some_type into the hash table. You could define |
| the descriptor type as follows. |
| |
| struct some_type_hasher : typed_noop_remove <some_type> |
| // Deriving from typed_noop_remove means that we get a 'remove' that does |
| // nothing. This choice is good for raw values. |
| { |
| typedef some_type value_type; |
| typedef some_type compare_type; |
| static inline hashval_t hash (const value_type *); |
| static inline bool equal (const value_type *, const compare_type *); |
| }; |
| |
| inline hashval_t |
| some_type_hasher::hash (const value_type *e) |
| { ... compute and return a hash value for E ... } |
| |
| inline bool |
| some_type_hasher::equal (const value_type *p1, const compare_type *p2) |
| { ... compare P1 vs P2. Return true if they are the 'same' ... } |
| |
| |
| AN EXAMPLE HASH_TABLE DECLARATION |
| |
| To instantiate a hash table for some_type: |
| |
| hash_table <some_type_hasher> some_type_hash_table; |
| |
| There is no need to mention some_type directly, as the hash table will |
| obtain it using some_type_hasher::value_type. |
| |
| You can then used any of the functions in hash_table's public interface. |
| See hash_table for details. The interface is very similar to libiberty's |
| htab_t. |
| |
| |
| EASY DESCRIPTORS FOR POINTERS |
| |
| The class template pointer_hash provides everything you need to hash |
| pointers (as opposed to what they point to). So, to instantiate a hash |
| table over pointers to whatever_type, |
| |
| hash_table <pointer_hash <whatever_type>> whatever_type_hash_table; |
| |
| |
| HASH TABLE ITERATORS |
| |
| The hash table provides standard C++ iterators. For example, consider a |
| hash table of some_info. We wish to consume each element of the table: |
| |
| extern void consume (some_info *); |
| |
| We define a convenience typedef and the hash table: |
| |
| typedef hash_table <some_info_hasher> info_table_type; |
| info_table_type info_table; |
| |
| Then we write the loop in typical C++ style: |
| |
| for (info_table_type::iterator iter = info_table.begin (); |
| iter != info_table.end (); |
| ++iter) |
| if ((*iter).status == INFO_READY) |
| consume (&*iter); |
| |
| Or with common sub-expression elimination: |
| |
| for (info_table_type::iterator iter = info_table.begin (); |
| iter != info_table.end (); |
| ++iter) |
| { |
| some_info &elem = *iter; |
| if (elem.status == INFO_READY) |
| consume (&elem); |
| } |
| |
| One can also use a more typical GCC style: |
| |
| typedef some_info *some_info_p; |
| some_info *elem_ptr; |
| info_table_type::iterator iter; |
| FOR_EACH_HASH_TABLE_ELEMENT (info_table, elem_ptr, some_info_p, iter) |
| if (elem_ptr->status == INFO_READY) |
| consume (elem_ptr); |
| |
| */ |
| |
| |
| #ifndef TYPED_HASHTAB_H |
| #define TYPED_HASHTAB_H |
| |
| #include "hashtab.h" |
| |
| |
| /* The ordinary memory allocator. */ |
| /* FIXME (crowl): This allocator may be extracted for wider sharing later. */ |
| |
| template <typename Type> |
| struct xcallocator |
| { |
| static Type *control_alloc (size_t count); |
| static Type *data_alloc (size_t count); |
| static void control_free (Type *memory); |
| static void data_free (Type *memory); |
| }; |
| |
| |
| /* Allocate memory for COUNT control blocks. */ |
| |
| template <typename Type> |
| inline Type * |
| xcallocator <Type>::control_alloc (size_t count) |
| { |
| return static_cast <Type *> (xcalloc (count, sizeof (Type))); |
| } |
| |
| |
| /* Allocate memory for COUNT data blocks. */ |
| |
| template <typename Type> |
| inline Type * |
| xcallocator <Type>::data_alloc (size_t count) |
| { |
| return static_cast <Type *> (xcalloc (count, sizeof (Type))); |
| } |
| |
| |
| /* Free memory for control blocks. */ |
| |
| template <typename Type> |
| inline void |
| xcallocator <Type>::control_free (Type *memory) |
| { |
| return ::free (memory); |
| } |
| |
| |
| /* Free memory for data blocks. */ |
| |
| template <typename Type> |
| inline void |
| xcallocator <Type>::data_free (Type *memory) |
| { |
| return ::free (memory); |
| } |
| |
| |
| /* Helpful type for removing with free. */ |
| |
| template <typename Type> |
| struct typed_free_remove |
| { |
| static inline void remove (Type *p); |
| }; |
| |
| |
| /* Remove with free. */ |
| |
| template <typename Type> |
| inline void |
| typed_free_remove <Type>::remove (Type *p) |
| { |
| free (p); |
| } |
| |
| |
| /* Helpful type for a no-op remove. */ |
| |
| template <typename Type> |
| struct typed_noop_remove |
| { |
| static inline void remove (Type *p); |
| }; |
| |
| |
| /* Remove doing nothing. */ |
| |
| template <typename Type> |
| inline void |
| typed_noop_remove <Type>::remove (Type *p ATTRIBUTE_UNUSED) |
| { |
| } |
| |
| |
| /* Pointer hash with a no-op remove method. */ |
| |
| template <typename Type> |
| struct pointer_hash : typed_noop_remove <Type> |
| { |
| typedef Type value_type; |
| typedef Type compare_type; |
| |
| static inline hashval_t |
| hash (const value_type *); |
| |
| static inline int |
| equal (const value_type *existing, const compare_type *candidate); |
| }; |
| |
| template <typename Type> |
| inline hashval_t |
| pointer_hash <Type>::hash (const value_type *candidate) |
| { |
| /* This is a really poor hash function, but it is what the current code uses, |
| so I am reusing it to avoid an additional axis in testing. */ |
| return (hashval_t) ((intptr_t)candidate >> 3); |
| } |
| |
| template <typename Type> |
| inline int |
| pointer_hash <Type>::equal (const value_type *existing, |
| const compare_type *candidate) |
| { |
| return existing == candidate; |
| } |
| |
| |
| /* Table of primes and their inversion information. */ |
| |
| struct prime_ent |
| { |
| hashval_t prime; |
| hashval_t inv; |
| hashval_t inv_m2; /* inverse of prime-2 */ |
| hashval_t shift; |
| }; |
| |
| extern struct prime_ent const prime_tab[]; |
| |
| |
| /* Functions for computing hash table indexes. */ |
| |
| extern unsigned int hash_table_higher_prime_index (unsigned long n); |
| extern hashval_t hash_table_mod1 (hashval_t hash, unsigned int index); |
| extern hashval_t hash_table_mod2 (hashval_t hash, unsigned int index); |
| |
| |
| /* Internal implementation type. */ |
| |
| template <typename T> |
| struct hash_table_control |
| { |
| /* Table itself. */ |
| T **entries; |
| |
| /* Current size (in entries) of the hash table. */ |
| size_t size; |
| |
| /* Current number of elements including also deleted elements. */ |
| size_t n_elements; |
| |
| /* Current number of deleted elements in the table. */ |
| size_t n_deleted; |
| |
| /* The following member is used for debugging. Its value is number |
| of all calls of `htab_find_slot' for the hash table. */ |
| unsigned int searches; |
| |
| /* The following member is used for debugging. Its value is number |
| of collisions fixed for time of work with the hash table. */ |
| unsigned int collisions; |
| |
| /* Current size (in entries) of the hash table, as an index into the |
| table of primes. */ |
| unsigned int size_prime_index; |
| }; |
| |
| |
| /* User-facing hash table type. |
| |
| The table stores elements of type Descriptor::value_type. |
| |
| It hashes values with the hash member function. |
| The table currently works with relatively weak hash functions. |
| Use typed_pointer_hash <Value> when hashing pointers instead of objects. |
| |
| It compares elements with the equal member function. |
| Two elements with the same hash may not be equal. |
| Use typed_pointer_equal <Value> when hashing pointers instead of objects. |
| |
| It removes elements with the remove member function. |
| This feature is useful for freeing memory. |
| Derive from typed_null_remove <Value> when not freeing objects. |
| Derive from typed_free_remove <Value> when doing a simple object free. |
| |
| Specify the template Allocator to allocate and free memory. |
| The default is xcallocator. |
| |
| */ |
| |
| template <typename Descriptor, |
| template <typename Type> class Allocator = xcallocator> |
| class hash_table |
| { |
| public: |
| typedef typename Descriptor::value_type value_type; |
| typedef typename Descriptor::compare_type compare_type; |
| |
| class iterator |
| { |
| public: |
| inline iterator (); |
| inline iterator (value_type **, value_type **); |
| inline value_type &operator * (); |
| void slide (); |
| inline iterator &operator ++ (); |
| inline bool operator != (const iterator &) const; |
| private: |
| value_type **m_slot; |
| value_type **m_limit; |
| }; |
| |
| private: |
| hash_table_control <value_type> *htab; |
| |
| value_type **find_empty_slot_for_expand (hashval_t hash); |
| void expand (); |
| |
| public: |
| hash_table (); |
| void create (size_t initial_slots); |
| bool is_created (); |
| void dispose (); |
| value_type *find (const value_type *value); |
| value_type *find_with_hash (const compare_type *comparable, hashval_t hash); |
| value_type **find_slot (const value_type *value, enum insert_option insert); |
| value_type **find_slot_with_hash (const compare_type *comparable, |
| hashval_t hash, enum insert_option insert); |
| void empty (); |
| void clear_slot (value_type **slot); |
| void remove_elt (const value_type *value); |
| void remove_elt_with_hash (const compare_type *comparable, hashval_t hash); |
| size_t size (); |
| size_t elements (); |
| size_t elements_with_deleted (); |
| double collisions (); |
| |
| template <typename Argument, |
| int (*Callback) (value_type **slot, Argument argument)> |
| void traverse_noresize (Argument argument); |
| |
| template <typename Argument, |
| int (*Callback) (value_type **slot, Argument argument)> |
| void traverse (Argument argument); |
| |
| iterator begin (); |
| iterator end (); |
| }; |
| |
| |
| /* Construct the hash table. The only useful operation next is create. */ |
| |
| template <typename Descriptor, |
| template <typename Type> class Allocator> |
| inline |
| hash_table <Descriptor, Allocator>::hash_table () |
| : htab (NULL) |
| { |
| } |
| |
| |
| /* See if the table has been created, as opposed to constructed. */ |
| |
| template <typename Descriptor, |
| template <typename Type> class Allocator> |
| inline bool |
| hash_table <Descriptor, Allocator>::is_created () |
| { |
| return htab != NULL; |
| } |
| |
| |
| /* Like find_with_hash, but compute the hash value from the element. */ |
| |
| template <typename Descriptor, |
| template <typename Type> class Allocator> |
| inline typename Descriptor::value_type * |
| hash_table <Descriptor, Allocator>::find (const value_type *value) |
| { |
| return find_with_hash (value, Descriptor::hash (value)); |
| } |
| |
| |
| /* Like find_slot_with_hash, but compute the hash value from the element. */ |
| |
| template <typename Descriptor, |
| template <typename Type> class Allocator> |
| inline typename Descriptor::value_type ** |
| hash_table <Descriptor, Allocator> |
| ::find_slot (const value_type *value, enum insert_option insert) |
| { |
| return find_slot_with_hash (value, Descriptor::hash (value), insert); |
| } |
| |
| |
| /* Like remove_elt_with_hash, but compute the hash value from the element. */ |
| |
| template <typename Descriptor, |
| template <typename Type> class Allocator> |
| inline void |
| hash_table <Descriptor, Allocator>::remove_elt (const value_type *value) |
| { |
| remove_elt_with_hash (value, Descriptor::hash (value)); |
| } |
| |
| |
| /* Return the current size of this hash table. */ |
| |
| template <typename Descriptor, |
| template <typename Type> class Allocator> |
| inline size_t |
| hash_table <Descriptor, Allocator>::size () |
| { |
| return htab->size; |
| } |
| |
| |
| /* Return the current number of elements in this hash table. */ |
| |
| template <typename Descriptor, |
| template <typename Type> class Allocator> |
| inline size_t |
| hash_table <Descriptor, Allocator>::elements () |
| { |
| return htab->n_elements - htab->n_deleted; |
| } |
| |
| |
| /* Return the current number of elements in this hash table. */ |
| |
| template <typename Descriptor, |
| template <typename Type> class Allocator> |
| inline size_t |
| hash_table <Descriptor, Allocator>::elements_with_deleted () |
| { |
| return htab->n_elements; |
| } |
| |
| |
| /* Return the fraction of fixed collisions during all work with given |
| hash table. */ |
| |
| template <typename Descriptor, |
| template <typename Type> class Allocator> |
| inline double |
| hash_table <Descriptor, Allocator>::collisions () |
| { |
| if (htab->searches == 0) |
| return 0.0; |
| |
| return static_cast <double> (htab->collisions) / htab->searches; |
| } |
| |
| |
| /* Create a hash table with at least the given number of INITIAL_SLOTS. */ |
| |
| template <typename Descriptor, |
| template <typename Type> class Allocator> |
| void |
| hash_table <Descriptor, Allocator>::create (size_t size) |
| { |
| unsigned int size_prime_index; |
| |
| size_prime_index = hash_table_higher_prime_index (size); |
| size = prime_tab[size_prime_index].prime; |
| |
| htab = Allocator <hash_table_control <value_type> > ::control_alloc (1); |
| gcc_assert (htab != NULL); |
| htab->entries = Allocator <value_type*> ::data_alloc (size); |
| gcc_assert (htab->entries != NULL); |
| htab->size = size; |
| htab->size_prime_index = size_prime_index; |
| } |
| |
| |
| /* Dispose of a hash table. Free all memory and return this hash table to |
| the non-created state. Naturally the hash table must already exist. */ |
| |
| template <typename Descriptor, |
| template <typename Type> class Allocator> |
| void |
| hash_table <Descriptor, Allocator>::dispose () |
| { |
| size_t size = htab->size; |
| value_type **entries = htab->entries; |
| |
| for (int i = size - 1; i >= 0; i--) |
| if (entries[i] != HTAB_EMPTY_ENTRY && entries[i] != HTAB_DELETED_ENTRY) |
| Descriptor::remove (entries[i]); |
| |
| Allocator <value_type *> ::data_free (entries); |
| Allocator <hash_table_control <value_type> > ::control_free (htab); |
| htab = NULL; |
| } |
| |
| |
| /* Similar to find_slot, but without several unwanted side effects: |
| - Does not call equal when it finds an existing entry. |
| - Does not change the count of elements/searches/collisions in the |
| hash table. |
| This function also assumes there are no deleted entries in the table. |
| HASH is the hash value for the element to be inserted. */ |
| |
| template <typename Descriptor, |
| template <typename Type> class Allocator> |
| typename Descriptor::value_type ** |
| hash_table <Descriptor, Allocator>::find_empty_slot_for_expand (hashval_t hash) |
| { |
| hashval_t index = hash_table_mod1 (hash, htab->size_prime_index); |
| size_t size = htab->size; |
| value_type **slot = htab->entries + index; |
| hashval_t hash2; |
| |
| if (*slot == HTAB_EMPTY_ENTRY) |
| return slot; |
| else if (*slot == HTAB_DELETED_ENTRY) |
| abort (); |
| |
| hash2 = hash_table_mod2 (hash, htab->size_prime_index); |
| for (;;) |
| { |
| index += hash2; |
| if (index >= size) |
| index -= size; |
| |
| slot = htab->entries + index; |
| if (*slot == HTAB_EMPTY_ENTRY) |
| return slot; |
| else if (*slot == HTAB_DELETED_ENTRY) |
| abort (); |
| } |
| } |
| |
| |
| /* The following function changes size of memory allocated for the |
| 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. If memory allocation fails, this function |
| will abort. */ |
| |
| template <typename Descriptor, |
| template <typename Type> class Allocator> |
| void |
| hash_table <Descriptor, Allocator>::expand () |
| { |
| value_type **oentries; |
| value_type **olimit; |
| value_type **p; |
| value_type **nentries; |
| size_t nsize, osize, elts; |
| unsigned int oindex, nindex; |
| |
| oentries = htab->entries; |
| oindex = htab->size_prime_index; |
| osize = htab->size; |
| olimit = oentries + osize; |
| elts = elements (); |
| |
| /* Resize only when table after removal of unused elements is either |
| too full or too empty. */ |
| if (elts * 2 > osize || (elts * 8 < osize && osize > 32)) |
| { |
| nindex = hash_table_higher_prime_index (elts * 2); |
| nsize = prime_tab[nindex].prime; |
| } |
| else |
| { |
| nindex = oindex; |
| nsize = osize; |
| } |
| |
| nentries = Allocator <value_type *> ::data_alloc (nsize); |
| gcc_assert (nentries != NULL); |
| htab->entries = nentries; |
| htab->size = nsize; |
| htab->size_prime_index = nindex; |
| htab->n_elements -= htab->n_deleted; |
| htab->n_deleted = 0; |
| |
| p = oentries; |
| do |
| { |
| value_type *x = *p; |
| |
| if (x != HTAB_EMPTY_ENTRY && x != HTAB_DELETED_ENTRY) |
| { |
| value_type **q = find_empty_slot_for_expand (Descriptor::hash (x)); |
| |
| *q = x; |
| } |
| |
| p++; |
| } |
| while (p < olimit); |
| |
| Allocator <value_type *> ::data_free (oentries); |
| } |
| |
| |
| /* This function searches for a hash table entry equal to the given |
| COMPARABLE element starting with the given HASH value. It cannot |
| be used to insert or delete an element. */ |
| |
| template <typename Descriptor, |
| template <typename Type> class Allocator> |
| typename Descriptor::value_type * |
| hash_table <Descriptor, Allocator> |
| ::find_with_hash (const compare_type *comparable, hashval_t hash) |
| { |
| hashval_t index, hash2; |
| size_t size; |
| value_type *entry; |
| |
| htab->searches++; |
| size = htab->size; |
| index = hash_table_mod1 (hash, htab->size_prime_index); |
| |
| entry = htab->entries[index]; |
| if (entry == HTAB_EMPTY_ENTRY |
| || (entry != HTAB_DELETED_ENTRY && Descriptor::equal (entry, comparable))) |
| return entry; |
| |
| hash2 = hash_table_mod2 (hash, htab->size_prime_index); |
| for (;;) |
| { |
| htab->collisions++; |
| index += hash2; |
| if (index >= size) |
| index -= size; |
| |
| entry = htab->entries[index]; |
| if (entry == HTAB_EMPTY_ENTRY |
| || (entry != HTAB_DELETED_ENTRY |
| && Descriptor::equal (entry, comparable))) |
| return entry; |
| } |
| } |
| |
| |
| /* This function searches for a hash table slot containing an entry |
| equal to the given COMPARABLE element and starting with the given |
| HASH. To delete an entry, call this with insert=NO_INSERT, then |
| call clear_slot on the slot returned (possibly after doing some |
| checks). To insert an entry, call this with insert=INSERT, then |
| write the value you want into the returned slot. When inserting an |
| entry, NULL may be returned if memory allocation fails. */ |
| |
| template <typename Descriptor, |
| template <typename Type> class Allocator> |
| typename Descriptor::value_type ** |
| hash_table <Descriptor, Allocator> |
| ::find_slot_with_hash (const compare_type *comparable, hashval_t hash, |
| enum insert_option insert) |
| { |
| value_type **first_deleted_slot; |
| hashval_t index, hash2; |
| size_t size; |
| value_type *entry; |
| |
| size = htab->size; |
| if (insert == INSERT && size * 3 <= htab->n_elements * 4) |
| { |
| expand (); |
| size = htab->size; |
| } |
| |
| index = hash_table_mod1 (hash, htab->size_prime_index); |
| |
| htab->searches++; |
| first_deleted_slot = NULL; |
| |
| entry = htab->entries[index]; |
| if (entry == HTAB_EMPTY_ENTRY) |
| goto empty_entry; |
| else if (entry == HTAB_DELETED_ENTRY) |
| first_deleted_slot = &htab->entries[index]; |
| else if (Descriptor::equal (entry, comparable)) |
| return &htab->entries[index]; |
| |
| hash2 = hash_table_mod2 (hash, htab->size_prime_index); |
| for (;;) |
| { |
| htab->collisions++; |
| index += hash2; |
| if (index >= size) |
| index -= size; |
| |
| entry = htab->entries[index]; |
| if (entry == HTAB_EMPTY_ENTRY) |
| goto empty_entry; |
| else if (entry == HTAB_DELETED_ENTRY) |
| { |
| if (!first_deleted_slot) |
| first_deleted_slot = &htab->entries[index]; |
| } |
| else if (Descriptor::equal (entry, comparable)) |
| return &htab->entries[index]; |
| } |
| |
| empty_entry: |
| if (insert == NO_INSERT) |
| return NULL; |
| |
| if (first_deleted_slot) |
| { |
| htab->n_deleted--; |
| *first_deleted_slot = static_cast <value_type *> (HTAB_EMPTY_ENTRY); |
| return first_deleted_slot; |
| } |
| |
| htab->n_elements++; |
| return &htab->entries[index]; |
| } |
| |
| |
| /* This function clears all entries in the given hash table. */ |
| |
| template <typename Descriptor, |
| template <typename Type> class Allocator> |
| void |
| hash_table <Descriptor, Allocator>::empty () |
| { |
| size_t size = htab->size; |
| value_type **entries = htab->entries; |
| int i; |
| |
| for (i = size - 1; i >= 0; i--) |
| if (entries[i] != HTAB_EMPTY_ENTRY && entries[i] != HTAB_DELETED_ENTRY) |
| Descriptor::remove (entries[i]); |
| |
| /* Instead of clearing megabyte, downsize the table. */ |
| if (size > 1024*1024 / sizeof (PTR)) |
| { |
| int nindex = hash_table_higher_prime_index (1024 / sizeof (PTR)); |
| int nsize = prime_tab[nindex].prime; |
| |
| Allocator <value_type *> ::data_free (htab->entries); |
| htab->entries = Allocator <value_type *> ::data_alloc (nsize); |
| htab->size = nsize; |
| htab->size_prime_index = nindex; |
| } |
| else |
| memset (entries, 0, size * sizeof (value_type *)); |
| htab->n_deleted = 0; |
| htab->n_elements = 0; |
| } |
| |
| |
| /* This function clears a specified SLOT in a hash table. It is |
| useful when you've already done the lookup and don't want to do it |
| again. */ |
| |
| template <typename Descriptor, |
| template <typename Type> class Allocator> |
| void |
| hash_table <Descriptor, Allocator>::clear_slot (value_type **slot) |
| { |
| if (slot < htab->entries || slot >= htab->entries + htab->size |
| || *slot == HTAB_EMPTY_ENTRY || *slot == HTAB_DELETED_ENTRY) |
| abort (); |
| |
| Descriptor::remove (*slot); |
| |
| *slot = static_cast <value_type *> (HTAB_DELETED_ENTRY); |
| htab->n_deleted++; |
| } |
| |
| |
| /* This function deletes an element with the given COMPARABLE value |
| from hash table starting with the given HASH. If there is no |
| matching element in the hash table, this function does nothing. */ |
| |
| template <typename Descriptor, |
| template <typename Type> class Allocator> |
| void |
| hash_table <Descriptor, Allocator> |
| ::remove_elt_with_hash (const compare_type *comparable, hashval_t hash) |
| { |
| value_type **slot; |
| |
| slot = find_slot_with_hash (comparable, hash, NO_INSERT); |
| if (*slot == HTAB_EMPTY_ENTRY) |
| return; |
| |
| Descriptor::remove (*slot); |
| |
| *slot = static_cast <value_type *> (HTAB_DELETED_ENTRY); |
| htab->n_deleted++; |
| } |
| |
| |
| /* This function scans over the entire hash table calling CALLBACK for |
| each live entry. If CALLBACK returns false, the iteration stops. |
| ARGUMENT is passed as CALLBACK's second argument. */ |
| |
| template <typename Descriptor, |
| template <typename Type> class Allocator> |
| template <typename Argument, |
| int (*Callback) (typename Descriptor::value_type **slot, Argument argument)> |
| void |
| hash_table <Descriptor, Allocator>::traverse_noresize (Argument argument) |
| { |
| value_type **slot; |
| value_type **limit; |
| |
| slot = htab->entries; |
| limit = slot + htab->size; |
| |
| do |
| { |
| value_type *x = *slot; |
| |
| if (x != HTAB_EMPTY_ENTRY && x != HTAB_DELETED_ENTRY) |
| if (! Callback (slot, argument)) |
| break; |
| } |
| while (++slot < limit); |
| } |
| |
| |
| /* Like traverse_noresize, but does resize the table when it is too empty |
| to improve effectivity of subsequent calls. */ |
| |
| template <typename Descriptor, |
| template <typename Type> class Allocator> |
| template <typename Argument, |
| int (*Callback) (typename Descriptor::value_type **slot, |
| Argument argument)> |
| void |
| hash_table <Descriptor, Allocator>::traverse (Argument argument) |
| { |
| size_t size = htab->size; |
| if (elements () * 8 < size && size > 32) |
| expand (); |
| |
| traverse_noresize <Argument, Callback> (argument); |
| } |
| |
| |
| /* Iterator definitions. */ |
| |
| /* The default constructor produces the end value. */ |
| |
| template <typename Descriptor, |
| template <typename Type> class Allocator> |
| inline |
| hash_table <Descriptor, Allocator>::iterator::iterator () |
| : m_slot (NULL), m_limit (NULL) |
| { |
| } |
| |
| /* The parameterized constructor produces the begin value. */ |
| |
| template <typename Descriptor, |
| template <typename Type> class Allocator> |
| inline |
| hash_table <Descriptor, Allocator>::iterator::iterator |
| (value_type **slot, value_type **limit) |
| : m_slot (slot), m_limit (limit) |
| { |
| } |
| |
| /* Obtain the element. */ |
| |
| template <typename Descriptor, |
| template <typename Type> class Allocator> |
| inline typename hash_table <Descriptor, Allocator>::value_type & |
| hash_table <Descriptor, Allocator>::iterator::operator * () |
| { |
| return **m_slot; |
| } |
| |
| /* Slide down the iterator slots until an active entry is found. */ |
| |
| template <typename Descriptor, |
| template <typename Type> class Allocator> |
| void |
| hash_table <Descriptor, Allocator>::iterator::slide () |
| { |
| for ( ; m_slot < m_limit; ++m_slot ) |
| { |
| value_type *x = *m_slot; |
| if (x != HTAB_EMPTY_ENTRY && x != HTAB_DELETED_ENTRY) |
| return; |
| } |
| m_slot = NULL; |
| m_limit = NULL; |
| } |
| |
| /* Bump the iterator. */ |
| |
| template <typename Descriptor, |
| template <typename Type> class Allocator> |
| inline typename hash_table <Descriptor, Allocator>::iterator & |
| hash_table <Descriptor, Allocator>::iterator::operator ++ () |
| { |
| ++m_slot; |
| slide (); |
| return *this; |
| } |
| |
| /* Compare iterators. */ |
| |
| template <typename Descriptor, |
| template <typename Type> class Allocator> |
| inline bool |
| hash_table <Descriptor, Allocator>::iterator:: |
| operator != (const iterator &other) const |
| { |
| return m_slot != other.m_slot || m_limit != other.m_limit; |
| } |
| |
| /* Hash table iterator producers. */ |
| |
| /* The beginning of a hash table iteration. */ |
| |
| template <typename Descriptor, |
| template <typename Type> class Allocator> |
| inline typename hash_table <Descriptor, Allocator>::iterator |
| hash_table <Descriptor, Allocator>::begin () |
| { |
| iterator hti (htab->entries, htab->entries + htab->size); |
| hti.slide (); |
| return hti; |
| } |
| |
| /* The end of a hash table iteration. */ |
| |
| template <typename Descriptor, |
| template <typename Type> class Allocator> |
| inline typename hash_table <Descriptor, Allocator>::iterator |
| hash_table <Descriptor, Allocator>::end () |
| { |
| return iterator (); |
| } |
| |
| /* Iterate through the elements of hash_table HTAB, |
| using hash_table <....>::iterator ITER, |
| storing each element in RESULT, which is of type TYPE. */ |
| |
| #define FOR_EACH_HASH_TABLE_ELEMENT(HTAB, RESULT, TYPE, ITER) \ |
| for ((ITER) = (HTAB).begin (); \ |
| (ITER) != (HTAB).end () ? (RESULT = &*(ITER) , true) : false; \ |
| ++(ITER)) |
| |
| #endif /* TYPED_HASHTAB_H */ |