<URL: http://bugs.freeciv.org/Ticket/Display.html?id=40597 >

Attached patch improves hash key/value iteration using
the iterator interface in 40596.

- For convenience three iterate macros are provided for
  hash tables: for iterating over keys, values, or both.
- The implementation of the iterator and hash table is
  kept in the hash.c module and out of the header.
- Code making its own iteration macros based on internal
  hash tables is simplified.


-----------------------------------------------------------------------
あなたの凄い運命をこよなく愛するようになる。
 client/editor.c               |   12 ++--
 client/gui-gtk-2.0/editprop.c |   59 +++++++++------
 server/edithand.c             |    4 +-
 utility/hash.c                |  167 +++++++++++++++++++++++++++++++----------
 utility/hash.h                |   42 +++++------
 5 files changed, 189 insertions(+), 95 deletions(-)

diff --git a/client/editor.c b/client/editor.c
index d5b89f8..9cae047 100644
--- a/client/editor.c
+++ b/client/editor.c
@@ -510,11 +510,11 @@ static void popup_properties(struct tile *ptile)
   tiles = tile_list_new();
 
   if (editor_tile_is_selected(ptile)) {
-    hash_iterate(editor->selected_tile_table, sel_tile, dummy) {
+    hash_keys_iterate(editor->selected_tile_table, sel_tile) {
       if (can_edit_tile_properties(sel_tile)) {
         tile_list_append(tiles, sel_tile);
       }
-    } hash_iterate_end;
+    } hash_keys_iterate_end;
   } else {
     if (can_edit_tile_properties(ptile)) {
       tile_list_append(tiles, ptile);
@@ -1023,9 +1023,9 @@ void editor_apply_tool_to_selection(void)
   }
 
   connection_do_buffer(&client.conn);
-  hash_iterate(editor->selected_tile_table, ptile, dummy) {
+  hash_keys_iterate(editor->selected_tile_table, ptile) {
     editor_apply_tool(ptile, TRUE);
-  } hash_iterate_end;
+  } hash_keys_iterate_end;
   editor_notify_edit_finished();
   connection_do_unbuffer(&client.conn);
 
@@ -1825,11 +1825,11 @@ const struct tile *editor_get_selection_center(void)
   }
 
   origin = map_pos_to_tile(0, 0);
-  hash_iterate(editor->selected_tile_table, ptile, dummy) {
+  hash_keys_iterate(editor->selected_tile_table, ptile) {
     map_distance_vector(&dx, &dy, origin, ptile);
     xsum += dx;
     ysum += dy;
-  } hash_iterate_end;
+  } hash_keys_iterate_end;
 
   cx = xsum / count;
   cy = ysum / count;
diff --git a/client/gui-gtk-2.0/editprop.c b/client/gui-gtk-2.0/editprop.c
index 8c0863c..26696f1 100644
--- a/client/gui-gtk-2.0/editprop.c
+++ b/client/gui-gtk-2.0/editprop.c
@@ -22,6 +22,7 @@
 
 #include "fcintl.h"
 #include "hash.h"
+#include "iterator.h"
 #include "log.h"
 #include "mem.h"
 
@@ -539,29 +540,19 @@ static void property_page_remove_creation_tag(struct property_page *pp,
 static bool property_page_tag_is_known(struct property_page *pp, int tag);
 static void property_page_clear_tags(struct property_page *pp);
 
-#define property_page_objprop_iterate(ARG_pp, NAME_op) do {\
-  struct objprop *NAME_op;\
-  if (!(ARG_pp) || !(ARG_pp)->objprop_table) {\
-    break;\
-  }\
-  hash_iterate((ARG_pp)->objprop_table, MY_dummy, MY_value) {\
-    NAME_op = MY_value;
+static struct iterator *
+property_page_get_objprop_iter(const struct property_page *pp);
+#define property_page_objprop_iterate(ARG_pp, NAME_op)\
+  generic_iterate(property_page_get_objprop_iter(ARG_pp),\
+                  struct objprop *, NAME_op)
+#define property_page_objprop_iterate_end generic_iterate_end
 
-#define property_page_objprop_iterate_end \
-  } hash_iterate_end;\
-} while (0)
-
-#define property_page_objbind_iterate(ARG_pp, NAME_ob) do {\
-  struct objbind *NAME_ob;\
-  if (!(ARG_pp) || !(ARG_pp)->objbind_table) {\
-    break;\
-  }\
-  hash_iterate((ARG_pp)->objbind_table, MY_dummy, MY_value) {\
-    NAME_ob = MY_value;
-
-#define property_page_objbind_iterate_end \
-  } hash_iterate_end;\
-} while (0)
+static struct iterator *
+property_page_get_objbind_iter(const struct property_page *pp);
+#define property_page_objbind_iterate(ARG_pp, NAME_ob)\
+  generic_iterate(property_page_get_objbind_iter(ARG_pp),\
+                  struct objbind *, NAME_ob)
+#define property_page_objbind_iterate_end generic_iterate_end
 
 
 /****************************************************************************
@@ -3685,6 +3676,30 @@ static void property_page_setup_objprops(struct property_page *pp)
 }
 
 /****************************************************************************
+  Return an iterator over the object properties in the property page.
+****************************************************************************/
+static struct iterator *
+property_page_get_objprop_iter(const struct property_page *pp)
+{
+  if (!pp || !pp->objprop_table) {
+    return NULL;
+  }
+  return hash_get_value_iter(pp->objprop_table);
+}
+
+/****************************************************************************
+  Return an iterator over the bound objects in the property page.
+****************************************************************************/
+static struct iterator *
+property_page_get_objbind_iter(const struct property_page *pp)
+{
+  if (!pp || !pp->objbind_table) {
+    return NULL;
+  }
+  return hash_get_value_iter(pp->objbind_table);
+}
+
+/****************************************************************************
   Callback for when a property page's listview's selection changes.
 ****************************************************************************/
 static void property_page_selection_changed(GtkTreeSelection *sel,
diff --git a/server/edithand.c b/server/edithand.c
index 04ca03e..64dff4a 100644
--- a/server/edithand.c
+++ b/server/edithand.c
@@ -98,9 +98,9 @@ static void check_edited_tile_terrains(void)
     return;
   }
 
-  hash_iterate(unfixed_tile_table, ptile, dummy) {
+  hash_keys_iterate(unfixed_tile_table, ptile) {
     fix_tile_on_terrain_change(ptile, FALSE);
-  } hash_iterate_end;
+  } hash_keys_iterate_end;
   hash_delete_all_entries(unfixed_tile_table);
 
   assign_continent_numbers();
diff --git a/utility/hash.c b/utility/hash.c
index e212b2d..b8353f5 100644
--- a/utility/hash.c
+++ b/utility/hash.c
@@ -133,6 +133,15 @@ struct hash_table {
   bool no_shrink;		/* do not auto-shrink when set */
 };
 
+struct hash_iterator {
+  struct iterator vtable;
+  const struct hash_table *table;
+  int index;
+  const void *key;
+  const void *value;
+};
+#define HASH_ITERATOR(p) ((struct hash_iterator *)(p))
+
 /* Calculate hash value given hash_table ptr and key: */
 #define HASH_VAL(h,k) (((h)->fval)((k), ((h)->num_buckets)))
 
@@ -785,82 +794,158 @@ const void *hash_value_by_number(const struct hash_table *h,
 }
 
 /**************************************************************************
-  If the hash table is not empty, sets 'iter' to point to the start of the
-  hash table and returns TRUE. Otherwise returns FALSE.
+  Prevent or allow the hash table automatically shrinking. Returns
+  the old value of the setting.
 **************************************************************************/
-bool hash_get_start_iter(const struct hash_table *h,
-                         struct hash_iter *iter)
+bool hash_set_no_shrink(struct hash_table *h, bool no_shrink)
 {
-  if (!h || !iter || hash_num_entries(h) < 1) {
-    return FALSE;
+  bool old = h->no_shrink;
+  h->no_shrink = no_shrink;
+  return old;
+}
+
+/**************************************************************************
+  Helper function for hash (key, value) pair iteration.
+**************************************************************************/
+void *hash_iter_get_key(const struct iterator *hash_iter)
+{
+  if (!hash_iter) {
+    return NULL;
   }
+  return (void *)(HASH_ITERATOR(hash_iter)->key);
+}
 
-  iter->table = h;
-  iter->index = -1;
-  return hash_iter_next(iter);
+/**************************************************************************
+  Helper function for hash (key, value) pair iteration.
+**************************************************************************/
+void *hash_iter_get_value(const struct iterator *hash_iter)
+{
+  if (!hash_iter) {
+    return NULL;
+  }
+  return (void *)(HASH_ITERATOR(hash_iter)->value);
 }
 
 /**************************************************************************
-  Set the iterator 'iter' to the next item in the hash table. Returns
-  FALSE if there are no more items.
+  Iterator interface 'next' function implementation.
 **************************************************************************/
-bool hash_iter_next(struct hash_iter *iter)
+static void hash_iter_next(struct iterator *iter)
 {
+  struct hash_iterator *it;
   const struct hash_table *h;
-  struct hash_bucket *bucket;
+  const struct hash_bucket *bucket;
 
-  if (!iter || !iter->table) {
-    return FALSE;
+  it = HASH_ITERATOR(iter);
+  if (!it || !it->table) {
+    return;
   }
 
-  h = iter->table;
-  iter->index++;
+  h = it->table;
+  it->index++;
 
-  while (iter->index < hash_num_buckets(h)) {
-    bucket = h->buckets + iter->index;
-    if (bucket && bucket->used == BUCKET_USED) {
-      iter->key = bucket->key;
-      iter->value = bucket->data;
-      return TRUE;
+  while (it->index < hash_num_buckets(h)) {
+    bucket = h->buckets + it->index;
+    if (bucket->used == BUCKET_USED) {
+      it->key = bucket->key;
+      it->value = bucket->data;
+      return;
     }
-    iter->index++;
+    it->index++;
   }
 
-  return FALSE;
+  it->key = NULL;
+  it->value = NULL;
 }
 
 /**************************************************************************
-  Returns the key of the hash table item pointed to by this iterator.
+  Iterator interface 'free' function implementation.
 **************************************************************************/
-void *hash_iter_get_key(struct hash_iter *iter)
+static void hash_iter_free(struct iterator *iter)
 {
-  if (!iter) {
-    return NULL;
+  struct hash_iterator *it;
+  it = HASH_ITERATOR(iter);
+  if (it) {
+    it->table = NULL;
+    free(it);
   }
-  return (void *) iter->key;
 }
 
 /**************************************************************************
-  Returns the value (or "data") of the hash table item pointed to by this
-  iterator.
+  Iterator interface 'get' function implementation. This just returns the
+  iterator itself, so you would need to use hash_iter_get_key/value to
+  get the actual keys and values.
 **************************************************************************/
-void *hash_iter_get_value(struct hash_iter *iter)
+static void *hash_iter_get(const struct iterator *iter)
 {
-  if (!iter) {
+  return (void *) iter;
+}
+
+/**************************************************************************
+  Iterator interface 'valid' function implementation.
+**************************************************************************/
+static bool hash_iter_valid(const struct iterator *iter)
+{
+  struct hash_iterator *it;
+  it = HASH_ITERATOR(iter);
+  if (!it || !it->table) {
+    return FALSE;
+  }
+  return 0 <= it->index && it->index < hash_num_buckets(it->table);
+}
+
+/**************************************************************************
+  Returns an iterator that iterates over both keys and values of the hash
+  table. NB: iterator_next() returns an iterator pointer, so use the helper
+  functions hash_iter_get_{key,value} to access the key and value.
+**************************************************************************/
+struct iterator *hash_get_iter(const struct hash_table *h)
+{
+  struct hash_iterator *it;
+
+  if (!h || hash_num_entries(h) < 1) {
     return NULL;
   }
-  return (void *) iter->value;
+
+  it = fc_calloc(1, sizeof(*it));
+  it->vtable.next = hash_iter_next;
+  it->vtable.free = hash_iter_free;
+  it->vtable.get = hash_iter_get;
+  it->vtable.valid = hash_iter_valid;
+  it->table = h;
+  it->index = -1;
+
+  /* Seek to first used bucket. */
+  hash_iter_next(ITERATOR(it));
+
+  return ITERATOR(it);
 }
 
 /**************************************************************************
-  Prevent or allow the hash table automatically shrinking. Returns
-  the old value of the setting.
+  Returns an iterator over the hash table's keys.
 **************************************************************************/
-bool hash_set_no_shrink(struct hash_table *h, bool no_shrink)
+struct iterator *hash_get_key_iter(const struct hash_table *h)
 {
-  bool old = h->no_shrink;
+  struct hash_iterator *it;
 
-  h->no_shrink = no_shrink;
+  it = HASH_ITERATOR(hash_get_iter(h));
+  if (it) {
+    it->vtable.get = hash_iter_get_key;
+  }
 
-  return old;
+  return ITERATOR(it);
+}
+
+/**************************************************************************
+  Returns an iterator over the hash table's values.
+**************************************************************************/
+struct iterator *hash_get_value_iter(const struct hash_table *h)
+{
+  struct hash_iterator *it;
+
+  it = HASH_ITERATOR(hash_get_iter(h));
+  if (it) {
+    it->vtable.get = hash_iter_get_value;
+  }
+
+  return ITERATOR(it);
 }
diff --git a/utility/hash.h b/utility/hash.h
index d23f6f5..de9227e 100644
--- a/utility/hash.h
+++ b/utility/hash.h
@@ -82,29 +82,23 @@ unsigned int hash_num_deleted(const struct hash_table *h);
 bool hash_set_no_shrink(struct hash_table *h,
                         bool no_shrink);
 
-struct hash_iter {
-  const struct hash_table *table;
-  int index;
-  const void *key;
-  const void *value;
-};
-
-bool hash_get_start_iter(const struct hash_table *h,
-                         struct hash_iter *iter);
-bool hash_iter_next(struct hash_iter *iter);
-void *hash_iter_get_key(struct hash_iter *iter);
-void *hash_iter_get_value(struct hash_iter *iter);
-
-#define hash_iterate(ARG_hash_table, NAME_key, NAME_value) {\
-  struct hash_iter MY_iter;\
-  if (hash_get_start_iter((ARG_hash_table), &MY_iter)) {\
-    void *NAME_key, *NAME_value;\
-    bool MY_more_items = TRUE;\
-    while (MY_more_items) {\
-      NAME_key = hash_iter_get_key(&MY_iter);\
-      NAME_value = hash_iter_get_value(&MY_iter);\
-      MY_more_items = hash_iter_next(&MY_iter);
-
-#define hash_iterate_end } } }
+#include "iterator.h"
+
+struct iterator *hash_get_key_iter(const struct hash_table *h);
+#define hash_keys_iterate(ARG_ht, NAME_key)\
+  generic_iterate(hash_get_key_iter(ARG_ht), void *, NAME_key)
+#define hash_keys_iterate_end generic_iterate_end
+
+struct iterator *hash_get_value_iter(const struct hash_table *h);
+#define hash_values_iterate(ARG_ht, NAME_value)\
+  generic_iterate(hash_get_value_iter(ARG_ht), void *, NAME_value)
+#define hash_values_iterate_end generic_iterate_end
+
+struct iterator *hash_get_iter(const struct hash_table *h);
+void *hash_iter_get_key(const struct iterator *hash_iter);
+void *hash_iter_get_value(const struct iterator *hash_iter);
+#define hash_iterate(ARG_ht, NAME_iter)\
+  generic_iterate(hash_get_iter(ARG_ht), struct iterator *, NAME_iter)
+#define hash_iterate_end generic_iterate_end
 
 #endif  /* FC__HASH_H */
_______________________________________________
Freeciv-dev mailing list
Freeciv-dev@gna.org
https://mail.gna.org/listinfo/freeciv-dev

Reply via email to