fileinfo: Change the way attribute matchers are created
authorBenjamin Otte <otte@redhat.com>
Tue, 1 Nov 2011 12:58:09 +0000 (13:58 +0100)
committerBenjamin Otte <otte@redhat.com>
Wed, 16 Nov 2011 16:11:52 +0000 (17:11 +0100)
We now sort the matchers and remove unnecessary duplicates (like
removing standard:type when we already match standard:*), so that we can
do more complex operations on them easily in later commits.

gio/gfileinfo.c

index f3f92c3..ec08b0d 100644 (file)
@@ -2102,36 +2102,79 @@ struct _GFileAttributeMatcher {
   gint iterator_pos;
 };
 
-static void
-matcher_add (GFileAttributeMatcher *matcher,
-            guint                  id,
-             guint                  mask)
+G_DEFINE_BOXED_TYPE (GFileAttributeMatcher, g_file_attribute_matcher,
+                     g_file_attribute_matcher_ref,
+                     g_file_attribute_matcher_unref)
+
+static gint
+compare_sub_matchers (gconstpointer a,
+                      gconstpointer b)
 {
-  SubMatcher *sub_matchers;
-  int i;
-  SubMatcher s;
+  const SubMatcher *suba = a;
+  const SubMatcher *subb = b;
+  int diff;
+
+  diff = suba->id - subb->id;
+
+  if (diff)
+    return diff;
+
+  return suba->mask - subb->mask;
+}
+
+static gboolean
+sub_matcher_matches (SubMatcher *matcher,
+                     SubMatcher *submatcher)
+{
+  if ((matcher->mask & submatcher->mask) != matcher->mask)
+    return FALSE;
+  
+  return matcher->id == (submatcher->id & matcher->mask);
+}
 
-  if (matcher->sub_matchers == NULL)
-    matcher->sub_matchers = g_array_new (FALSE, FALSE, sizeof (SubMatcher));
+/* Call this function after modifying a matcher.
+ * It will ensure all the invariants other functions rely on.
+ */
+static void
+matcher_optimize (GFileAttributeMatcher *matcher)
+{
+  SubMatcher *submatcher, *compare;
+  guint i, j;
 
-  sub_matchers = (SubMatcher *)matcher->sub_matchers->data;
-  for (i = 0; i < matcher->sub_matchers->len; i++)
+  /* remove sub_matchers if we match everything anyway */
+  if (matcher->all)
     {
-      /* Already added */
-      if (sub_matchers[i].id == id &&
-         sub_matchers[i].mask == mask)
-       return;
+      if (matcher->sub_matchers)
+        {
+          g_array_free (matcher->sub_matchers, TRUE);
+          matcher->sub_matchers = NULL;
+        }
+      return matcher;
     }
 
-  s.id = id;
-  s.mask = mask;
+  /* sort sub_matchers by id (and then mask), so we can bsearch
+   * and compare matchers in O(N) instead of O(N²) */
+  g_array_sort (matcher->sub_matchers, compare_sub_matchers);
 
-  g_array_append_val (matcher->sub_matchers, s);
-}
+  /* remove duplicates and specific matches when we match the whole namespace */
+  j = 0;
+  compare = &g_array_index (matcher->sub_matchers, SubMatcher, j);
 
-G_DEFINE_BOXED_TYPE (GFileAttributeMatcher, g_file_attribute_matcher,
-                     g_file_attribute_matcher_ref,
-                     g_file_attribute_matcher_unref)
+  for (i = 1; i < matcher->sub_matchers->len; i++)
+    {
+      submatcher = &g_array_index (matcher->sub_matchers, SubMatcher, i);
+      if (sub_matcher_matches (compare, submatcher))
+        continue;
+
+      j++;
+      compare++;
+
+      if (j < i)
+        *compare = *submatcher;
+    }
+
+  g_array_set_size (matcher->sub_matchers, j + 1);
+}
 
 /**
  * g_file_attribute_matcher_new:
@@ -2177,6 +2220,7 @@ g_file_attribute_matcher_new (const char *attributes)
 
   matcher = g_malloc0 (sizeof (GFileAttributeMatcher));
   matcher->ref = 1;
+  matcher->sub_matchers = g_array_new (FALSE, FALSE, sizeof (SubMatcher));
 
   split = g_strsplit (attributes, ",", -1);
 
@@ -2186,7 +2230,7 @@ g_file_attribute_matcher_new (const char *attributes)
        matcher->all = TRUE;
       else
        {
-         guint32 id, mask;
+          SubMatcher s;
 
          colon = strstr (split[i], "::");
          if (colon != NULL &&
@@ -2194,24 +2238,26 @@ g_file_attribute_matcher_new (const char *attributes)
                (colon[2] == '*' &&
                 colon[3] == 0)))
            {
-             id = lookup_attribute (split[i]);
-             mask = 0xffffffff;
+             s.id = lookup_attribute (split[i]);
+             s.mask = 0xffffffff;
            }
          else
            {
              if (colon)
                *colon = 0;
 
-             id = lookup_namespace (split[i]) << NS_POS;
-             mask = NS_MASK << NS_POS;
+             s.id = lookup_namespace (split[i]) << NS_POS;
+             s.mask = NS_MASK << NS_POS;
            }
 
-         matcher_add (matcher, id, mask);
+          g_array_append_val (matcher->sub_matchers, s);
        }
     }
 
   g_strfreev (split);
 
+  matcher_optimize (matcher);
+
   return matcher;
 }