From 82006725f6e69d2375da99b55fd1ab85a4c45625 Mon Sep 17 00:00:00 2001 From: Timo Sirainen Date: Fri, 10 Apr 2020 19:56:14 +0300 Subject: [PATCH] lib: Add str_begins_suffix() --- src/lib/strfuncs.c | 10 ++++++++++ src/lib/strfuncs.h | 13 +++++++++++++ src/lib/test-strfuncs.c | 19 +++++++++++++++---- 3 files changed, 38 insertions(+), 4 deletions(-) diff --git a/src/lib/strfuncs.c b/src/lib/strfuncs.c index 56e45765ba..32da9b0140 100644 --- a/src/lib/strfuncs.c +++ b/src/lib/strfuncs.c @@ -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) { diff --git a/src/lib/strfuncs.h b/src/lib/strfuncs.h index c338bf8798..0042991de2 100644 --- a/src/lib/strfuncs.h +++ b/src/lib/strfuncs.h @@ -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) diff --git a/src/lib/test-strfuncs.c b/src/lib/test-strfuncs.c index f48a53538d..0972fea392 100644 --- a/src/lib/test-strfuncs.c +++ b/src/lib/test-strfuncs.c @@ -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")); -- 2.47.3