[svn] Simplify iteration over hash table entries.

This commit is contained in:
hniksic 2005-08-27 06:05:39 -07:00
parent e2e26c4487
commit 8b7dabcdf8
8 changed files with 174 additions and 138 deletions

View File

@ -1,3 +1,14 @@
2005-08-27 Hrvoje Niksic <hniksic@xemacs.org>
* hash.c (hash_table_map): Rename to hash_table_for_each and
update callers.
Document the meaning of the callback's return value.
(hash_table_iterate): New function.
(hash_table_iter_next): Likewise.
Update most places that used hash_table_for_each to use the
iteration, which doesn't require a temporary function with
explicit state management.
2005-08-26 Albert Chin <wget@mlists.thewrittenword.com> 2005-08-26 Albert Chin <wget@mlists.thewrittenword.com>
* Makefile.in: Use @datadir@. Define localedir as $(datadir)/locale. * Makefile.in: Use @datadir@. Define localedir as $(datadir)/locale.

View File

@ -700,8 +700,9 @@ dissociate_urls_from_file_mapper (void *key, void *value, void *arg)
static void static void
dissociate_urls_from_file (const char *file) dissociate_urls_from_file (const char *file)
{ {
hash_table_map (dl_url_file_map, dissociate_urls_from_file_mapper, /* Can't use hash_table_iter_* because the table mutates while mapping. */
(char *)file); hash_table_for_each (dl_url_file_map, dissociate_urls_from_file_mapper,
(char *) file);
} }
/* Register that URL has been successfully downloaded to FILE. This /* Register that URL has been successfully downloaded to FILE. This
@ -937,19 +938,16 @@ downloaded_file (downloaded_file_t mode, const char *file)
return FILE_NOT_ALREADY_DOWNLOADED; return FILE_NOT_ALREADY_DOWNLOADED;
} }
static int
df_free_mapper (void *key, void *value, void *ignored)
{
xfree (key);
return 0;
}
static void static void
downloaded_files_free (void) downloaded_files_free (void)
{ {
if (downloaded_files_hash) if (downloaded_files_hash)
{ {
hash_table_map (downloaded_files_hash, df_free_mapper, NULL); hash_table_iterator iter;
for (hash_table_iterate (downloaded_files_hash, &iter);
hash_table_iter_next (&iter);
)
xfree (iter.key);
hash_table_destroy (downloaded_files_hash); hash_table_destroy (downloaded_files_hash);
downloaded_files_hash = NULL; downloaded_files_hash = NULL;
} }

View File

@ -1421,44 +1421,13 @@ cookie_jar_load (struct cookie_jar *jar, const char *file)
fclose (fp); fclose (fp);
} }
/* Mapper for save_cookies callable by hash_table_map. VALUE points
to the head in a chain of cookies. The function prints the entire
chain. */
static int
save_cookies_mapper (void *key, void *value, void *arg)
{
FILE *fp = (FILE *)arg;
char *domain = (char *)key;
struct cookie *cookie = (struct cookie *)value;
for (; cookie; cookie = cookie->next)
{
if (!cookie->permanent && !opt.keep_session_cookies)
continue;
if (cookie_expired_p (cookie))
continue;
if (!cookie->domain_exact)
fputc ('.', fp);
fputs (domain, fp);
if (cookie->port != PORT_ANY)
fprintf (fp, ":%d", cookie->port);
fprintf (fp, "\t%s\t%s\t%s\t%.0f\t%s\t%s\n",
cookie->domain_exact ? "FALSE" : "TRUE",
cookie->path, cookie->secure ? "TRUE" : "FALSE",
(double)cookie->expiry_time,
cookie->attr, cookie->value);
if (ferror (fp))
return 1; /* stop mapping */
}
return 0;
}
/* Save cookies, in format described above, to FILE. */ /* Save cookies, in format described above, to FILE. */
void void
cookie_jar_save (struct cookie_jar *jar, const char *file) cookie_jar_save (struct cookie_jar *jar, const char *file)
{ {
FILE *fp; FILE *fp;
hash_table_iterator iter;
DEBUGP (("Saving cookies to %s.\n", file)); DEBUGP (("Saving cookies to %s.\n", file));
@ -1476,8 +1445,33 @@ cookie_jar_save (struct cookie_jar *jar, const char *file)
fprintf (fp, "# Generated by Wget on %s.\n", datetime_str (&cookies_now)); fprintf (fp, "# Generated by Wget on %s.\n", datetime_str (&cookies_now));
fputs ("# Edit at your own risk.\n\n", fp); fputs ("# Edit at your own risk.\n\n", fp);
hash_table_map (jar->chains, save_cookies_mapper, fp); for (hash_table_iterate (jar->chains, &iter);
hash_table_iter_next (&iter);
)
{
const char *domain = iter.key;
struct cookie *cookie = iter.value;
for (; cookie; cookie = cookie->next)
{
if (!cookie->permanent && !opt.keep_session_cookies)
continue;
if (cookie_expired_p (cookie))
continue;
if (!cookie->domain_exact)
fputc ('.', fp);
fputs (domain, fp);
if (cookie->port != PORT_ANY)
fprintf (fp, ":%d", cookie->port);
fprintf (fp, "\t%s\t%s\t%s\t%.0f\t%s\t%s\n",
cookie->domain_exact ? "FALSE" : "TRUE",
cookie->path, cookie->secure ? "TRUE" : "FALSE",
(double)cookie->expiry_time,
cookie->attr, cookie->value);
if (ferror (fp))
goto out;
}
}
out:
if (ferror (fp)) if (ferror (fp))
logprintf (LOG_NOTQUIET, _("Error writing to `%s': %s\n"), logprintf (LOG_NOTQUIET, _("Error writing to `%s': %s\n"),
file, strerror (errno)); file, strerror (errno));
@ -1489,9 +1483,9 @@ cookie_jar_save (struct cookie_jar *jar, const char *file)
} }
/* Destroy all the elements in the chain and unhook it from the cookie /* Destroy all the elements in the chain and unhook it from the cookie
jar. This is written in the form of a callback to hash_table_map jar. This is written in the form of a callback to
and used by cookie_jar_delete to delete all the cookies in a hash_table_for_each and used by cookie_jar_delete to delete all the
jar. */ cookies in a jar. */
static int static int
nuke_cookie_chain (void *value, void *key, void *arg) nuke_cookie_chain (void *value, void *key, void *arg)
@ -1521,7 +1515,7 @@ nuke_cookie_chain (void *value, void *key, void *arg)
void void
cookie_jar_delete (struct cookie_jar *jar) cookie_jar_delete (struct cookie_jar *jar)
{ {
hash_table_map (jar->chains, nuke_cookie_chain, jar); hash_table_for_each (jar->chains, nuke_cookie_chain, jar);
hash_table_destroy (jar->chains); hash_table_destroy (jar->chains);
xfree (jar); xfree (jar);
} }

View File

@ -72,7 +72,9 @@ so, delete this exception statement from your version. */
hash_table_get_pair -- get key/value pair for key. hash_table_get_pair -- get key/value pair for key.
hash_table_contains -- test whether the table contains key. hash_table_contains -- test whether the table contains key.
hash_table_remove -- remove key->value mapping for given key. hash_table_remove -- remove key->value mapping for given key.
hash_table_map -- iterate through table entries. hash_table_for_each -- call function for each table entry.
hash_table_iterate -- iterate over entries in hash table.
hash_table_iter_next -- return next element during iteration.
hash_table_clear -- clear hash table contents. hash_table_clear -- clear hash table contents.
hash_table_count -- return the number of entries in the table. hash_table_count -- return the number of entries in the table.
@ -81,21 +83,22 @@ so, delete this exception statement from your version. */
with each resize, which ensures that the amortized time per with each resize, which ensures that the amortized time per
operation remains constant. operation remains constant.
By default, tables created by hash_table_new consider the keys to If not instructed otherwise, tables created by hash_table_new
be equal if their pointer values are the same. You can use consider the keys to be equal if their pointer values are the same.
make_string_hash_table to create tables whose keys are considered You can use make_string_hash_table to create tables whose keys are
equal if their string contents are the same. In the general case, considered equal if their string contents are the same. In the
the criterion of equality used to compare keys is specified at general case, the criterion of equality used to compare keys is
table creation time with two callback functions, "hash" and "test". specified at table creation time with two callback functions,
The hash function transforms the key into an arbitrary number that "hash" and "test". The hash function transforms the key into an
must be the same for two equal keys. The test function accepts two arbitrary number that must be the same for two equal keys. The
keys and returns non-zero if they are to be considered equal. test function accepts two keys and returns non-zero if they are to
be considered equal.
Note that neither keys nor values are copied when inserted into the Note that neither keys nor values are copied when inserted into the
hash table, so they must exist for the lifetime of the table. This hash table, so they must exist for the lifetime of the table. This
means that e.g. the use of static strings is OK, but objects with a means that e.g. the use of static strings is OK, but objects with a
shorter life-time need to be copied (with strdup() or the like in shorter life-time probably need to be copied (with strdup() or the
the case of strings) before being inserted. */ like in the case of strings) before being inserted. */
/* IMPLEMENTATION: /* IMPLEMENTATION:
@ -495,18 +498,20 @@ hash_table_clear (struct hash_table *ht)
ht->count = 0; ht->count = 0;
} }
/* Map MAPFUN over all entries in HT. MAPFUN is called with three /* Call FN for each entry in HT. FN is called with three arguments:
arguments: the key, the value, and MAPARG. the key, the value, and ARG. When FN returns a non-zero value, the
mapping stops.
It is undefined what happens if you add or remove entries in the It is undefined what happens if you add or remove entries in the
hash table while hash_table_map is running. The exception is the hash table while hash_table_for_each is running. The exception is
entry you're currently mapping over; you may remove or change that the entry you're currently mapping over; you may call
entry. */ hash_table_put or hash_table_remove on that entry's key. That is
also the reason why this function cannot be implemented in terms of
hash_table_iterate. */
void void
hash_table_map (struct hash_table *ht, hash_table_for_each (struct hash_table *ht,
int (*mapfun) (void *, void *, void *), int (*fn) (void *, void *, void *), void *arg)
void *maparg)
{ {
struct cell *c = ht->cells; struct cell *c = ht->cells;
struct cell *end = ht->cells + ht->size; struct cell *end = ht->cells + ht->size;
@ -517,7 +522,7 @@ hash_table_map (struct hash_table *ht,
void *key; void *key;
repeat: repeat:
key = c->key; key = c->key;
if (mapfun (key, c->value, maparg)) if (fn (key, c->value, arg))
return; return;
/* hash_table_remove might have moved the adjacent cells. */ /* hash_table_remove might have moved the adjacent cells. */
if (c->key != key && CELL_OCCUPIED (c)) if (c->key != key && CELL_OCCUPIED (c))
@ -525,6 +530,48 @@ hash_table_map (struct hash_table *ht,
} }
} }
/* Initiate iteration over HT. Get the next entry using
hash_table_iter_next. The typical loop looks like this:
hash_table_iterator iter;
for (hash_table_iterate (ht, &iter); hash_table_iter_next (&iter); )
... do something with iter.key and iter.value ...
The iterator does not need to be deallocated after use. The hash
table must not be modified while being iterated over. */
void
hash_table_iterate (struct hash_table *ht, hash_table_iterator *iter)
{
iter->pos = ht->cells;
iter->end = ht->cells + ht->size;
}
/* Get the next hash table entry. ITER is an iterator object
initialized using hash_table_iterate. While there are more
entries, the key and value pointers are stored to ITER->key and
ITER->value respectively and 1 is returned. When there are no more
entries, 0 is returned.
The hash table must not be modified between calls to this
function. */
int
hash_table_iter_next (hash_table_iterator *iter)
{
struct cell *c = iter->pos;
struct cell *end = iter->end;
for (; c < end; c++)
if (CELL_OCCUPIED (c))
{
iter->key = c->key;
iter->value = c->value;
iter->pos = c + 1;
return 1;
}
return 0;
}
/* Return the number of elements in the hash table. This is not the /* Return the number of elements in the hash table. This is not the
same as the physical size of the hash table, which is always same as the physical size of the hash table, which is always
greater than the number of elements. */ greater than the number of elements. */
@ -653,14 +700,15 @@ make_nocase_string_hash_table (int items)
/* Hashing of numeric values, such as pointers and integers. /* Hashing of numeric values, such as pointers and integers.
This implementation is the Robert Jenkins' 32 bit Mix Function, This implementation is the Robert Jenkins' 32 bit Mix Function,
with a simple adaptation for 64-bit values. It offers excellent with a simple adaptation for 64-bit values. According to Jenkins
spreading of values and doesn't need to know the hash table size to it should offer excellent spreading of values. Unlike the popular
work (unlike the very popular Knuth's multiplication hash). */ Knuth's multiplication hash, this function doesn't need to know the
hash table size to work. */
unsigned long unsigned long
hash_pointer (const void *ptr) hash_pointer (const void *ptr)
{ {
unsigned long key = (unsigned long)ptr; unsigned long key = (unsigned long) ptr;
key += (key << 12); key += (key << 12);
key ^= (key >> 22); key ^= (key >> 22);
key += (key << 4); key += (key << 4);
@ -693,20 +741,16 @@ cmp_pointer (const void *ptr1, const void *ptr2)
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
int
print_hash_table_mapper (void *key, void *value, void *count)
{
++*(int *)count;
printf ("%s: %s\n", (const char *)key, (char *)value);
return 0;
}
void void
print_hash (struct hash_table *sht) print_hash (struct hash_table *sht)
{ {
int debug_count = 0; hash_table_iterator iter;
hash_table_map (sht, print_hash_table_mapper, &debug_count); int count = 0;
assert (debug_count == sht->count);
for (hash_table_iterate (sht, &iter); hash_table_iter_next (&iter);
++count)
printf ("%s: %s\n", iter.key, iter.value);
assert (count == sht->count);
} }
int int

View File

@ -45,8 +45,16 @@ void hash_table_put (struct hash_table *, const void *, void *);
int hash_table_remove (struct hash_table *, const void *); int hash_table_remove (struct hash_table *, const void *);
void hash_table_clear (struct hash_table *); void hash_table_clear (struct hash_table *);
void hash_table_map (struct hash_table *, void hash_table_for_each (struct hash_table *,
int (*) (void *, void *, void *), void *); int (*) (void *, void *, void *), void *);
typedef struct {
void *key, *value; /* public members */
void *pos, *end; /* private members */
} hash_table_iterator;
void hash_table_iterate (struct hash_table *, hash_table_iterator *);
int hash_table_iter_next (hash_table_iterator *);
int hash_table_count (const struct hash_table *); int hash_table_count (const struct hash_table *);
struct hash_table *make_string_hash_table (int); struct hash_table *make_string_hash_table (int);

View File

@ -857,26 +857,22 @@ sufmatch (const char **list, const char *what)
return false; return false;
} }
static int
host_cleanup_mapper (void *key, void *value, void *arg_ignored)
{
struct address_list *al;
xfree (key); /* host */
al = (struct address_list *)value;
assert (al->refcount == 1);
address_list_delete (al);
return 0;
}
void void
host_cleanup (void) host_cleanup (void)
{ {
if (host_name_addresses_map) if (host_name_addresses_map)
{ {
hash_table_map (host_name_addresses_map, host_cleanup_mapper, NULL); hash_table_iterator iter;
for (hash_table_iterate (host_name_addresses_map, &iter);
hash_table_iter_next (&iter);
)
{
char *host = iter.key;
struct address_list *al = iter.value;
xfree (host);
assert (al->refcount == 1);
address_list_delete (al);
}
hash_table_destroy (host_name_addresses_map); hash_table_destroy (host_name_addresses_map);
host_name_addresses_map = NULL; host_name_addresses_map = NULL;
} }

View File

@ -552,20 +552,19 @@ res_retrieve_file (const char *url, char **file)
return err == RETROK; return err == RETROK;
} }
static int
cleanup_hash_table_mapper (void *key, void *value, void *arg_ignored)
{
xfree (key);
free_specs (value);
return 0;
}
void void
res_cleanup (void) res_cleanup (void)
{ {
if (registered_specs) if (registered_specs)
{ {
hash_table_map (registered_specs, cleanup_hash_table_mapper, NULL); hash_table_iterator iter;
for (hash_table_iterate (registered_specs, &iter);
hash_table_iter_next (&iter);
)
{
xfree (iter.key);
free_specs (iter.value);
}
hash_table_destroy (registered_specs); hash_table_destroy (registered_specs);
registered_specs = NULL; registered_specs = NULL;
} }

View File

@ -1165,50 +1165,36 @@ string_set_contains (struct hash_table *ht, const char *s)
return hash_table_contains (ht, s); return hash_table_contains (ht, s);
} }
static int
string_set_to_array_mapper (void *key, void *value_ignored, void *arg)
{
char ***arrayptr = (char ***) arg;
*(*arrayptr)++ = (char *) key;
return 0;
}
/* Convert the specified string set to array. ARRAY should be large /* Convert the specified string set to array. ARRAY should be large
enough to hold hash_table_count(ht) char pointers. */ enough to hold hash_table_count(ht) char pointers. */
void string_set_to_array (struct hash_table *ht, char **array) void string_set_to_array (struct hash_table *ht, char **array)
{ {
hash_table_map (ht, string_set_to_array_mapper, &array); hash_table_iterator iter;
} for (hash_table_iterate (ht, &iter); hash_table_iter_next (&iter); )
*array++ = iter.key;
static int
string_set_free_mapper (void *key, void *value_ignored, void *arg_ignored)
{
xfree (key);
return 0;
} }
void void
string_set_free (struct hash_table *ht) string_set_free (struct hash_table *ht)
{ {
hash_table_map (ht, string_set_free_mapper, NULL); hash_table_iterator iter;
for (hash_table_iterate (ht, &iter); hash_table_iter_next (&iter); )
xfree (iter.key);
hash_table_destroy (ht); hash_table_destroy (ht);
} }
static int /* Utility function: simply call free() on all keys and values of HT. */
free_keys_and_values_mapper (void *key, void *value, void *arg_ignored)
{
xfree (key);
xfree (value);
return 0;
}
/* Another utility function: call free() on all keys and values of HT. */
void void
free_keys_and_values (struct hash_table *ht) free_keys_and_values (struct hash_table *ht)
{ {
hash_table_map (ht, free_keys_and_values_mapper, NULL); hash_table_iterator iter;
for (hash_table_iterate (ht, &iter); hash_table_iter_next (&iter); )
{
xfree (iter.key);
xfree (iter.value);
}
} }
/* Get digit grouping data for thousand separors by calling /* Get digit grouping data for thousand separors by calling