]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
lib-storage: mail_search_args_simplify_drop_redundant_args() - Apply Absorptive law...
authorMarco Bettini <marco.bettini@open-xchange.com>
Wed, 9 Nov 2022 15:29:39 +0000 (15:29 +0000)
committerMarco Bettini <marco.bettini@open-xchange.com>
Wed, 16 Nov 2022 15:03:56 +0000 (15:03 +0000)
src/lib-storage/mail-search-args-simplify.c
src/lib-storage/mail-search.c
src/lib-storage/mail-search.h
src/lib-storage/test-mail-search-args-simplify.c

index 9c86811b13ca6d8d0b10b07aed079cc67489c4b4..2a1cace6d6912e0c2cce8112663e619ea4834b5a 100644 (file)
@@ -383,62 +383,73 @@ mail_search_args_have_all_equal(struct mail_search_arg *parent_arg,
        return TRUE;
 }
 
-static unsigned int
-mail_search_args_count(const struct mail_search_arg *args)
-{
-       unsigned int count;
+/* Absorptive Law - This law enables a reduction in a complicated expression to
+   a simpler one by absorbing like terms.
 
-       for (count = 0; args != NULL; count++)
-               args = args->next;
-       return count;
-}
+   A + (A.B) =  (A.1) + (A.B)  = A(1 + B)  = A  (OR Absorption Law)
+   A(A + B)  = (A + 0).(A + B) = A + (0.B) = A  (AND Absorption Law)
+
+   Cases with multiple shared terms (duals appy as well)
 
+   A + B + (A.C) + (B.C) = (A + (A.C)) + (B + B.C))  (apply law to sides of external sum))
+                         = A + B
+   A + B + (A.B.C) = (A + (A.(B.C))) + B            (X = B.C)
+                   = (A + (A.(X)) + B                (apply law to X)
+                  = A + B
+*/
 static bool
 mail_search_args_simplify_drop_redundant_args(struct mail_search_args *all_args,
                                              struct mail_search_arg **argsp,
                                              bool and_arg)
 {
-       struct mail_search_arg *arg, **argp, one_arg, *lowest_arg = NULL;
+       if (*argsp == NULL || (*argsp)->next == NULL)
+               return FALSE;
+
+       struct mail_search_arg *arg, **argp;
        enum mail_search_arg_type child_subargs_type;
-       unsigned int count, lowest_count = UINT_MAX;
-       bool ret = FALSE;
+       bool changed = FALSE;
 
-       if (*argsp == NULL)
-               return FALSE;
+       ARRAY(const struct mail_search_arg *) candidates;
+       t_array_init(&candidates, 1);
 
        child_subargs_type = and_arg ? SEARCH_OR : SEARCH_SUB;
-
-       /* find the arg which has the lowest number of child args */
        for (arg = *argsp; arg != NULL; arg = arg->next) {
-               if (arg->type != child_subargs_type) {
-                       one_arg = *arg;
-                       one_arg.next = NULL;
-                       lowest_arg = &one_arg;
-                       break;
-               }
-               count = mail_search_args_count(arg->value.subargs);
-               if (count < lowest_count) {
-                       lowest_arg = arg->value.subargs;
-                       lowest_count = count;
+               if (arg->type == child_subargs_type) {
+                       const struct mail_search_arg *entry = arg->value.subargs;
+                       if (entry == NULL ||
+                           array_lsearch(&candidates, &entry,
+                                         mail_search_arg_equals_p) != NULL)
+                               continue;
+                       array_push_back(&candidates, &entry);
+               } else {
+                       struct mail_search_arg *copy = t_new(struct mail_search_arg, 1);
+                       *copy = *arg;
+                       copy->next = NULL;
+                       const struct mail_search_arg *entry = copy;
+                       array_push_back(&candidates, &entry);
                }
        }
-       i_assert(lowest_arg != NULL);
-
-       /* if there are any args that include lowest_arg, drop the arg since
-          it's redundant. (non-SUB duplicates are dropped elsewhere.) */
-       for (argp = argsp; *argp != NULL; ) {
-               if (*argp != lowest_arg && (*argp)->type == child_subargs_type &&
-                   (*argp)->value.subargs != lowest_arg &&
-                   mail_search_args_have_all_equal(*argp, lowest_arg)) {
-                       if (all_args->init_refcount > 0)
-                               mail_search_arg_one_deinit(*argp);
-                       *argp = (*argp)->next;
-                       ret = TRUE;
-               } else {
-                       argp = &(*argp)->next;
+
+       const struct mail_search_arg *candidate;
+       array_foreach_elem(&candidates, candidate) {
+               /* if there are any args that include the candidate - EXCEPT the
+                  one that originally contained it - drop the arg, since it is
+                  redundant. (non-SUB duplicates are dropped elsewhere.) */
+               for (argp = argsp; *argp != NULL; ) {
+                       if (*argp != candidate &&
+                          (*argp)->type == child_subargs_type &&
+                          (*argp)->value.subargs != candidate &&
+                          mail_search_args_have_all_equal(*argp, candidate)) {
+                               if (all_args->init_refcount > 0)
+                                       mail_search_arg_one_deinit(*argp);
+                               *argp = (*argp)->next;
+                               changed = TRUE;
+                       } else {
+                               argp = &(*argp)->next;
+                       }
                }
        }
-       return ret;
+       return changed;
 }
 
 static bool
index 1b14b67d48d433b8f287d4df7299187756b56457..fc426533ecf2f8eaab3746fc7dbb3f4eddfaeae6 100644 (file)
@@ -733,6 +733,14 @@ bool mail_search_arg_equals(const struct mail_search_arg *arg1,
        return arg1 == NULL && arg2 == NULL;
 }
 
+int mail_search_arg_equals_p(const struct mail_search_arg *const *arg1,
+                            const struct mail_search_arg *const *arg2)
+{
+       if (arg1 == NULL && arg2 == NULL) return 0;
+       if (arg1 == NULL) return 1;
+       return mail_search_arg_equals(*arg1, *arg2) ? 0 : 1;
+}
+
 bool mail_search_args_equal(const struct mail_search_args *args1,
                            const struct mail_search_args *args2)
 {
index 2147175b13f47e389ae8d7a78eb62c4143ccc83f..8224e9ef2e5d08642dc296dc74d9ad819ded52b5 100644 (file)
@@ -207,6 +207,8 @@ bool mail_search_args_equal(const struct mail_search_args *args1,
    structs. All the siblings of arg1 and arg2 are also compared. */
 bool mail_search_arg_equals(const struct mail_search_arg *arg1,
                            const struct mail_search_arg *arg2);
+int mail_search_arg_equals_p(const struct mail_search_arg *const *arg1,
+                            const struct mail_search_arg *const *arg2);
 /* Same as mail_search_arg_equals(), but don't compare siblings. */
 bool mail_search_arg_one_equals(const struct mail_search_arg *arg1,
                                const struct mail_search_arg *arg2);
index a8b165b8bf791c10e9ac4f87b010522d459b6422..dcde23665b4011ffebb507b473db6a9afb69c4c8 100644 (file)
@@ -218,6 +218,17 @@ static const struct {
        { "( OR TEXT unique1 TEXT unique2 ) ( OR TEXT unique3 TEXT unique4 )", "OR TEXT unique1 TEXT unique2 OR TEXT unique3 TEXT unique4" },
        { "( OR TEXT common1 TEXT unique1 ) ( OR TEXT common1 TEXT unique2 ) TEXT unique3", "OR TEXT common1 TEXT unique1 OR TEXT common1 TEXT unique2 TEXT unique3" },
        { "( OR TEXT common1 TEXT unique1 ) ( OR TEXT common1 TEXT common2 ) ( OR TEXT common2 TEXT unique2 )", "OR TEXT common1 TEXT unique1 OR TEXT common1 TEXT common2 OR TEXT common2 TEXT unique2" },
+
+       /* extra for simplifications on 2nd and later terms */
+       { "( OR BODY z BODY x ) BODY x BODY y",  "BODY x BODY y" },
+       { "( OR BODY x BODY y ) ( OR BODY x BODY z )",  "OR (BODY y BODY z) BODY x" },
+       { "( OR BODY z BODY x ) BODY x BODY y",  "BODY x BODY y" },
+       { "( OR BODY z NOT BODY x ) BODY x BODY y",  "OR BODY z NOT BODY x BODY x BODY y" },
+
+       { "( OR BODY z BODY y ) BODY x BODY y",  "BODY x BODY y" },
+       { "( OR BODY z BODY y ) ( OR BODY z BODY w ) BODY x BODY y BODY w",  "BODY x BODY y BODY w" },
+
+       { "subject y", "SUBJECT y"},
 };
 
 static struct mail_search_args *