diff --git a/src/base/base_strings.c b/src/base/base_strings.c index bcfe297f..d327e952 100644 --- a/src/base/base_strings.c +++ b/src/base/base_strings.c @@ -325,6 +325,76 @@ str8_match(String8 a, String8 b, StringMatchFlags flags) return result; } +internal B32 +str8_char_match(U8 a, U8 b, StringMatchFlags flags) +{ + U8 at = a; + U8 bt = b; + if (flags & StringMatchFlag_CaseInsensitive) { + at = upper_from_char(at); + bt = upper_from_char(bt); + } + if (flags & StringMatchFlag_SlashInsensitive) { + if (at == '\\') { at = '/'; } + if (bt == '\\') { bt = '/'; } + } + return (at == bt); +} + +internal B32 +str8_match_wildcard(String8 string, String8 pattern, StringMatchFlags flags) +{ + B32 matched = 0; + + U64 pattern_cursor = 0; + U64 string_cursor = 0; + + U64 pattern_start = max_U64; + U64 string_start = 0; + + for (;;) { + if (pattern_cursor == pattern.size) { + if (string_cursor == string.size || (flags & StringMatchFlag_RightSideSloppy)) { + matched = 1; + break; + } + } + + if (string_cursor == string.size) { + while (pattern_cursor < pattern.size && pattern.str[pattern_cursor] == '*') { + pattern_cursor += 1; + } + matched = (pattern_cursor == pattern.size); + break; + } + + if (pattern_cursor < pattern.size && pattern.str[pattern_cursor] == '*') { + pattern_start = pattern_cursor; + string_start = string_cursor; + pattern_cursor += 1; + continue; + } + + + if (pattern_cursor < pattern.size && (pattern.str[pattern_cursor] == '?' || str8_char_match(string.str[string_cursor], pattern.str[pattern_cursor], flags))) { + string_cursor += 1; + pattern_cursor += 1; + continue; + } + + if (pattern_start != max_U64) { + pattern_cursor = pattern_start + 1; + string_start += 1; + string_cursor = string_start; + continue; + } + + break; + } + + return matched; +} + internal U64 str8_find_needle(String8 string, U64 start_pos, String8 needle, StringMatchFlags flags) { diff --git a/src/base/base_strings.h b/src/base/base_strings.h index 95755092..dd078d8b 100644 --- a/src/base/base_strings.h +++ b/src/base/base_strings.h @@ -197,6 +197,7 @@ internal String8 backslashed_from_str8(Arena *arena, String8 string); #define str8_match_lit(a_lit, b, flags) str8_match(str8_lit(a_lit), (b), (flags)) #define str8_match_cstr(a_cstr, b, flags) str8_match(str8_cstring(a_cstr), (b), (flags)) internal B32 str8_match(String8 a, String8 b, StringMatchFlags flags); +internal B32 str8_match_wildcard(String8 string, String8 pattern, StringMatchFlags flags); internal U64 str8_find_needle(String8 string, U64 start_pos, String8 needle, StringMatchFlags flags); internal U64 str8_find_needle_reverse(String8 string, U64 start_pos, String8 needle, StringMatchFlags flags); internal B32 str8_is_before(String8 a, String8 b); diff --git a/src/torture/torture_base.c b/src/torture/torture_base.c index b55e13f6..ee6e7ca8 100644 --- a/src/torture/torture_base.c +++ b/src/torture/torture_base.c @@ -104,4 +104,76 @@ TEST(count_digits) T_Ok(count_digits_u64(999999, 10) == 6); } +TEST(match_wildcard) +{ + // empty strings + T_Ok(str8_match_wildcard(str8_lit(""), str8_lit(""), 0) == 1); + T_Ok(str8_match_wildcard(str8_lit(""), str8_lit("*"), 0) == 1); + T_Ok(str8_match_wildcard(str8_lit(""), str8_lit("**"), 0) == 1); + T_Ok(str8_match_wildcard(str8_lit(""), str8_lit("?"), 0) == 0); + T_Ok(str8_match_wildcard(str8_lit(""), str8_lit("*?"), 0) == 0); + T_Ok(str8_match_wildcard(str8_lit(""), str8_lit("?*"), 0) == 0); + T_Ok(str8_match_wildcard(str8_lit("a"), str8_lit(""), 0) == 0); + + // exact + T_Ok(str8_match_wildcard(str8_lit("a"), str8_lit("a"), 0) == 1); + T_Ok(str8_match_wildcard(str8_lit("a"), str8_lit("A"), 0) == 0); + + // ? + T_Ok(str8_match_wildcard(str8_lit("a"), str8_lit("?"), 0) == 1); + T_Ok(str8_match_wildcard(str8_lit(""), str8_lit("?"), 0) == 0); + T_Ok(str8_match_wildcard(str8_lit("ab"), str8_lit("?"), 0) == 0); + T_Ok(str8_match_wildcard(str8_lit("ab"), str8_lit("a?"), 0) == 1); + T_Ok(str8_match_wildcard(str8_lit("ab"), str8_lit("?b"), 0) == 1); + T_Ok(str8_match_wildcard(str8_lit("ab"), str8_lit("??"), 0) == 1); + T_Ok(str8_match_wildcard(str8_lit("abc"), str8_lit("a?c"), 0) == 1); + T_Ok(str8_match_wildcard(str8_lit("ab"), str8_lit("???"), 0) == 0); + + // * + T_Ok(str8_match_wildcard(str8_lit(""), str8_lit("*"), 0) == 1); + T_Ok(str8_match_wildcard(str8_lit("a"), str8_lit("*"), 0) == 1); + T_Ok(str8_match_wildcard(str8_lit("abc"), str8_lit("*"), 0) == 1); + T_Ok(str8_match_wildcard(str8_lit("abc"), str8_lit("a*"), 0) == 1); + T_Ok(str8_match_wildcard(str8_lit("abc"), str8_lit("*c"), 0) == 1); + T_Ok(str8_match_wildcard(str8_lit("abc"), str8_lit("*b*"), 0) == 1); + T_Ok(str8_match_wildcard(str8_lit("abc"), str8_lit("a*c"), 0) == 1); + T_Ok(str8_match_wildcard(str8_lit("abc"), str8_lit("b*"), 0) == 0); + T_Ok(str8_match_wildcard(str8_lit("abc"), str8_lit("**"), 0) == 1); + T_Ok(str8_match_wildcard(str8_lit("abc"), str8_lit("a**c"), 0) == 1); + T_Ok(str8_match_wildcard(str8_lit("abc"), str8_lit("a*b*c"), 0) == 1); + T_Ok(str8_match_wildcard(str8_lit("abc"), str8_lit("*a*d*"), 0) == 0); + + T_Ok(str8_match_wildcard(str8_lit("abcd"), str8_lit("a*d"), 0) == 1); + T_Ok(str8_match_wildcard(str8_lit("abefcdgiescdfimde"), str8_lit("ab*cd?i*de"), 0) == 1); + T_Ok(str8_match_wildcard(str8_lit("mississippi"), str8_lit("m*iss*ppi"), 0) == 1); + T_Ok(str8_match_wildcard(str8_lit("abc"), str8_lit("*b"), 0) == 0); + T_Ok(str8_match_wildcard(str8_lit("a"), str8_lit("aa"), 0) == 0); + T_Ok(str8_match_wildcard(str8_lit("aa"), str8_lit("a"), 0) == 0); + + // case insensitive + T_Ok(str8_match_wildcard(str8_lit("a"), str8_lit("A"), StringMatchFlag_CaseInsensitive) == 1); + T_Ok(str8_match_wildcard(str8_lit("FooBar"), str8_lit("foobar"), StringMatchFlag_CaseInsensitive) == 1); + T_Ok(str8_match_wildcard(str8_lit("Foobar"), str8_lit("foo*"), StringMatchFlag_CaseInsensitive) == 1); + + // right side sloppy + T_Ok(str8_match_wildcard(str8_lit("abc"), str8_lit("ab"), StringMatchFlag_RightSideSloppy) == 1); + T_Ok(str8_match_wildcard(str8_lit("abc"), str8_lit(""), StringMatchFlag_RightSideSloppy) == 1); + T_Ok(str8_match_wildcard(str8_lit(""), str8_lit("a"), StringMatchFlag_RightSideSloppy) == 0); + + // slash insensitive + T_Ok(str8_match_wildcard(str8_lit("a/b"), str8_lit("a\\b"), 0) == 0); + T_Ok(str8_match_wildcard(str8_lit("a/b"), str8_lit("a\\b"), StringMatchFlag_SlashInsensitive) == 1); + T_Ok(str8_match_wildcard(str8_lit("a/b/c"), str8_lit("a\\*\\c"), StringMatchFlag_SlashInsensitive) == 1); + + // combined + T_Ok(str8_match_wildcard(str8_lit("Ab\\Cde"), str8_lit("ab/*e"), StringMatchFlag_CaseInsensitive|StringMatchFlag_SlashInsensitive) == 1); + + T_Ok(str8_match_wildcard(str8_lit("abc"), str8_lit("*?*"), 0) == 1); + T_Ok(str8_match_wildcard(str8_lit("a"), str8_lit("*?*"), 0) == 1); + T_Ok(str8_match_wildcard(str8_lit(""), str8_lit("*?*"), 0) == 0); + T_Ok(str8_match_wildcard(str8_lit("abc"), str8_lit("?*?"), 0) == 1); + T_Ok(str8_match_wildcard(str8_lit("ab"), str8_lit("?*?"), 0) == 1); + T_Ok(str8_match_wildcard(str8_lit("a"), str8_lit("?*?"), 0) == 0); +} + #undef T_Group