From a5ecb9bd6b287e69bcd9565c0b33410527b34b58 Mon Sep 17 00:00:00 2001 From: Luke Date: Sat, 18 Jul 2020 17:59:38 +0100 Subject: AK: Add case insensitive version of starts_with --- AK/FlyString.cpp | 5 +++++ AK/FlyString.h | 1 + AK/String.cpp | 10 ++-------- AK/String.h | 2 +- AK/StringUtils.cpp | 25 +++++++++++++++++++++++++ AK/StringUtils.h | 1 + AK/StringView.cpp | 12 ++---------- AK/StringView.h | 2 +- AK/Tests/TestString.cpp | 2 ++ AK/Tests/TestStringUtils.cpp | 11 +++++++++++ AK/Tests/TestStringView.cpp | 2 ++ 11 files changed, 53 insertions(+), 20 deletions(-) diff --git a/AK/FlyString.cpp b/AK/FlyString.cpp index 22c00c2db0..fdf2eed56a 100644 --- a/AK/FlyString.cpp +++ b/AK/FlyString.cpp @@ -99,6 +99,11 @@ bool FlyString::equals_ignoring_case(const StringView& other) const 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 { return StringUtils::ends_with(view(), str, case_sensitivity); diff --git a/AK/FlyString.h b/AK/FlyString.h index 239dafe5cf..7862f0eee0 100644 --- a/AK/FlyString.h +++ b/AK/FlyString.h @@ -85,6 +85,7 @@ public: Optional to_int() 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; static void did_destroy_impl(Badge, StringImpl&); diff --git a/AK/String.cpp b/AK/String.cpp index bcb10d2d79..0bc454af1f 100644 --- a/AK/String.cpp +++ b/AK/String.cpp @@ -260,15 +260,9 @@ String String::format(const char* fmt, ...) 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 diff --git a/AK/String.h b/AK/String.h index ac107f1016..8362f4fefa 100644 --- a/AK/String.h +++ b/AK/String.h @@ -145,7 +145,7 @@ public: ConstIterator begin() const { return characters(); } 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 starts_with(char) const; bool ends_with(char) const; diff --git a/AK/StringUtils.cpp b/AK/StringUtils.cpp index ae5f9676d5..363cd07ab5 100644 --- a/AK/StringUtils.cpp +++ b/AK/StringUtils.cpp @@ -202,6 +202,31 @@ bool ends_with(const StringView& str, const StringView& end, CaseSensitivity cas 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; +} + } } diff --git a/AK/StringUtils.h b/AK/StringUtils.h index fb5464c59e..b2b9185166 100644 --- a/AK/StringUtils.h +++ b/AK/StringUtils.h @@ -44,6 +44,7 @@ Optional convert_to_uint(const StringView&); Optional convert_to_uint_from_hex(const StringView&); bool equals_ignoring_case(const StringView&, const StringView&); bool ends_with(const StringView& a, const StringView& b, CaseSensitivity); +bool starts_with(const StringView&, const StringView&, CaseSensitivity); } } diff --git a/AK/StringView.cpp b/AK/StringView.cpp index 121b7f235d..635a4be2d2 100644 --- a/AK/StringView.cpp +++ b/AK/StringView.cpp @@ -147,17 +147,9 @@ bool StringView::starts_with(char ch) const 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 diff --git a/AK/StringView.h b/AK/StringView.h index 2d05bea0a7..35c99e69f5 100644 --- a/AK/StringView.h +++ b/AK/StringView.h @@ -72,7 +72,7 @@ public: 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 starts_with(char) const; bool ends_with(char) const; diff --git a/AK/Tests/TestString.cpp b/AK/Tests/TestString.cpp index b3c70e6826..55aab73259 100644 --- a/AK/Tests/TestString.cpp +++ b/AK/Tests/TestString.cpp @@ -87,6 +87,8 @@ TEST_CASE(starts_with) EXPECT(!test_string.starts_with('B')); EXPECT(test_string.starts_with("ABCDEF")); 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) diff --git a/AK/Tests/TestStringUtils.cpp b/AK/Tests/TestStringUtils.cpp index ce17af6e6e..2d5ecea7e0 100644 --- a/AK/Tests/TestStringUtils.cpp +++ b/AK/Tests/TestStringUtils.cpp @@ -164,4 +164,15 @@ TEST_CASE(ends_with) 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) diff --git a/AK/Tests/TestStringView.cpp b/AK/Tests/TestStringView.cpp index ded60c943e..021be691b2 100644 --- a/AK/Tests/TestStringView.cpp +++ b/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("ABCDEF")); 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) -- cgit v1.2.3