[gtk/wip/otte/css] selector: Rework iterating over subnodes



commit c7dca199ae344dca6914f4a534652a4aa8a99fda
Author: Benjamin Otte <otte redhat com>
Date:   Tue Jan 28 03:53:48 2020 +0100

    selector: Rework iterating over subnodes
    
    Instead of a foreach() function, introduce an iterator, so that the
    caller can drive the iteration.
    
    This allows doing stuff inbetween callbacks and avoids closures when
    more than one data object should be passed.
    
    As a side effect I even get a small, but noticeable performance
    improvement in the 2-10% range depending on benchmark, I guess that's
    because there's no function pointer passing going on anymore.

 gtk/gtkcssprovider.c        |   2 +-
 gtk/gtkcssselector.c        | 276 ++++++++++++++++++++------------------------
 gtk/gtkcssselectorprivate.h |   2 +-
 3 files changed, 126 insertions(+), 154 deletions(-)
---
diff --git a/gtk/gtkcssprovider.c b/gtk/gtkcssprovider.c
index f1eafed695..6db8d88ba6 100644
--- a/gtk/gtkcssprovider.c
+++ b/gtk/gtkcssprovider.c
@@ -415,7 +415,7 @@ verify_tree_match_results (GtkCssProvider *provider,
              break;
            }
        }
-      should_match = _gtk_css_selector_matches (ruleset->selector, node);
+      should_match = gtk_css_selector_matches (ruleset->selector, node);
       if (found != !!should_match)
        {
          g_error ("expected rule '%s' to %s, but it %s",
diff --git a/gtk/gtkcssselector.c b/gtk/gtkcssselector.c
index dfb002e32b..b08e3c7a2b 100644
--- a/gtk/gtkcssselector.c
+++ b/gtk/gtkcssselector.c
@@ -48,37 +48,37 @@ typedef enum {
 } GtkCssSelectorCategory;
 
 typedef struct _GtkCssSelectorClass GtkCssSelectorClass;
-typedef gboolean (* GtkCssSelectorForeachFunc) (const GtkCssSelector *selector,
-                                                GtkCssNode           *node,
-                                                gpointer              data);
 
 struct _GtkCssSelectorClass {
   const char        *name;
   GtkCssSelectorCategory category;
 
-  void              (* print)       (const GtkCssSelector       *selector,
-                                     GString                    *string);
-  /* NULL or an iterator that calls func with each subnode of @node.
-   * Potentially no subnode exists.
-   * If any @invocation of @func returns %TRUE, the function will immediately
-   * return %TRUE itself. If @func never returns %TRUE (or isn't called at all),
-   * %FALSE will be returned.
+  void              (* print)                   (const GtkCssSelector       *selector,
+                                                 GString                    *string);
+  /* NULL or an iterator that returns the next node or %NULL if there are no
+   * more nodes.
+   * Call it like:
+   * for (iter = gtk_css_selector_iterator (node, NULL);
+   *      iter;
+   *      iter = gtk_css_selector_iterator (node, iter))
+   *   {
+   *     do_stuff();
+   *   }
    */
-  gboolean          (* foreach_matcher)  (const GtkCssSelector       *selector,
-                                          GtkCssNode                 *node,
-                                          GtkCssSelectorForeachFunc   func,
-                                          gpointer                    data);
-  gboolean          (* match_one)   (const GtkCssSelector       *selector,
-                                     GtkCssNode                 *node);
-  GtkCssChange      (* get_change)  (const GtkCssSelector       *selector,
-                                    GtkCssChange                previous_change);
-  void              (* add_specificity)  (const GtkCssSelector  *selector,
-                                          guint                 *ids,
-                                          guint                 *classes,
-                                          guint                 *elements);
-  guint             (* hash_one)    (const GtkCssSelector       *selector);
-  int               (* compare_one) (const GtkCssSelector       *a,
-                                    const GtkCssSelector       *b);
+  GtkCssNode *      (* iterator)                (const GtkCssSelector       *selector,
+                                                 GtkCssNode                 *node,
+                                                 GtkCssNode                 *current);
+  gboolean          (* match_one)               (const GtkCssSelector       *selector,
+                                                 GtkCssNode                 *node);
+  GtkCssChange      (* get_change)              (const GtkCssSelector       *selector,
+                                                GtkCssChange                previous_change);
+  void              (* add_specificity)         (const GtkCssSelector       *selector,
+                                                 guint                      *ids,
+                                                 guint                      *classes,
+                                                 guint                      *elements);
+  guint             (* hash_one)                (const GtkCssSelector       *selector);
+  int               (* compare_one)             (const GtkCssSelector       *a,
+                                                const GtkCssSelector       *b);
 };
 
 typedef enum {
@@ -169,19 +169,18 @@ g_ptr_array_insert_sorted (GPtrArray *array,
 }
 
 static inline gboolean
-gtk_css_selector_match (const GtkCssSelector *selector,
-                        GtkCssNode           *node)
+gtk_css_selector_match_one (const GtkCssSelector *selector,
+                            GtkCssNode           *node)
 {
   return selector->class->match_one (selector, node);
 }
 
-static inline gboolean
-gtk_css_selector_foreach (const GtkCssSelector      *selector,
-                          GtkCssNode                *node,
-                          GtkCssSelectorForeachFunc  func,
-                          gpointer                   data)
+static inline GtkCssNode *
+gtk_css_selector_iterator (const GtkCssSelector *selector,
+                           GtkCssNode           *node,
+                           GtkCssNode           *current)
 {
-  return selector->class->foreach_matcher (selector, node, func, data);
+  return selector->class->iterator (selector, node, current);
 }
 
 static int
@@ -240,13 +239,15 @@ gtk_css_selector_default_add_specificity (const GtkCssSelector *selector,
   /* no specificity changes */
 }
  
-static gboolean
-gtk_css_selector_default_foreach_matcher (const GtkCssSelector       *selector,
-                                          GtkCssNode                 *node,
-                                          GtkCssSelectorForeachFunc   func,
-                                          gpointer                    data)
+static GtkCssNode *
+gtk_css_selector_default_iterator (const GtkCssSelector *selector,
+                                   GtkCssNode           *node,
+                                   GtkCssNode           *current)
 {
-  return func (selector, node, data);
+  if (current)
+    return NULL;
+  else
+    return node;
 }
 
 static gboolean
@@ -278,23 +279,12 @@ gtk_css_selector_descendant_print (const GtkCssSelector *selector,
   g_string_append_c (string, ' ');
 }
 
-static gboolean
-gtk_css_selector_descendant_foreach_matcher (const GtkCssSelector      *selector,
-                                             GtkCssNode                *node,
-                                             GtkCssSelectorForeachFunc  func,
-                                             gpointer                   data)
+static GtkCssNode *
+gtk_css_selector_descendant_iterator (const GtkCssSelector *selector,
+                                      GtkCssNode           *node,
+                                      GtkCssNode           *current)
 {
-  GtkCssNode *parent;
-
-  for (parent = gtk_css_node_get_parent (node);
-       parent;
-       parent = gtk_css_node_get_parent (parent))
-    {
-      if (func (selector, parent, data))
-        return TRUE;
-    }
-
-  return FALSE;
+  return gtk_css_node_get_parent (current ? current : node);
 }
 
 static GtkCssChange
@@ -307,7 +297,7 @@ static const GtkCssSelectorClass GTK_CSS_SELECTOR_DESCENDANT = {
   "descendant",
   GTK_CSS_SELECTOR_CATEGORY_PARENT,
   gtk_css_selector_descendant_print,
-  gtk_css_selector_descendant_foreach_matcher,
+  gtk_css_selector_descendant_iterator,
   gtk_css_selector_default_match_one,
   gtk_css_selector_descendant_get_change,
   gtk_css_selector_default_add_specificity,
@@ -324,19 +314,15 @@ gtk_css_selector_child_print (const GtkCssSelector *selector,
   g_string_append (string, " > ");
 }
 
-static gboolean
-gtk_css_selector_child_foreach_matcher (const GtkCssSelector      *selector,
-                                        GtkCssNode                *node,
-                                        GtkCssSelectorForeachFunc  func,
-                                        gpointer                   data)
+static GtkCssNode *
+gtk_css_selector_child_iterator (const GtkCssSelector *selector,
+                                 GtkCssNode           *node,
+                                 GtkCssNode           *current)
 {
-  GtkCssNode *parent;
-
-  parent = gtk_css_node_get_parent (node);
-  if (parent == NULL)
-    return FALSE;
+  if (current == NULL)
+    return gtk_css_node_get_parent (node);
 
-  return func (selector, parent, data);
+  return NULL;
 }
 
 static GtkCssChange
@@ -349,7 +335,7 @@ static const GtkCssSelectorClass GTK_CSS_SELECTOR_CHILD = {
   "child",
   GTK_CSS_SELECTOR_CATEGORY_PARENT,
   gtk_css_selector_child_print,
-  gtk_css_selector_child_foreach_matcher,
+  gtk_css_selector_child_iterator,
   gtk_css_selector_default_match_one,
   gtk_css_selector_child_get_change,
   gtk_css_selector_default_add_specificity,
@@ -386,23 +372,12 @@ get_next_visible_sibling (GtkCssNode *node)
   return node;
 }
 
-static gboolean
-gtk_css_selector_sibling_foreach_matcher (const GtkCssSelector      *selector,
-                                          GtkCssNode                *node,
-                                          GtkCssSelectorForeachFunc  func,
-                                          gpointer                   data)
+static GtkCssNode *
+gtk_css_selector_sibling_iterator (const GtkCssSelector *selector,
+                                   GtkCssNode           *node,
+                                   GtkCssNode           *current)
 {
-  GtkCssNode *prev;
-
-  for (prev = get_previous_visible_sibling (node);
-       prev;
-       prev = get_previous_visible_sibling (prev))
-    {
-      if (func (selector, prev, data))
-        return TRUE;
-    }
-
-  return FALSE;
+  return get_previous_visible_sibling (current ? current : node);
 }
 
 static GtkCssChange
@@ -415,7 +390,7 @@ static const GtkCssSelectorClass GTK_CSS_SELECTOR_SIBLING = {
   "sibling",
   GTK_CSS_SELECTOR_CATEGORY_SIBLING,
   gtk_css_selector_sibling_print,
-  gtk_css_selector_sibling_foreach_matcher,
+  gtk_css_selector_sibling_iterator,
   gtk_css_selector_default_match_one,
   gtk_css_selector_sibling_get_change,
   gtk_css_selector_default_add_specificity,
@@ -432,19 +407,15 @@ gtk_css_selector_adjacent_print (const GtkCssSelector *selector,
   g_string_append (string, " + ");
 }
 
-static gboolean
-gtk_css_selector_adjacent_foreach_matcher (const GtkCssSelector      *selector,
-                                           GtkCssNode                *node,
-                                           GtkCssSelectorForeachFunc  func,
-                                           gpointer                   data)
+static GtkCssNode *
+gtk_css_selector_adjacent_iterator (const GtkCssSelector *selector,
+                                    GtkCssNode           *node,
+                                    GtkCssNode           *current)
 {
-  GtkCssNode *prev;
-
-  prev = get_previous_visible_sibling (node);
-  if (prev == NULL)
-    return FALSE;
+  if (current == NULL)
+    return get_previous_visible_sibling (node);
 
-  return func (selector, prev, data);
+  return NULL;
 }
 
 static GtkCssChange
@@ -457,7 +428,7 @@ static const GtkCssSelectorClass GTK_CSS_SELECTOR_ADJACENT = {
   "adjacent",
   GTK_CSS_SELECTOR_CATEGORY_SIBLING,
   gtk_css_selector_adjacent_print,
-  gtk_css_selector_adjacent_foreach_matcher,
+  gtk_css_selector_adjacent_iterator,
   gtk_css_selector_default_match_one,
   gtk_css_selector_adjacent_get_change,
   gtk_css_selector_default_add_specificity,
@@ -530,7 +501,7 @@ static const GtkCssSelectorClass GTK_CSS_SELECTOR_ ## c = { \
   G_STRINGIFY(n), \
   ignore_for_change ? GTK_CSS_SELECTOR_CATEGORY_SIMPLE : GTK_CSS_SELECTOR_CATEGORY_SIMPLE_RADICAL, \
   gtk_css_selector_ ## n ## _print, \
-  gtk_css_selector_default_foreach_matcher, \
+  gtk_css_selector_default_iterator, \
   match_func, \
   gtk_css_selector_ ## n ## _get_change, \
   gtk_css_selector_ ## n ## _add_specificity, \
@@ -542,7 +513,7 @@ static const GtkCssSelectorClass GTK_CSS_SELECTOR_NOT_ ## c = { \
   "not_" G_STRINGIFY(n), \
   GTK_CSS_SELECTOR_CATEGORY_SIMPLE, \
   gtk_css_selector_not_ ## n ## _print, \
-  gtk_css_selector_default_foreach_matcher, \
+  gtk_css_selector_default_iterator, \
   gtk_css_selector_not_ ## n ## _match_one, \
   gtk_css_selector_ ## n ## _get_change, \
   gtk_css_selector_ ## n ## _add_specificity, \
@@ -1675,24 +1646,8 @@ _gtk_css_selector_to_string (const GtkCssSelector *selector)
   return g_string_free (string, FALSE);
 }
 
-static gboolean
-gtk_css_selector_foreach_match (const GtkCssSelector *selector,
-                                GtkCssNode           *node,
-                                gpointer              unused)
-{
-  selector = gtk_css_selector_previous (selector);
-
-  if (selector == NULL)
-    return TRUE;
-
-  if (!gtk_css_selector_match (selector, node))
-    return FALSE;
-
-  return gtk_css_selector_foreach (selector, node, gtk_css_selector_foreach_match, NULL);
-}
-
 /**
- * _gtk_css_selector_matches:
+ * gtk_css_selector_matches:
  * @selector: the selector
  * @node: The node to match
  *
@@ -1701,17 +1656,31 @@ gtk_css_selector_foreach_match (const GtkCssSelector *selector,
  * Returns: %TRUE if the selector matches @node
  **/
 gboolean
-_gtk_css_selector_matches (const GtkCssSelector *selector,
-                           GtkCssNode           *node)
+gtk_css_selector_matches (const GtkCssSelector *selector,
+                          GtkCssNode           *node)
 {
+  GtkCssNode *child;
+  const GtkCssSelector *prev;
 
   g_return_val_if_fail (selector != NULL, FALSE);
   g_return_val_if_fail (node != NULL, FALSE);
 
-  if (!gtk_css_selector_match (selector, node))
+  if (!gtk_css_selector_match_one (selector, node))
     return FALSE;
 
-  return gtk_css_selector_foreach (selector, node, gtk_css_selector_foreach_match, NULL);
+  prev = gtk_css_selector_previous (selector);
+  if (prev == NULL)
+    return TRUE;
+
+  for (child = gtk_css_selector_iterator (selector, node, NULL);
+       child;
+       child = gtk_css_selector_iterator (selector, node, child))
+   {
+     if (gtk_css_selector_matches (prev, child))
+       return TRUE;
+   }
+
+  return FALSE;
 }
 
 /* Computes specificity according to CSS 2.1.
@@ -1889,58 +1858,59 @@ gtk_css_selector_tree_match_bloom (const GtkCssSelectorTree     *tree,
   return FALSE;
 }
 
-typedef struct
-{
-  const GtkCountingBloomFilter *filter;
-  GPtrArray *results;
-} TreeMatchData;
-
 static void
-gtk_css_selector_tree_found_match (const GtkCssSelectorTree *tree,
-                                  TreeMatchData            *tm)
+gtk_css_selector_tree_found_match (const GtkCssSelectorTree  *tree,
+                                  GPtrArray                **results)
 {
   int i;
   gpointer *matches;
 
   matches = gtk_css_selector_tree_get_matches (tree);
-  if (matches)
-    {
-      if (tm->results == NULL)
-        tm->results = g_ptr_array_sized_new (16);
+  if (!matches)
+    return;
 
-      for (i = 0; matches[i] != NULL; i++)
-        g_ptr_array_insert_sorted (tm->results, matches[i]);
-    }
+  if (*results == NULL)
+    *results = g_ptr_array_sized_new (16);
+
+  for (i = 0; matches[i] != NULL; i++)
+    g_ptr_array_insert_sorted (*results, matches[i]);
 }
 
-static gboolean
-gtk_css_selector_tree_match_foreach (const GtkCssSelector *selector,
-                                     GtkCssNode           *node,
-                                     gpointer              data)
+static void
+gtk_css_selector_tree_match (const GtkCssSelectorTree      *tree,
+                             const GtkCountingBloomFilter  *filter,
+                             GtkCssNode                    *node,
+                             GPtrArray                    **results)
 {
-  TreeMatchData *tm = data;
-  const GtkCssSelectorTree *tree = (const GtkCssSelectorTree *) selector;
   const GtkCssSelectorTree *prev;
+  GtkCssNode *child;
 
-  if (!gtk_css_selector_match (selector, node))
-    return FALSE;
+  if (!gtk_css_selector_match_one (&tree->selector, node))
+    return;
 
-  gtk_css_selector_tree_found_match (tree, tm);
+  gtk_css_selector_tree_found_match (tree, results);
 
-  if (tm->filter && !gtk_css_selector_is_simple (&tree->selector))
+  if (filter && !gtk_css_selector_is_simple (&tree->selector))
     {
       /* We can pass both TRUE or FALSE for skipping here, because the
        * function will immediately update it. */
-      if (!gtk_css_selector_tree_match_bloom (tree, tm->filter, FALSE))
-          return FALSE;
+      if (!gtk_css_selector_tree_match_bloom (tree, filter, FALSE))
+          return;
     }
 
   for (prev = gtk_css_selector_tree_get_previous (tree);
        prev != NULL;
        prev = gtk_css_selector_tree_get_sibling (prev))
-    gtk_css_selector_foreach (&prev->selector, node, gtk_css_selector_tree_match_foreach, tm);
+    {
+      for (child = gtk_css_selector_iterator (&tree->selector, node, NULL);
+           child;
+           child = gtk_css_selector_iterator (&tree->selector, node, child))
+        {
+          gtk_css_selector_tree_match (prev, filter, child, results);
+        }
+    }
 
-  return FALSE;
+  return;
 }
 
 GPtrArray *
@@ -1948,13 +1918,15 @@ _gtk_css_selector_tree_match_all (const GtkCssSelectorTree     *tree,
                                   const GtkCountingBloomFilter *filter,
                                   GtkCssNode                   *node)
 {
-  TreeMatchData tm = { filter, NULL };
+  GPtrArray *results = NULL;
 
   for (; tree != NULL;
        tree = gtk_css_selector_tree_get_sibling (tree))
-    gtk_css_selector_foreach (&tree->selector, node, gtk_css_selector_tree_match_foreach, &tm);
+    {
+      gtk_css_selector_tree_match (tree, filter, node, &results);
+    }
 
-  return tm.results;
+  return results;
 }
 
 /* When checking for changes via the tree we need to know if a rule further
diff --git a/gtk/gtkcssselectorprivate.h b/gtk/gtkcssselectorprivate.h
index 4d8e39f376..e351beb915 100644
--- a/gtk/gtkcssselectorprivate.h
+++ b/gtk/gtkcssselectorprivate.h
@@ -35,7 +35,7 @@ char *            _gtk_css_selector_to_string       (const GtkCssSelector   *sel
 void              _gtk_css_selector_print           (const GtkCssSelector   *selector,
                                                      GString                *str);
 
-gboolean          _gtk_css_selector_matches         (const GtkCssSelector   *selector,
+gboolean          gtk_css_selector_matches          (const GtkCssSelector   *selector,
                                                     GtkCssNode             *node);
 GtkCssChange      _gtk_css_selector_get_change      (const GtkCssSelector   *selector);
 int               _gtk_css_selector_compare         (const GtkCssSelector   *a,


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]