]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
lib: Add str_begins_suffix()
authorTimo Sirainen <timo.sirainen@open-xchange.com>
Fri, 10 Apr 2020 16:56:14 +0000 (19:56 +0300)
committertimo.sirainen <timo.sirainen@open-xchange.com>
Wed, 23 Mar 2022 10:25:06 +0000 (10:25 +0000)
src/lib/strfuncs.c
src/lib/strfuncs.h
src/lib/test-strfuncs.c

index 56e45765badea545fe3a7a61321937cc6693784c..32da9b0140ca05f0332a24cb953ebbcc48e554d5 100644 (file)
@@ -633,6 +633,16 @@ str_match(const char *p1, const char *p2)
        return i;
 }
 
+#undef str_begins_suffix
+bool str_begins_suffix(const char *haystack, const char *needle, const char **suffix_r)
+{
+       size_t prefix_len = str_match(haystack, needle);
+       if (needle[prefix_len] != '\0')
+               return FALSE;
+       *suffix_r = haystack + prefix_len;
+       return TRUE;
+}
+
 size_t i_memspn(const void *data, size_t data_len,
                const void *accept, size_t accept_len)
 {
index c338bf8798634791f9eb268be52717cede0afbce..0042991de2ec98d03303379bac3887302860dd74 100644 (file)
@@ -87,6 +87,8 @@ bool mem_equals_timing_safe(const void *p1, const void *p2, size_t size);
 bool str_equals_timing_almost_safe(const char *s1, const char *s2);
 
 size_t str_match(const char *p1, const char *p2) ATTR_PURE;
+bool str_begins_suffix(const char *haystack, const char *needle,
+                      const char **suffix_r);
 static inline ATTR_PURE bool
 str_begins_with(const char *haystack, const char *needle)
 {
@@ -95,9 +97,20 @@ str_begins_with(const char *haystack, const char *needle)
 #if defined(__GNUC__) && (__GNUC__ >= 2)
 /* GCC (and Clang) are known to have a compile-time strlen("literal") shortcut, and
    an optimised strncmp(), so use that by default. Macro is multi-evaluation safe. */
+static inline bool
+str_begins_builtin_success(const char *haystack, size_t needle_len,
+                          const char **suffix_r)
+{
+       *suffix_r = haystack + needle_len;
+       return TRUE;
+}
 # define str_begins_with(h, n) \
        (__builtin_constant_p(n) ? strncmp((h), (n), strlen(n))==0 : \
         (str_begins_with)((h), (n)))
+# define str_begins_suffix(h, n, suffix_r) \
+       (!__builtin_constant_p(n) ? (str_begins_suffix)((h), (n), (suffix_r)) : \
+        (strncmp((h), (n), strlen(n)) != 0 ? FALSE : \
+         str_begins_builtin_success((h), strlen(n), suffix_r)))
 #endif
 #define str_begins(h, n) str_begins_with(h, n)
 
index f48a53538d9db5ae99d6c31ba5a3317df82f53e5..0972fea392af08f4e10b7608a7dd24415f3126bc 100644 (file)
@@ -475,7 +475,7 @@ test_str_match(void)
                MATCH_TEST("blahblahblah", "foo", "bar"),
 #undef MATCH_TEST
        };
-
+       const char *suffix;
        unsigned int i;
 
        test_begin("str_match");
@@ -487,12 +487,23 @@ test_str_match(void)
        for (i = 0; i < N_ELEMENTS(tests); i++) {
                /* This is just 2 ways of wording the same test, but that also
                   sanity tests the match values above. */
-               test_assert_idx(str_begins(tests[i].s1, tests[i].s2) ==
-                               (strncmp(tests[i].s1, tests[i].s2, strlen(tests[i].s2)) == 0), i);
-               test_assert_idx(str_begins(tests[i].s1, tests[i].s2) ==
+               bool equals = strncmp(tests[i].s1, tests[i].s2, strlen(tests[i].s2)) == 0;
+               test_assert_idx(str_begins_with(tests[i].s1, tests[i].s2) == equals, i);
+               test_assert_idx(str_begins_suffix(tests[i].s1, tests[i].s2, &suffix) == equals &&
+                               (!equals || suffix == tests[i].s1 + strlen(tests[i].s2)), i);
+               test_assert_idx(str_begins_suffix(tests[i].s1, tests[i].s2, &suffix) ==
                                (strlen(tests[i].s2) == tests[i].match), i);
        }
        /* test literal-optimized versions of these */
+       test_assert(str_begins_suffix("", "", &suffix) && suffix[0] == '\0');
+       test_assert(str_begins_suffix("123", "", &suffix) && strcmp(suffix, "123") == 0);
+       test_assert(str_begins_suffix("123", "1", &suffix) && strcmp(suffix, "23") == 0);
+       test_assert(str_begins_suffix("123", "123", &suffix) && suffix[0] == '\0');
+       suffix = NULL;
+       test_assert(!str_begins_suffix("123", "1234", &suffix) && suffix == NULL);
+       test_assert(!str_begins_suffix("", "123", &suffix) && suffix == NULL);
+       test_assert(!str_begins_suffix("12", "123", &suffix) && suffix == NULL);
+
        test_assert(str_begins_with("", ""));
        test_assert(str_begins_with("123", ""));
        test_assert(str_begins_with("123", "1"));