template <typename Type>
struct pointer_hash : typed_noop_remove <Type>
{
- typedef Type value_type;
- typedef Type compare_type;
+ typedef Type *value_type;
+ typedef Type *compare_type;
+ typedef int store_values_directly;
- static inline hashval_t
- hash (const value_type *);
+ static inline hashval_t hash (const value_type &);
- static inline int
- equal (const value_type *existing, const compare_type *candidate);
+ static inline bool equal (const value_type &existing, const compare_type &candidate);
};
template <typename Type>
inline hashval_t
-pointer_hash <Type>::hash (const value_type *candidate)
+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. */
}
template <typename Type>
-inline int
-pointer_hash <Type>::equal (const value_type *existing,
- const compare_type *candidate)
+inline bool
+pointer_hash <Type>::equal (const value_type &existing,
+ const compare_type &candidate)
{
return existing == candidate;
}
extern hashval_t hash_table_mod1 (hashval_t hash, unsigned int index);
extern hashval_t hash_table_mod2 (hashval_t hash, unsigned int index);
+/* The below is some template meta programming to decide if we should use the
+ hash table partial specialization that directly stores value_type instead of
+ pointers to value_type. If the Descriptor type defines the type
+ Descriptor::store_values_directly then values are stored directly otherwise
+ pointers to them are stored. */
+template<typename T> struct notype { typedef void type; };
+
+template<typename T, typename = void>
+struct storage_tester
+{
+ static const bool value = false;
+};
+
+template<typename T>
+struct storage_tester<T, typename notype<typename
+ T::store_values_directly>::type>
+{
+ static const bool value = true;
+};
+
+ template<typename Traits>
+ struct has_is_deleted
+{
+ template<typename U, bool (*)(U &)> struct helper {};
+ template<typename U> static char test (helper<U, U::is_deleted> *);
+ template<typename U> static int test (...);
+ static const bool value = sizeof (test<Traits> (0)) == sizeof (char);
+};
+
+template<typename Type, typename Traits, bool = has_is_deleted<Traits>::value>
+struct is_deleted_helper
+{
+ static inline bool
+ call (Type &v)
+ {
+ return Traits::is_deleted (v);
+ }
+};
+
+template<typename Type, typename Traits>
+struct is_deleted_helper<Type *, Traits, false>
+{
+ static inline bool
+ call (Type *v)
+ {
+ return v == HTAB_DELETED_ENTRY;
+ }
+};
+
+ template<typename Traits>
+ struct has_is_empty
+{
+ template<typename U, bool (*)(U &)> struct helper {};
+ template<typename U> static char test (helper<U, U::is_empty> *);
+ template<typename U> static int test (...);
+ static const bool value = sizeof (test<Traits> (0)) == sizeof (char);
+};
+
+template<typename Type, typename Traits, bool = has_is_deleted<Traits>::value>
+struct is_empty_helper
+{
+ static inline bool
+ call (Type &v)
+ {
+ return Traits::is_empty (v);
+ }
+};
+
+template<typename Type, typename Traits>
+struct is_empty_helper<Type *, Traits, false>
+{
+ static inline bool
+ call (Type *v)
+ {
+ return v == HTAB_EMPTY_ENTRY;
+ }
+};
+
+ template<typename Traits>
+ struct has_mark_deleted
+{
+ template<typename U, void (*)(U &)> struct helper {};
+ template<typename U> static char test (helper<U, U::mark_deleted> *);
+ template<typename U> static int test (...);
+ static const bool value = sizeof (test<Traits> (0)) == sizeof (char);
+};
+
+template<typename Type, typename Traits, bool = has_is_deleted<Traits>::value>
+struct mark_deleted_helper
+{
+ static inline void
+ call (Type &v)
+ {
+ Traits::mark_deleted (v);
+ }
+};
+
+template<typename Type, typename Traits>
+struct mark_deleted_helper<Type *, Traits, false>
+{
+ static inline void
+ call (Type *&v)
+ {
+ v = static_cast<Type *> (HTAB_DELETED_ENTRY);
+ }
+};
+
+ template<typename Traits>
+ struct has_mark_empty
+{
+ template<typename U, void (*)(U &)> struct helper {};
+ template<typename U> static char test (helper<U, U::mark_empty> *);
+ template<typename U> static int test (...);
+ static const bool value = sizeof (test<Traits> (0)) == sizeof (char);
+};
+
+template<typename Type, typename Traits, bool = has_is_deleted<Traits>::value>
+struct mark_empty_helper
+{
+ static inline void
+ call (Type &v)
+ {
+ Traits::mark_empty (v);
+ }
+};
+
+template<typename Type, typename Traits>
+struct mark_empty_helper<Type *, Traits, false>
+{
+ static inline void
+ call (Type *&v)
+ {
+ v = static_cast<Type *> (HTAB_EMPTY_ENTRY);
+ }
+};
/* User-facing hash table type.
- The table stores elements of type Descriptor::value_type.
+ The table stores elements of type Descriptor::value_type, or pointers to
+ objects of type value_type if the descriptor does not define the type
+ store_values_directly.
It hashes values with the hash member function.
The table currently works with relatively weak hash functions.
Specify the template Allocator to allocate and free memory.
The default is xcallocator.
+ Storage is an implementation detail and should not be used outside the
+ hash table code.
+
*/
template <typename Descriptor,
- template<typename Type> class Allocator= xcallocator>
+ template<typename Type> class Allocator= xcallocator,
+ bool Storage = storage_tester<Descriptor>::value>
class hash_table
+{
+};
+
+template <typename Descriptor,
+ template<typename Type> class Allocator>
+class hash_table<Descriptor, Allocator, false>
{
typedef typename Descriptor::value_type value_type;
typedef typename Descriptor::compare_type compare_type;
iterator (value_type **slot, value_type **limit) :
m_slot (slot), m_limit (limit) {}
- inline value_type &operator * () { return **m_slot; }
+ inline value_type *operator * () { return *m_slot; }
void slide ();
inline iterator &operator ++ ();
bool operator != (const iterator &other) const
};
template<typename Descriptor, template<typename Type> class Allocator>
-hash_table<Descriptor, Allocator>::hash_table (size_t size) :
+hash_table<Descriptor, Allocator, false>::hash_table (size_t size) :
m_n_elements (0), m_n_deleted (0), m_searches (0), m_collisions (0)
{
unsigned int size_prime_index;
}
template<typename Descriptor, template<typename Type> class Allocator>
-hash_table<Descriptor, Allocator>::~hash_table ()
+hash_table<Descriptor, Allocator, false>::~hash_table ()
{
for (size_t i = m_size - 1; i < m_size; i--)
if (m_entries[i] != HTAB_EMPTY_ENTRY && m_entries[i] != HTAB_DELETED_ENTRY)
template<typename Descriptor, template<typename Type> class Allocator>
typename Descriptor::value_type **
-hash_table<Descriptor, Allocator>::find_empty_slot_for_expand (hashval_t hash)
+hash_table<Descriptor, Allocator, false>
+::find_empty_slot_for_expand (hashval_t hash)
{
hashval_t index = hash_table_mod1 (hash, m_size_prime_index);
size_t size = m_size;
template<typename Descriptor, template<typename Type> class Allocator>
void
-hash_table<Descriptor, Allocator>::expand ()
+hash_table<Descriptor, Allocator, false>::expand ()
{
value_type **oentries = m_entries;
unsigned int oindex = m_size_prime_index;
template<typename Descriptor, template<typename Type> class Allocator>
void
-hash_table<Descriptor, Allocator>::empty ()
+hash_table<Descriptor, Allocator, false>::empty ()
{
size_t size = m_size;
value_type **entries = m_entries;
template<typename Descriptor, template<typename Type> class Allocator>
void
-hash_table<Descriptor, Allocator>::clear_slot (value_type **slot)
+hash_table<Descriptor, Allocator, false>::clear_slot (value_type **slot)
{
if (slot < m_entries || slot >= m_entries + size ()
|| *slot == HTAB_EMPTY_ENTRY || *slot == HTAB_DELETED_ENTRY)
template<typename Descriptor, template<typename Type> class Allocator>
typename Descriptor::value_type *
-hash_table<Descriptor, Allocator>
+hash_table<Descriptor, Allocator, false>
::find_with_hash (const compare_type *comparable, hashval_t hash)
{
m_searches++;
template<typename Descriptor, template<typename Type> class Allocator>
typename Descriptor::value_type **
-hash_table<Descriptor, Allocator>
+hash_table<Descriptor, Allocator, false>
::find_slot_with_hash (const compare_type *comparable, hashval_t hash,
enum insert_option insert)
{
template<typename Descriptor, template<typename Type> class Allocator>
void
-hash_table<Descriptor, Allocator>
+hash_table<Descriptor, Allocator, false>
::remove_elt_with_hash (const compare_type *comparable, hashval_t hash)
{
value_type **slot = find_slot_with_hash (comparable, hash, NO_INSERT);
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 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)
+hash_table<Descriptor, Allocator, false>::traverse_noresize (Argument argument)
{
value_type **slot = m_entries;
value_type **limit = slot + size ();
int (*Callback) (typename Descriptor::value_type **slot,
Argument argument)>
void
-hash_table<Descriptor, Allocator>::traverse (Argument argument)
+hash_table<Descriptor, Allocator, false>::traverse (Argument argument)
{
size_t size = m_size;
if (elements () * 8 < size && size > 32)
template<typename Descriptor, template<typename Type> class Allocator>
void
-hash_table<Descriptor, Allocator>::iterator::slide ()
+hash_table<Descriptor, Allocator, false>::iterator::slide ()
{
for ( ; m_slot < m_limit; ++m_slot )
{
/* Bump the iterator. */
template<typename Descriptor, template<typename Type> class Allocator>
-inline typename hash_table<Descriptor, Allocator>::iterator &
-hash_table<Descriptor, Allocator>::iterator::operator ++ ()
+inline typename hash_table<Descriptor, Allocator, false>::iterator &
+hash_table<Descriptor, Allocator, false>::iterator::operator ++ ()
+{
+ ++m_slot;
+ slide ();
+ return *this;
+}
+
+/* A partial specialization used when values should be stored directly. */
+
+template <typename Descriptor,
+ template<typename Type> class Allocator>
+class hash_table<Descriptor, Allocator, true>
+{
+ typedef typename Descriptor::value_type value_type;
+ typedef typename Descriptor::compare_type compare_type;
+
+public:
+ hash_table (size_t);
+ ~hash_table ();
+
+ /* Current size (in entries) of the hash table. */
+ size_t size () const { return m_size; }
+
+ /* Return the current number of elements in this hash table. */
+ size_t elements () const { return m_n_elements - m_n_deleted; }
+
+ /* Return the current number of elements in this hash table. */
+ size_t elements_with_deleted () const { return m_n_elements; }
+
+ /* This function clears all entries in the given hash table. */
+ void empty ();
+
+ /* 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. */
+
+ void clear_slot (value_type *);
+
+ /* 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. */
+ value_type &find_with_hash (const compare_type &, hashval_t);
+
+/* Like find_slot_with_hash, but compute the hash value from the element. */
+ value_type &find (const value_type &value)
+ {
+ return find_with_hash (value, Descriptor::hash (value));
+ }
+
+ value_type *find_slot (const value_type &value, insert_option insert)
+ {
+ return find_slot_with_hash (value, Descriptor::hash (value), insert);
+ }
+
+ /* 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. */
+ value_type *find_slot_with_hash (const compare_type &comparable,
+ hashval_t hash, enum insert_option insert);
+
+ /* 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. */
+ void remove_elt_with_hash (const compare_type &, hashval_t);
+
+/* Like remove_elt_with_hash, but compute the hash value from the element. */
+ void remove_elt (const value_type &value)
+ {
+ remove_elt_with_hash (value, Descriptor::hash (value));
+ }
+
+ /* 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 Argument,
+ int (*Callback) (value_type *slot, Argument argument)>
+ void traverse_noresize (Argument argument);
+
+ /* Like traverse_noresize, but does resize the table when it is too empty
+ to improve effectivity of subsequent calls. */
+ template <typename Argument,
+ int (*Callback) (value_type *slot, Argument argument)>
+ void traverse (Argument argument);
+
+ class iterator
+ {
+ public:
+ iterator () : m_slot (NULL), m_limit (NULL) {}
+
+ iterator (value_type *slot, value_type *limit) :
+ m_slot (slot), m_limit (limit) {}
+
+ inline value_type &operator * () { return *m_slot; }
+ void slide ();
+ inline iterator &operator ++ ();
+ bool operator != (const iterator &other) const
+ {
+ return m_slot != other.m_slot || m_limit != other.m_limit;
+ }
+
+ private:
+ value_type *m_slot;
+ value_type *m_limit;
+ };
+
+ iterator begin () const
+ {
+ iterator iter (m_entries, m_entries + m_size);
+ iter.slide ();
+ return iter;
+ }
+
+ iterator end () const { return iterator (); }
+
+ double collisions () const
+ {
+ return m_searches ? static_cast <double> (m_collisions) / m_searches : 0;
+ }
+
+private:
+
+ value_type *find_empty_slot_for_expand (hashval_t);
+ void expand ();
+ static bool is_deleted (value_type &v)
+ {
+ return is_deleted_helper<value_type, Descriptor>::call (v);
+ }
+ static bool is_empty (value_type &v)
+ {
+ return is_empty_helper<value_type, Descriptor>::call (v);
+ }
+
+ static void mark_deleted (value_type &v)
+ {
+ return mark_deleted_helper<value_type, Descriptor>::call (v);
+ }
+
+ static void mark_empty (value_type &v)
+ {
+ return mark_empty_helper<value_type, Descriptor>::call (v);
+ }
+
+ /* Table itself. */
+ typename Descriptor::value_type *m_entries;
+
+ size_t m_size;
+
+ /* Current number of elements including also deleted elements. */
+ size_t m_n_elements;
+
+ /* Current number of deleted elements in the table. */
+ size_t m_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 m_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 m_collisions;
+
+ /* Current size (in entries) of the hash table, as an index into the
+ table of primes. */
+ unsigned int m_size_prime_index;
+};
+
+template<typename Descriptor, template<typename Type> class Allocator>
+hash_table<Descriptor, Allocator, true>::hash_table (size_t size) :
+ m_n_elements (0), m_n_deleted (0), m_searches (0), m_collisions (0)
+{
+ unsigned int size_prime_index;
+
+ size_prime_index = hash_table_higher_prime_index (size);
+ size = prime_tab[size_prime_index].prime;
+
+ m_entries = Allocator <value_type> ::data_alloc (size);
+ gcc_assert (m_entries != NULL);
+ m_size = size;
+ m_size_prime_index = size_prime_index;
+}
+
+template<typename Descriptor, template<typename Type> class Allocator>
+hash_table<Descriptor, Allocator, true>::~hash_table ()
+{
+ for (size_t i = m_size - 1; i < m_size; i--)
+ if (!is_empty (m_entries[i]) && !is_deleted (m_entries[i]))
+ Descriptor::remove (m_entries[i]);
+
+ Allocator <value_type> ::data_free (m_entries);
+}
+
+/* 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, true>
+::find_empty_slot_for_expand (hashval_t hash)
+{
+ hashval_t index = hash_table_mod1 (hash, m_size_prime_index);
+ size_t size = m_size;
+ value_type *slot = m_entries + index;
+ hashval_t hash2;
+
+ if (is_empty (*slot))
+ return slot;
+ else if (is_deleted (*slot))
+ abort ();
+
+ hash2 = hash_table_mod2 (hash, m_size_prime_index);
+ for (;;)
+ {
+ index += hash2;
+ if (index >= size)
+ index -= size;
+
+ slot = m_entries + index;
+ if (is_empty (*slot))
+ return slot;
+ else if (is_deleted (*slot))
+ 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, true>::expand ()
+{
+ value_type *oentries = m_entries;
+ unsigned int oindex = m_size_prime_index;
+ size_t osize = size ();
+ value_type *olimit = oentries + osize;
+ size_t elts = elements ();
+
+ /* Resize only when table after removal of unused elements is either
+ too full or too empty. */
+ unsigned int nindex;
+ size_t nsize;
+ 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;
+ }
+
+ value_type *nentries = Allocator <value_type> ::data_alloc (nsize);
+ gcc_assert (nentries != NULL);
+ m_entries = nentries;
+ m_size = nsize;
+ m_size_prime_index = nindex;
+ m_n_elements -= m_n_deleted;
+ m_n_deleted = 0;
+
+ value_type *p = oentries;
+ do
+ {
+ value_type &x = *p;
+
+ if (!is_empty (x) && !is_deleted (x))
+ {
+ value_type *q = find_empty_slot_for_expand (Descriptor::hash (x));
+
+ *q = x;
+ }
+
+ p++;
+ }
+ while (p < olimit);
+
+ Allocator <value_type> ::data_free (oentries);
+}
+
+template<typename Descriptor, template<typename Type> class Allocator>
+void
+hash_table<Descriptor, Allocator, true>::empty ()
+{
+ size_t size = m_size;
+ value_type *entries = m_entries;
+ int i;
+
+ for (i = size - 1; i >= 0; i--)
+ if (!is_empty (entries[i]) && !is_deleted (entries[i]))
+ 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 (m_entries);
+ m_entries = Allocator <value_type> ::data_alloc (nsize);
+ m_size = nsize;
+ m_size_prime_index = nindex;
+ }
+ else
+ memset (entries, 0, size * sizeof (value_type));
+ m_n_deleted = 0;
+ m_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, true>::clear_slot (value_type *slot)
+{
+ if (slot < m_entries || slot >= m_entries + size ()
+ || is_empty (*slot) || is_deleted (*slot))
+ abort ();
+
+ Descriptor::remove (*slot);
+
+ mark_deleted (*slot);
+ m_n_deleted++;
+}
+
+/* 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, true>
+::find_with_hash (const compare_type &comparable, hashval_t hash)
+{
+ m_searches++;
+ size_t size = m_size;
+ hashval_t index = hash_table_mod1 (hash, m_size_prime_index);
+
+ value_type *entry = &m_entries[index];
+ if (is_empty (*entry)
+ || (!is_deleted (*entry) && Descriptor::equal (*entry, comparable)))
+ return *entry;
+
+ hashval_t hash2 = hash_table_mod2 (hash, m_size_prime_index);
+ for (;;)
+ {
+ m_collisions++;
+ index += hash2;
+ if (index >= size)
+ index -= size;
+
+ entry = &m_entries[index];
+ if (is_empty (*entry)
+ || (!is_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, true>
+::find_slot_with_hash (const compare_type &comparable, hashval_t hash,
+ enum insert_option insert)
+{
+ if (insert == INSERT && m_size * 3 <= m_n_elements * 4)
+ expand ();
+
+ m_searches++;
+
+ value_type *first_deleted_slot = NULL;
+ hashval_t index = hash_table_mod1 (hash, m_size_prime_index);
+ hashval_t hash2 = hash_table_mod2 (hash, m_size_prime_index);
+ value_type *entry = &m_entries[index];
+ size_t size = m_size;
+ if (is_empty (*entry))
+ goto empty_entry;
+ else if (is_deleted (*entry))
+ first_deleted_slot = &m_entries[index];
+ else if (Descriptor::equal (*entry, comparable))
+ return &m_entries[index];
+
+ for (;;)
+ {
+ m_collisions++;
+ index += hash2;
+ if (index >= size)
+ index -= size;
+
+ entry = &m_entries[index];
+ if (is_empty (*entry))
+ goto empty_entry;
+ else if (is_deleted (*entry))
+ {
+ if (!first_deleted_slot)
+ first_deleted_slot = &m_entries[index];
+ }
+ else if (Descriptor::equal (*entry, comparable))
+ return &m_entries[index];
+ }
+
+ empty_entry:
+ if (insert == NO_INSERT)
+ return NULL;
+
+ if (first_deleted_slot)
+ {
+ m_n_deleted--;
+ mark_empty (*first_deleted_slot);
+ return first_deleted_slot;
+ }
+
+ m_n_elements++;
+ return &m_entries[index];
+}
+
+/* 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, true>
+::remove_elt_with_hash (const compare_type &comparable, hashval_t hash)
+{
+ value_type *slot = find_slot_with_hash (comparable, hash, NO_INSERT);
+ if (is_empty (*slot))
+ return;
+
+ Descriptor::remove (*slot);
+
+ mark_deleted (*slot);
+ m_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, true>::traverse_noresize (Argument argument)
+{
+ value_type *slot = m_entries;
+ value_type *limit = slot + size ();
+
+ do
+ {
+ value_type &x = *slot;
+
+ if (!is_empty (x) && !is_deleted (x))
+ 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, true>::traverse (Argument argument)
+{
+ size_t size = m_size;
+ if (elements () * 8 < size && size > 32)
+ expand ();
+
+ traverse_noresize <Argument, Callback> (argument);
+}
+
+/* Slide down the iterator slots until an active entry is found. */
+
+template<typename Descriptor, template<typename Type> class Allocator>
+void
+hash_table<Descriptor, Allocator, true>::iterator::slide ()
+{
+ for ( ; m_slot < m_limit; ++m_slot )
+ {
+ value_type &x = *m_slot;
+ if (!is_empty (x) && !is_deleted (x))
+ return;
+ }
+ m_slot = NULL;
+ m_limit = NULL;
+}
+
+/* Bump the iterator. */
+
+template<typename Descriptor, template<typename Type> class Allocator>
+inline typename hash_table<Descriptor, Allocator, true>::iterator &
+hash_table<Descriptor, Allocator, true>::iterator::operator ++ ()
{
++m_slot;
slide ();
#define FOR_EACH_HASH_TABLE_ELEMENT(HTAB, RESULT, TYPE, ITER) \
for ((ITER) = (HTAB).begin (); \
- (ITER) != (HTAB).end () ? (RESULT = &*(ITER) , true) : false; \
+ (ITER) != (HTAB).end () ? (RESULT = *(ITER) , true) : false; \
++(ITER))
#endif /* TYPED_HASHTAB_H */