]> sjero.net Git - wget/commitdiff
[svn] Simplify iteration over hash table entries.
authorhniksic <devnull@localhost>
Sat, 27 Aug 2005 13:05:39 +0000 (06:05 -0700)
committerhniksic <devnull@localhost>
Sat, 27 Aug 2005 13:05:39 +0000 (06:05 -0700)
src/ChangeLog
src/convert.c
src/cookies.c
src/hash.c
src/hash.h
src/host.c
src/res.c
src/utils.c

index 79e59b938ee175c2b87169208b81edfb06e4f76f..43784484482e2ec778e6aadf18b4f3cabbac2eed 100644 (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>
 
        * Makefile.in: Use @datadir@.  Define localedir as $(datadir)/locale.
index f2c6a3a567e531852bd54aa80718da6714b77fa9..a460a4b4fb01c9205382fd548307b8979584af6e 100644 (file)
@@ -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;
     }
index 9320eabbab32b90eca0aaed200a6471f6224190d..075a45d9e5099561b229f6201b9d245f058ab456 100644 (file)
@@ -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)
 }
 \f
 /* 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);
 }
index 0330d7f8e4f5bcc3f295b54b520a1b0f1fee493d..36a90be82c9cffcc408f5a2846f4b775a12f7954 100644 (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_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
index d4173a1602facfc7b3a14d1a8216725d41c338e3..61977df101bf881c476b4c1a447bbe14e82f6954 100644 (file)
@@ -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);
index aecefabaa9b76c491c26c80988e9cc23d5c49c86..1cfd01703b71e2e3fe6ee5cddf45ced6feba43ae 100644 (file)
@@ -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;
     }
index fa3dbb7e906fce6c7feb845b2be55c9af14e59f0..630d74b5ce7ec4767f19052d0ef59f93385a974c 100644 (file)
--- a/src/res.c
+++ b/src/res.c
@@ -552,20 +552,19 @@ res_retrieve_file (const char *url, char **file)
   return err == RETROK;
 }
 \f
-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;
     }
index 697ebcf24e2349434ca67e9abbb5afcc1fc0cc92..94dc989eed8add3421d028e9f564fb5fccf77a18 100644 (file)
@@ -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);
+    }
 }
 \f
 /* Get digit grouping data for thousand separors by calling