summaryrefslogtreecommitdiff
path: root/AK/StringUtils.cpp
diff options
context:
space:
mode:
authorhowar6hill <f.eiwu@yahoo.com>2020-02-26 15:25:24 +0800
committerAndreas Kling <kling@serenityos.org>2020-03-02 10:38:08 +0100
commit055344f3461a28578f8b49e9f2431bb9f22936b4 (patch)
tree4a39e875cef56df1950d56a2dd385898f0d000fc /AK/StringUtils.cpp
parent2a30a020c13e462cba6e197f92a3171d79b12ba2 (diff)
downloadserenity-055344f3461a28578f8b49e9f2431bb9f22936b4.zip
AK: Move the wildcard-matching implementation to StringUtils
Provide wrappers in the String and StringView classes, and add some tests.
Diffstat (limited to 'AK/StringUtils.cpp')
-rw-r--r--AK/StringUtils.cpp64
1 files changed, 64 insertions, 0 deletions
diff --git a/AK/StringUtils.cpp b/AK/StringUtils.cpp
new file mode 100644
index 0000000000..fe37f49e0b
--- /dev/null
+++ b/AK/StringUtils.cpp
@@ -0,0 +1,64 @@
+#include <AK/String.h>
+#include <AK/StringUtils.h>
+#include <AK/StringView.h>
+
+namespace AK {
+
+namespace StringUtils {
+
+ bool matches(const StringView& str, const StringView& mask, CaseSensitivity case_sensitivity)
+ {
+ if (str.is_null() || mask.is_null())
+ return str.is_null() && mask.is_null();
+
+ if (case_sensitivity == CaseSensitivity::CaseInsensitive) {
+ const String str_lower = String(str).to_lowercase();
+ const String mask_lower = String(mask).to_lowercase();
+ return matches(str_lower, mask_lower, CaseSensitivity::CaseSensitive);
+ }
+
+ const char* string_ptr = str.characters_without_null_termination();
+ const char* string_end = string_ptr + str.length();
+ const char* mask_ptr = mask.characters_without_null_termination();
+ const char* mask_end = mask_ptr + mask.length();
+
+ // Match string against mask directly unless we hit a *
+ while ((string_ptr < string_end) && (mask_ptr < mask_end) && (*mask_ptr != '*')) {
+ if ((*mask_ptr != *string_ptr) && (*mask_ptr != '?'))
+ return false;
+ mask_ptr++;
+ string_ptr++;
+ }
+
+ const char* cp = nullptr;
+ const char* mp = nullptr;
+
+ while (string_ptr < string_end) {
+ if ((mask_ptr < mask_end) && (*mask_ptr == '*')) {
+ // If we have only a * left, there is no way to not match.
+ if (++mask_ptr == mask_end)
+ return true;
+ mp = mask_ptr;
+ cp = string_ptr + 1;
+ } else if ((mask_ptr < mask_end) && ((*mask_ptr == *string_ptr) || (*mask_ptr == '?'))) {
+ mask_ptr++;
+ string_ptr++;
+ } else if ((cp != nullptr) && (mp != nullptr)) {
+ mask_ptr = mp;
+ string_ptr = cp++;
+ } else {
+ break;
+ }
+ }
+
+ // Handle any trailing mask
+ while ((mask_ptr < mask_end) && (*mask_ptr == '*'))
+ mask_ptr++;
+
+ // If we 'ate' all of the mask and the string then we match.
+ return (mask_ptr == mask_end) && string_ptr == string_end;
+ }
+
+}
+
+}