Browse Source

AK: Add case insensitive version of starts_with

Luke 5 years ago
parent
commit
a5ecb9bd6b

+ 5 - 0
AK/FlyString.cpp

@@ -99,6 +99,11 @@ bool FlyString::equals_ignoring_case(const StringView& other) const
     return StringUtils::equals_ignoring_case(view(), other);
     return StringUtils::equals_ignoring_case(view(), other);
 }
 }
 
 
+bool FlyString::starts_with(const StringView& str, CaseSensitivity case_sensitivity) const
+{
+    return StringUtils::starts_with(view(), str, case_sensitivity);
+}
+
 bool FlyString::ends_with(const StringView& str, CaseSensitivity case_sensitivity) const
 bool FlyString::ends_with(const StringView& str, CaseSensitivity case_sensitivity) const
 {
 {
     return StringUtils::ends_with(view(), str, case_sensitivity);
     return StringUtils::ends_with(view(), str, case_sensitivity);

+ 1 - 0
AK/FlyString.h

@@ -85,6 +85,7 @@ public:
     Optional<int> to_int() const;
     Optional<int> to_int() const;
 
 
     bool equals_ignoring_case(const StringView&) const;
     bool equals_ignoring_case(const StringView&) const;
+    bool starts_with(const StringView&, CaseSensitivity = CaseSensitivity::CaseSensitive) const;
     bool ends_with(const StringView&, CaseSensitivity = CaseSensitivity::CaseSensitive) const;
     bool ends_with(const StringView&, CaseSensitivity = CaseSensitivity::CaseSensitive) const;
 
 
     static void did_destroy_impl(Badge<StringImpl>, StringImpl&);
     static void did_destroy_impl(Badge<StringImpl>, StringImpl&);

+ 2 - 8
AK/String.cpp

@@ -260,15 +260,9 @@ String String::format(const char* fmt, ...)
     return builder.to_string();
     return builder.to_string();
 }
 }
 
 
-bool String::starts_with(const StringView& str) const
+bool String::starts_with(const StringView& str, CaseSensitivity case_sensitivity) const
 {
 {
-    if (str.is_empty())
-        return true;
-    if (is_empty())
-        return false;
-    if (str.length() > length())
-        return false;
-    return !memcmp(characters(), str.characters_without_null_termination(), str.length());
+    return StringUtils::starts_with(*this, str, case_sensitivity);
 }
 }
 
 
 bool String::starts_with(char ch) const
 bool String::starts_with(char ch) const

+ 1 - 1
AK/String.h

@@ -145,7 +145,7 @@ public:
     ConstIterator begin() const { return characters(); }
     ConstIterator begin() const { return characters(); }
     ConstIterator end() const { return begin() + length(); }
     ConstIterator end() const { return begin() + length(); }
 
 
-    bool starts_with(const StringView&) const;
+    bool starts_with(const StringView&, CaseSensitivity = CaseSensitivity::CaseSensitive) const;
     bool ends_with(const StringView&, CaseSensitivity = CaseSensitivity::CaseSensitive) const;
     bool ends_with(const StringView&, CaseSensitivity = CaseSensitivity::CaseSensitive) const;
     bool starts_with(char) const;
     bool starts_with(char) const;
     bool ends_with(char) const;
     bool ends_with(char) const;

+ 25 - 0
AK/StringUtils.cpp

@@ -202,6 +202,31 @@ bool ends_with(const StringView& str, const StringView& end, CaseSensitivity cas
     return true;
     return true;
 }
 }
 
 
+bool starts_with(const StringView& str, const StringView& start, CaseSensitivity case_sensitivity)
+{
+    if (start.is_empty())
+        return true;
+    if (str.is_empty())
+        return false;
+    if (start.length() > str.length())
+        return false;
+    if (str.characters_without_null_termination() == start.characters_without_null_termination())
+        return true;
+
+    if (case_sensitivity == CaseSensitivity::CaseSensitive)
+        return !memcmp(str.characters_without_null_termination(), start.characters_without_null_termination(), start.length());
+
+    auto str_chars = str.characters_without_null_termination();
+    auto start_chars = start.characters_without_null_termination();
+
+    size_t si = 0;
+    for (size_t starti = 0; starti < start.length(); ++si, ++starti) {
+        if (to_lowercase(str_chars[si]) != to_lowercase(start_chars[starti]))
+            return false;
+    }
+    return true;
+}
+
 }
 }
 
 
 }
 }

+ 1 - 0
AK/StringUtils.h

@@ -44,6 +44,7 @@ Optional<unsigned> convert_to_uint(const StringView&);
 Optional<unsigned> convert_to_uint_from_hex(const StringView&);
 Optional<unsigned> convert_to_uint_from_hex(const StringView&);
 bool equals_ignoring_case(const StringView&, const StringView&);
 bool equals_ignoring_case(const StringView&, const StringView&);
 bool ends_with(const StringView& a, const StringView& b, CaseSensitivity);
 bool ends_with(const StringView& a, const StringView& b, CaseSensitivity);
+bool starts_with(const StringView&, const StringView&, CaseSensitivity);
 }
 }
 
 
 }
 }

+ 2 - 10
AK/StringView.cpp

@@ -147,17 +147,9 @@ bool StringView::starts_with(char ch) const
     return ch == characters_without_null_termination()[0];
     return ch == characters_without_null_termination()[0];
 }
 }
 
 
-bool StringView::starts_with(const StringView& str) const
+bool StringView::starts_with(const StringView& str, CaseSensitivity case_sensitivity) const
 {
 {
-    if (str.is_empty())
-        return true;
-    if (is_empty())
-        return false;
-    if (str.length() > length())
-        return false;
-    if (characters_without_null_termination() == str.characters_without_null_termination())
-        return true;
-    return !memcmp(characters_without_null_termination(), str.characters_without_null_termination(), str.length());
+    return StringUtils::starts_with(*this, str, case_sensitivity);
 }
 }
 
 
 bool StringView::ends_with(char ch) const
 bool StringView::ends_with(char ch) const

+ 1 - 1
AK/StringView.h

@@ -72,7 +72,7 @@ public:
 
 
     unsigned hash() const;
     unsigned hash() const;
 
 
-    bool starts_with(const StringView&) const;
+    bool starts_with(const StringView&, CaseSensitivity = CaseSensitivity::CaseSensitive) const;
     bool ends_with(const StringView&, CaseSensitivity = CaseSensitivity::CaseSensitive) const;
     bool ends_with(const StringView&, CaseSensitivity = CaseSensitivity::CaseSensitive) const;
     bool starts_with(char) const;
     bool starts_with(char) const;
     bool ends_with(char) const;
     bool ends_with(char) const;

+ 2 - 0
AK/Tests/TestString.cpp

@@ -87,6 +87,8 @@ TEST_CASE(starts_with)
     EXPECT(!test_string.starts_with('B'));
     EXPECT(!test_string.starts_with('B'));
     EXPECT(test_string.starts_with("ABCDEF"));
     EXPECT(test_string.starts_with("ABCDEF"));
     EXPECT(!test_string.starts_with("DEF"));
     EXPECT(!test_string.starts_with("DEF"));
+    EXPECT(test_string.starts_with("abc",  CaseSensitivity::CaseInsensitive));
+    EXPECT(!test_string.starts_with("abc",  CaseSensitivity::CaseSensitive));
 }
 }
 
 
 TEST_CASE(ends_with)
 TEST_CASE(ends_with)

+ 11 - 0
AK/Tests/TestStringUtils.cpp

@@ -164,4 +164,15 @@ TEST_CASE(ends_with)
     EXPECT(!AK::StringUtils::ends_with(test_string, "def",  CaseSensitivity::CaseSensitive));
     EXPECT(!AK::StringUtils::ends_with(test_string, "def",  CaseSensitivity::CaseSensitive));
 }
 }
 
 
+TEST_CASE(starts_with)
+{
+    String test_string = "ABCDEF";
+    EXPECT(AK::StringUtils::starts_with(test_string, "ABC", CaseSensitivity::CaseSensitive));
+    EXPECT(AK::StringUtils::starts_with(test_string, "ABCDEF",  CaseSensitivity::CaseSensitive));
+    EXPECT(!AK::StringUtils::starts_with(test_string, "BCDEF", CaseSensitivity::CaseSensitive));
+    EXPECT(!AK::StringUtils::starts_with(test_string, "ABCDEFG", CaseSensitivity::CaseSensitive));
+    EXPECT(AK::StringUtils::starts_with(test_string, "abc",  CaseSensitivity::CaseInsensitive));
+    EXPECT(!AK::StringUtils::starts_with(test_string, "abc",  CaseSensitivity::CaseSensitive));
+}
+
 TEST_MAIN(StringUtils)
 TEST_MAIN(StringUtils)

+ 2 - 0
AK/Tests/TestStringView.cpp

@@ -68,6 +68,8 @@ TEST_CASE(starts_with)
     EXPECT(test_string_view.starts_with("AB"));
     EXPECT(test_string_view.starts_with("AB"));
     EXPECT(test_string_view.starts_with("ABCDEF"));
     EXPECT(test_string_view.starts_with("ABCDEF"));
     EXPECT(!test_string_view.starts_with("DEF"));
     EXPECT(!test_string_view.starts_with("DEF"));
+    EXPECT(test_string_view.starts_with("abc",  CaseSensitivity::CaseInsensitive));
+    EXPECT(!test_string_view.starts_with("abc",  CaseSensitivity::CaseSensitive));
 }
 }
 
 
 TEST_CASE(ends_with)
 TEST_CASE(ends_with)