mirror of
https://github.com/moparisthebest/wget
synced 2024-07-03 16:38:41 -04:00
[svn] Simplify iteration over hash table entries.
This commit is contained in:
parent
e2e26c4487
commit
8b7dabcdf8
@ -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>
|
||||
|
||||
* Makefile.in: Use @datadir@. Define localedir as $(datadir)/locale.
|
||||
|
@ -700,8 +700,9 @@ dissociate_urls_from_file_mapper (void *key, void *value, void *arg)
|
||||
static void
|
||||
dissociate_urls_from_file (const char *file)
|
||||
{
|
||||
hash_table_map (dl_url_file_map, dissociate_urls_from_file_mapper,
|
||||
(char *)file);
|
||||
/* Can't use hash_table_iter_* because the table mutates while mapping. */
|
||||
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
|
||||
@ -937,19 +938,16 @@ downloaded_file (downloaded_file_t mode, const char *file)
|
||||
return FILE_NOT_ALREADY_DOWNLOADED;
|
||||
}
|
||||
|
||||
static int
|
||||
df_free_mapper (void *key, void *value, void *ignored)
|
||||
{
|
||||
xfree (key);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
downloaded_files_free (void)
|
||||
{
|
||||
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);
|
||||
downloaded_files_hash = NULL;
|
||||
}
|
||||
|
@ -1421,44 +1421,13 @@ cookie_jar_load (struct cookie_jar *jar, const char *file)
|
||||
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. */
|
||||
|
||||
void
|
||||
cookie_jar_save (struct cookie_jar *jar, const char *file)
|
||||
{
|
||||
FILE *fp;
|
||||
hash_table_iterator iter;
|
||||
|
||||
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));
|
||||
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))
|
||||
logprintf (LOG_NOTQUIET, _("Error writing to `%s': %s\n"),
|
||||
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
|
||||
jar. This is written in the form of a callback to hash_table_map
|
||||
and used by cookie_jar_delete to delete all the cookies in a
|
||||
jar. */
|
||||
jar. This is written in the form of a callback to
|
||||
hash_table_for_each and used by cookie_jar_delete to delete all the
|
||||
cookies in a jar. */
|
||||
|
||||
static int
|
||||
nuke_cookie_chain (void *value, void *key, void *arg)
|
||||
@ -1521,7 +1515,7 @@ nuke_cookie_chain (void *value, void *key, void *arg)
|
||||
void
|
||||
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);
|
||||
xfree (jar);
|
||||
}
|
||||
|
118
src/hash.c
118
src/hash.c
@ -72,7 +72,9 @@ so, delete this exception statement from your version. */
|
||||
hash_table_get_pair -- get key/value pair for key.
|
||||
hash_table_contains -- test whether the table contains 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_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
|
||||
operation remains constant.
|
||||
|
||||
By default, tables created by hash_table_new consider the keys to
|
||||
be equal if their pointer values are the same. You can use
|
||||
make_string_hash_table to create tables whose keys are considered
|
||||
equal if their string contents are the same. In the general case,
|
||||
the criterion of equality used to compare keys is specified at
|
||||
table creation time with two callback functions, "hash" and "test".
|
||||
The hash function transforms the key into an arbitrary number that
|
||||
must be the same for two equal keys. The test function accepts two
|
||||
keys and returns non-zero if they are to be considered equal.
|
||||
If not instructed otherwise, tables created by hash_table_new
|
||||
consider the keys to be equal if their pointer values are the same.
|
||||
You can use make_string_hash_table to create tables whose keys are
|
||||
considered equal if their string contents are the same. In the
|
||||
general case, the criterion of equality used to compare keys is
|
||||
specified at table creation time with two callback functions,
|
||||
"hash" and "test". The hash function transforms the key into an
|
||||
arbitrary number that must be the same for two equal keys. The
|
||||
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
|
||||
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
|
||||
shorter life-time need to be copied (with strdup() or the like in
|
||||
the case of strings) before being inserted. */
|
||||
shorter life-time probably need to be copied (with strdup() or the
|
||||
like in the case of strings) before being inserted. */
|
||||
|
||||
/* IMPLEMENTATION:
|
||||
|
||||
@ -495,20 +498,22 @@ hash_table_clear (struct hash_table *ht)
|
||||
ht->count = 0;
|
||||
}
|
||||
|
||||
/* Map MAPFUN over all entries in HT. MAPFUN is called with three
|
||||
arguments: the key, the value, and MAPARG.
|
||||
/* Call FN for each entry in HT. FN is called with three arguments:
|
||||
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
|
||||
hash table while hash_table_map is running. The exception is the
|
||||
entry you're currently mapping over; you may remove or change that
|
||||
entry. */
|
||||
hash table while hash_table_for_each is running. The exception is
|
||||
the entry you're currently mapping over; you may call
|
||||
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
|
||||
hash_table_map (struct hash_table *ht,
|
||||
int (*mapfun) (void *, void *, void *),
|
||||
void *maparg)
|
||||
hash_table_for_each (struct hash_table *ht,
|
||||
int (*fn) (void *, void *, void *), void *arg)
|
||||
{
|
||||
struct cell *c = ht->cells;
|
||||
struct cell *c = ht->cells;
|
||||
struct cell *end = ht->cells + ht->size;
|
||||
|
||||
for (; c < end; c++)
|
||||
@ -517,7 +522,7 @@ hash_table_map (struct hash_table *ht,
|
||||
void *key;
|
||||
repeat:
|
||||
key = c->key;
|
||||
if (mapfun (key, c->value, maparg))
|
||||
if (fn (key, c->value, arg))
|
||||
return;
|
||||
/* hash_table_remove might have moved the adjacent cells. */
|
||||
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
|
||||
same as the physical size of the hash table, which is always
|
||||
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.
|
||||
|
||||
This implementation is the Robert Jenkins' 32 bit Mix Function,
|
||||
with a simple adaptation for 64-bit values. It offers excellent
|
||||
spreading of values and doesn't need to know the hash table size to
|
||||
work (unlike the very popular Knuth's multiplication hash). */
|
||||
with a simple adaptation for 64-bit values. According to Jenkins
|
||||
it should offer excellent spreading of values. Unlike the popular
|
||||
Knuth's multiplication hash, this function doesn't need to know the
|
||||
hash table size to work. */
|
||||
|
||||
unsigned long
|
||||
hash_pointer (const void *ptr)
|
||||
{
|
||||
unsigned long key = (unsigned long)ptr;
|
||||
unsigned long key = (unsigned long) ptr;
|
||||
key += (key << 12);
|
||||
key ^= (key >> 22);
|
||||
key += (key << 4);
|
||||
@ -693,20 +741,16 @@ cmp_pointer (const void *ptr1, const void *ptr2)
|
||||
#include <stdio.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
|
||||
print_hash (struct hash_table *sht)
|
||||
{
|
||||
int debug_count = 0;
|
||||
hash_table_map (sht, print_hash_table_mapper, &debug_count);
|
||||
assert (debug_count == sht->count);
|
||||
hash_table_iterator iter;
|
||||
int count = 0;
|
||||
|
||||
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
|
||||
|
12
src/hash.h
12
src/hash.h
@ -45,8 +45,16 @@ void hash_table_put (struct hash_table *, const void *, void *);
|
||||
int hash_table_remove (struct hash_table *, const void *);
|
||||
void hash_table_clear (struct hash_table *);
|
||||
|
||||
void hash_table_map (struct hash_table *,
|
||||
int (*) (void *, void *, void *), void *);
|
||||
void hash_table_for_each (struct hash_table *,
|
||||
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 *);
|
||||
|
||||
struct hash_table *make_string_hash_table (int);
|
||||
|
26
src/host.c
26
src/host.c
@ -857,26 +857,22 @@ sufmatch (const char **list, const char *what)
|
||||
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
|
||||
host_cleanup (void)
|
||||
{
|
||||
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);
|
||||
host_name_addresses_map = NULL;
|
||||
}
|
||||
|
17
src/res.c
17
src/res.c
@ -552,20 +552,19 @@ res_retrieve_file (const char *url, char **file)
|
||||
return err == RETROK;
|
||||
}
|
||||
|
||||
static int
|
||||
cleanup_hash_table_mapper (void *key, void *value, void *arg_ignored)
|
||||
{
|
||||
xfree (key);
|
||||
free_specs (value);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
res_cleanup (void)
|
||||
{
|
||||
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);
|
||||
registered_specs = NULL;
|
||||
}
|
||||
|
40
src/utils.c
40
src/utils.c
@ -1165,50 +1165,36 @@ string_set_contains (struct hash_table *ht, const char *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
|
||||
enough to hold hash_table_count(ht) char pointers. */
|
||||
|
||||
void string_set_to_array (struct hash_table *ht, char **array)
|
||||
{
|
||||
hash_table_map (ht, string_set_to_array_mapper, &array);
|
||||
}
|
||||
|
||||
static int
|
||||
string_set_free_mapper (void *key, void *value_ignored, void *arg_ignored)
|
||||
{
|
||||
xfree (key);
|
||||
return 0;
|
||||
hash_table_iterator iter;
|
||||
for (hash_table_iterate (ht, &iter); hash_table_iter_next (&iter); )
|
||||
*array++ = iter.key;
|
||||
}
|
||||
|
||||
void
|
||||
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);
|
||||
}
|
||||
|
||||
static int
|
||||
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. */
|
||||
/* Utility function: simply call free() on all keys and values of HT. */
|
||||
|
||||
void
|
||||
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
|
||||
|
Loading…
Reference in New Issue
Block a user