summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMatthias Clasen <mclasen@redhat.com>2020-01-12 13:12:07 -0500
committerMatthias Clasen <mclasen@redhat.com>2020-01-15 11:14:51 -0500
commit436af79fa7efe3d2f1d3dc9804d10966c36b7aca (patch)
treef5f5263866b4d002dcc6b9c625167a0074570c4c
parenta9e599d7202ec263034878bf6bdb6ab973e41e15 (diff)
downloadgtk+-436af79fa7efe3d2f1d3dc9804d10966c36b7aca.tar.gz
css: Faster matching for simple selectors
Inline simple selectors for node matchers. A bit ugly, but works.
-rw-r--r--gtk/gtkcssmatcher.c40
-rw-r--r--gtk/gtkcssmatcherprivate.h18
-rw-r--r--gtk/gtkcssselector.c163
3 files changed, 193 insertions, 28 deletions
diff --git a/gtk/gtkcssmatcher.c b/gtk/gtkcssmatcher.c
index 0fee1ea6c3..a1007c1217 100644
--- a/gtk/gtkcssmatcher.c
+++ b/gtk/gtkcssmatcher.c
@@ -160,14 +160,14 @@ gtk_css_matcher_widget_path_has_position (const GtkCssMatcher *matcher,
}
static const GtkCssMatcherClass GTK_CSS_MATCHER_WIDGET_PATH = {
+ GTK_CSS_MATCHER_TYPE_WIDGET_PATH,
gtk_css_matcher_widget_path_get_parent,
gtk_css_matcher_widget_path_get_previous,
gtk_css_matcher_widget_path_get_state,
gtk_css_matcher_widget_path_has_name,
gtk_css_matcher_widget_path_has_class,
gtk_css_matcher_widget_path_has_id,
- gtk_css_matcher_widget_path_has_position,
- FALSE
+ gtk_css_matcher_widget_path_has_position
};
gboolean
@@ -336,14 +336,14 @@ gtk_css_matcher_node_has_position (const GtkCssMatcher *matcher,
}
static const GtkCssMatcherClass GTK_CSS_MATCHER_NODE = {
+ GTK_CSS_MATCHER_TYPE_NODE,
gtk_css_matcher_node_get_parent,
gtk_css_matcher_node_get_previous,
gtk_css_matcher_node_get_state,
gtk_css_matcher_node_has_name,
gtk_css_matcher_node_has_class,
gtk_css_matcher_node_has_id,
- gtk_css_matcher_node_has_position,
- FALSE
+ gtk_css_matcher_node_has_position
};
void
@@ -421,14 +421,14 @@ gtk_css_matcher_any_has_position (const GtkCssMatcher *matcher,
}
static const GtkCssMatcherClass GTK_CSS_MATCHER_ANY = {
+ GTK_CSS_MATCHER_TYPE_ANY,
gtk_css_matcher_any_get_parent,
gtk_css_matcher_any_get_previous,
gtk_css_matcher_any_get_state,
gtk_css_matcher_any_has_name,
gtk_css_matcher_any_has_class,
gtk_css_matcher_any_has_id,
- gtk_css_matcher_any_has_position,
- TRUE
+ gtk_css_matcher_any_has_position
};
void
@@ -496,15 +496,15 @@ gtk_css_matcher_superset_has_position (const GtkCssMatcher *matcher,
return TRUE;
}
-static const GtkCssMatcherClass GTK_CSS_MATCHER_SUPERSET2 = {
+static const GtkCssMatcherClass GTK_CSS_MATCHER_SUPERSET = {
+ GTK_CSS_MATCHER_TYPE_SUPERSET,
gtk_css_matcher_superset_get_parent,
gtk_css_matcher_superset_get_previous,
gtk_css_matcher_superset_get_state,
gtk_css_matcher_superset_has_name,
gtk_css_matcher_superset_has_class,
gtk_css_matcher_superset_has_id,
- gtk_css_matcher_superset_has_position,
- FALSE
+ gtk_css_matcher_superset_has_position
};
void
@@ -516,8 +516,27 @@ _gtk_css_matcher_superset_init (GtkCssMatcher *matcher,
g_return_if_fail (subset != NULL);
g_return_if_fail ((relevant & ~(GTK_CSS_CHANGE_CLASS | GTK_CSS_CHANGE_NAME | GTK_CSS_CHANGE_POSITION | GTK_CSS_CHANGE_STATE)) == 0);
- *klass = GTK_CSS_MATCHER_SUPERSET2;
+ switch (subset->klass->matcher_type)
+ {
+ case GTK_CSS_MATCHER_TYPE_NODE:
+ matcher->node = subset->node;
+ if (!(relevant & GTK_CSS_CHANGE_STATE))
+ matcher->node.node_state = gtk_css_matcher_superset_get_state (matcher);
+ break;
+ case GTK_CSS_MATCHER_TYPE_WIDGET_PATH:
+ matcher->path = subset->path;
+ break;
+ case GTK_CSS_MATCHER_TYPE_ANY:
+ break;
+ case GTK_CSS_MATCHER_TYPE_SUPERSET:
+ default:
+ g_assert_not_reached ();
+ break;
+ }
+
+ *klass = GTK_CSS_MATCHER_SUPERSET;
+ klass->matcher_type = subset->klass->matcher_type;
if (relevant & GTK_CSS_CHANGE_CLASS)
klass->has_class = subset->klass->has_class;
if (relevant & GTK_CSS_CHANGE_NAME)
@@ -529,6 +548,5 @@ _gtk_css_matcher_superset_init (GtkCssMatcher *matcher,
if (relevant & GTK_CSS_CHANGE_STATE)
klass->get_state = subset->klass->get_state;
- *matcher = *subset;
matcher->klass = klass;
}
diff --git a/gtk/gtkcssmatcherprivate.h b/gtk/gtkcssmatcherprivate.h
index 6f5e9d195c..89c889bd61 100644
--- a/gtk/gtkcssmatcherprivate.h
+++ b/gtk/gtkcssmatcherprivate.h
@@ -29,7 +29,15 @@ typedef struct _GtkCssMatcherSuperset GtkCssMatcherSuperset;
typedef struct _GtkCssMatcherWidgetPath GtkCssMatcherWidgetPath;
typedef struct _GtkCssMatcherClass GtkCssMatcherClass;
+typedef enum {
+ GTK_CSS_MATCHER_TYPE_WIDGET_PATH,
+ GTK_CSS_MATCHER_TYPE_NODE,
+ GTK_CSS_MATCHER_TYPE_ANY,
+ GTK_CSS_MATCHER_TYPE_SUPERSET
+} GtkCssMatcherType;
+
struct _GtkCssMatcherClass {
+ GtkCssMatcherType matcher_type;
gboolean (* get_parent) (GtkCssMatcher *matcher,
const GtkCssMatcher *child);
gboolean (* get_previous) (GtkCssMatcher *matcher,
@@ -46,7 +54,6 @@ struct _GtkCssMatcherClass {
gboolean forward,
int a,
int b);
- gboolean is_any;
};
struct _GtkCssMatcherWidgetPath {
@@ -67,17 +74,10 @@ struct _GtkCssMatcherNode {
guint n_classes;
};
-struct _GtkCssMatcherSuperset {
- const GtkCssMatcherClass *klass;
- const GtkCssMatcher *subset;
- GtkCssChange relevant;
-};
-
union _GtkCssMatcher {
const GtkCssMatcherClass *klass;
GtkCssMatcherWidgetPath path;
GtkCssMatcherNode node;
- GtkCssMatcherSuperset superset;
};
gboolean _gtk_css_matcher_init (GtkCssMatcher *matcher,
@@ -145,7 +145,7 @@ _gtk_css_matcher_has_position (const GtkCssMatcher *matcher,
static inline gboolean
_gtk_css_matcher_matches_any (const GtkCssMatcher *matcher)
{
- return matcher->klass->is_any;
+ return matcher->klass->matcher_type == GTK_CSS_MATCHER_TYPE_ANY;
}
diff --git a/gtk/gtkcssselector.c b/gtk/gtkcssselector.c
index 2b7461e5eb..b1f7d3350c 100644
--- a/gtk/gtkcssselector.c
+++ b/gtk/gtkcssselector.c
@@ -1803,6 +1803,91 @@ gtk_css_selectors_skip_initial_selector (GtkCssSelector *selector, const GtkCssS
return (GtkCssSelector *)gtk_css_selector_previous (selector);
}
+static inline gboolean
+match_node_name (const GtkCssMatcher *matcher,
+ const char *name)
+{
+ return matcher->node.node_name == name;
+}
+
+static inline gboolean
+match_node_id (const GtkCssMatcher *matcher,
+ const char *id)
+{
+ return matcher->node.node_id == id;
+}
+
+static inline gboolean
+match_node_class (const GtkCssMatcher *matcher,
+ GQuark class_name)
+{
+ const GQuark *classes = matcher->node.classes;
+
+ switch (matcher->node.n_classes)
+ {
+ case 3:
+ if (classes[2] == class_name)
+ return TRUE;
+ G_GNUC_FALLTHROUGH;
+
+ case 2:
+ if (classes[1] == class_name)
+ return TRUE;
+ G_GNUC_FALLTHROUGH;
+
+ case 1:
+ if (classes[0] == class_name)
+ return TRUE;
+ G_GNUC_FALLTHROUGH;
+
+ case 0:
+ return FALSE;
+
+ default:
+ return gtk_css_node_has_class (matcher->node.node, class_name);
+ }
+
+ return FALSE;
+}
+
+static inline gboolean
+match_node_state (const GtkCssMatcher *matcher,
+ GtkStateFlags state)
+{
+ return (matcher->node.node_state & state) == state;
+}
+
+static inline gboolean
+gtk_css_selector_match_node (const GtkCssSelector *selector,
+ const GtkCssMatcher *matcher)
+{
+ if (selector->class == &GTK_CSS_SELECTOR_NAME)
+ return match_node_name (matcher, selector->name.name);
+
+ if (selector->class == &GTK_CSS_SELECTOR_CLASS)
+ return match_node_class (matcher, selector->style_class.style_class);
+
+ if (selector->class == &GTK_CSS_SELECTOR_PSEUDOCLASS_STATE)
+ return match_node_state (matcher, selector->state.state);
+
+ if (selector->class == &GTK_CSS_SELECTOR_ID)
+ return match_node_id (matcher, selector->id.name);
+
+ if (selector->class == &GTK_CSS_SELECTOR_NOT_NAME)
+ return !match_node_name (matcher, selector->name.name);
+
+ if (selector->class == &GTK_CSS_SELECTOR_NOT_CLASS)
+ return !match_node_class (matcher, selector->style_class.style_class);
+
+ if (selector->class == &GTK_CSS_SELECTOR_NOT_PSEUDOCLASS_STATE)
+ return !match_node_state (matcher, selector->state.state);
+
+ if (selector->class == &GTK_CSS_SELECTOR_NOT_ID)
+ return !match_node_id (matcher, selector->id.name);
+
+ return gtk_css_selector_match (selector, matcher);
+}
+
static gboolean
gtk_css_selector_tree_match_foreach (const GtkCssSelector *selector,
const GtkCssMatcher *matcher,
@@ -1824,15 +1909,42 @@ gtk_css_selector_tree_match_foreach (const GtkCssSelector *selector,
return FALSE;
}
+static gboolean
+gtk_css_selector_tree_match_node_foreach (const GtkCssSelector *selector,
+ const GtkCssMatcher *matcher,
+ gpointer res)
+{
+ const GtkCssSelectorTree *tree = (const GtkCssSelectorTree *) selector;
+ const GtkCssSelectorTree *prev;
+
+ if (!gtk_css_selector_match_node (selector, matcher))
+ return FALSE;
+
+ gtk_css_selector_tree_found_match (tree, res);
+
+ 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, matcher, gtk_css_selector_tree_match_node_foreach, res);
+
+ return FALSE;
+}
+
GPtrArray *
_gtk_css_selector_tree_match_all (const GtkCssSelectorTree *tree,
const GtkCssMatcher *matcher)
{
GPtrArray *array = NULL;
+ GtkCssSelectorForeachFunc func;
+
+ if (matcher->klass->matcher_type == GTK_CSS_MATCHER_TYPE_NODE)
+ func = gtk_css_selector_tree_match_node_foreach;
+ else
+ func = gtk_css_selector_tree_match_foreach;
for (; tree != NULL;
- tree = gtk_css_selector_tree_get_sibling (tree))
- gtk_css_selector_foreach (&tree->selector, matcher, gtk_css_selector_tree_match_foreach, &array);
+ tree = gtk_css_selector_tree_get_sibling (tree))
+ gtk_css_selector_foreach (&tree->selector, matcher, func, &array);
return array;
}
@@ -1868,13 +1980,14 @@ static GtkCssChange
gtk_css_selector_tree_get_change (const GtkCssSelectorTree *tree,
const GtkCssMatcher *matcher)
{
+ const GtkCssSelector *selector = &tree->selector;
GtkCssChange change = 0;
const GtkCssSelectorTree *prev;
- if (!gtk_css_selector_match (&tree->selector, matcher))
+ if (!gtk_css_selector_match (selector, matcher))
return 0;
- if (!tree->selector.class->is_simple)
+ if (!selector->class->is_simple)
return tree->change | GTK_CSS_CHANGE_GOT_MATCH;
for (prev = gtk_css_selector_tree_get_previous (tree);
@@ -1883,7 +1996,32 @@ gtk_css_selector_tree_get_change (const GtkCssSelectorTree *tree,
change |= gtk_css_selector_tree_get_change (prev, matcher);
if (change || gtk_css_selector_tree_get_matches (tree))
- change = tree->selector.class->get_change (&tree->selector, change & ~GTK_CSS_CHANGE_GOT_MATCH) | GTK_CSS_CHANGE_GOT_MATCH;
+ change = tree->selector.class->get_change (selector, change & ~GTK_CSS_CHANGE_GOT_MATCH) | GTK_CSS_CHANGE_GOT_MATCH;
+
+ return change;
+}
+
+static GtkCssChange
+gtk_css_selector_tree_get_change_node (const GtkCssSelectorTree *tree,
+ const GtkCssMatcher *matcher)
+{
+ const GtkCssSelector *selector = &tree->selector;
+ GtkCssChange change = 0;
+ const GtkCssSelectorTree *prev;
+
+ if (!gtk_css_selector_match_node (selector, matcher))
+ return 0;
+
+ if (!selector->class->is_simple)
+ return tree->change | GTK_CSS_CHANGE_GOT_MATCH;
+
+ for (prev = gtk_css_selector_tree_get_previous (tree);
+ prev != NULL;
+ prev = gtk_css_selector_tree_get_sibling (prev))
+ change |= gtk_css_selector_tree_get_change_node (prev, matcher);
+
+ if (change || gtk_css_selector_tree_get_matches (tree))
+ change = tree->selector.class->get_change (selector, change & ~GTK_CSS_CHANGE_GOT_MATCH) | GTK_CSS_CHANGE_GOT_MATCH;
return change;
}
@@ -1903,9 +2041,18 @@ _gtk_css_selector_tree_get_change_all (const GtkCssSelectorTree *tree,
change = 0;
/* no need to foreach here because we abort for non-simple selectors */
- for (; tree != NULL;
- tree = gtk_css_selector_tree_get_sibling (tree))
- change |= gtk_css_selector_tree_get_change (tree, matcher);
+ if (matcher->klass->matcher_type == GTK_CSS_MATCHER_TYPE_NODE)
+ {
+ for (; tree != NULL;
+ tree = gtk_css_selector_tree_get_sibling (tree))
+ change |= gtk_css_selector_tree_get_change_node (tree, matcher);
+ }
+ else
+ {
+ for (; tree != NULL;
+ tree = gtk_css_selector_tree_get_sibling (tree))
+ change |= gtk_css_selector_tree_get_change (tree, matcher);
+ }
/* Never return reserved bit set */
return change & ~GTK_CSS_CHANGE_RESERVED_BIT;