diff --git a/AK/String.h b/AK/String.h index 89a906d87f6..3e1580ff2c9 100644 --- a/AK/String.h +++ b/AK/String.h @@ -147,6 +147,8 @@ public: [[nodiscard]] Optional find_last(char needle) const { return StringUtils::find_last(*this, needle); } // FIXME: Implement find_last(StringView const&) for API symmetry. [[nodiscard]] Vector find_all(StringView const& needle) const { return StringUtils::find_all(*this, needle); } + using SearchDirection = StringUtils::SearchDirection; + [[nodiscard]] Optional find_any_of(StringView const& needles, SearchDirection direction) const { return StringUtils::find_any_of(*this, needles, direction); } [[nodiscard]] String substring(size_t start, size_t length) const; [[nodiscard]] String substring(size_t start) const; diff --git a/AK/StringUtils.cpp b/AK/StringUtils.cpp index 48bfb271498..7dd225c9bde 100644 --- a/AK/StringUtils.cpp +++ b/AK/StringUtils.cpp @@ -378,6 +378,24 @@ Vector find_all(StringView const& haystack, StringView const& needle) return positions; } +Optional find_any_of(StringView const& haystack, StringView const& needles, SearchDirection direction) +{ + if (haystack.is_empty() || needles.is_empty()) + return {}; + if (direction == SearchDirection::Forward) { + for (size_t i = 0; i < haystack.length(); ++i) { + if (needles.contains(haystack[i])) + return i; + } + } else if (direction == SearchDirection::Backward) { + for (size_t i = haystack.length(); i > 0; --i) { + if (needles.contains(haystack[i - 1])) + return i - 1; + } + } + return {}; +} + String to_snakecase(const StringView& str) { auto should_insert_underscore = [&](auto i, auto current_char) { diff --git a/AK/StringUtils.h b/AK/StringUtils.h index f7211b05616..f123665fd28 100644 --- a/AK/StringUtils.h +++ b/AK/StringUtils.h @@ -62,6 +62,11 @@ Optional find(StringView const& haystack, char needle, size_t start = 0) Optional find(StringView const& haystack, StringView const& needle, size_t start = 0); Optional find_last(StringView const& haystack, char needle); Vector find_all(StringView const& haystack, StringView const& needle); +enum class SearchDirection { + Forward, + Backward +}; +Optional find_any_of(StringView const& haystack, StringView const& needles, SearchDirection); String to_snakecase(const StringView&); diff --git a/AK/StringView.cpp b/AK/StringView.cpp index 5fe62fe8b83..b7428646717 100644 --- a/AK/StringView.cpp +++ b/AK/StringView.cpp @@ -238,33 +238,6 @@ bool StringView::operator==(const String& string) const return !__builtin_memcmp(m_characters, string.characters(), m_length); } -Optional StringView::find_first_of(const StringView& view) const -{ - if (const auto location = AK::find_if(begin(), end(), - [&](const auto c) { - return any_of(view.begin(), view.end(), - [&](const auto view_char) { - return c == view_char; - }); - }); - location != end()) { - return location.index(); - } - return {}; -} - -Optional StringView::find_last_of(const StringView& view) const -{ - for (size_t pos = m_length; pos != 0; --pos) { - char c = m_characters[pos - 1]; - for (char view_char : view) { - if (c == view_char) - return pos - 1; - } - } - return {}; -} - String StringView::to_string() const { return String { *this }; } } diff --git a/AK/StringView.h b/AK/StringView.h index 7473ffa3ca5..2d03af4eace 100644 --- a/AK/StringView.h +++ b/AK/StringView.h @@ -93,8 +93,8 @@ public: [[nodiscard]] Vector find_all(StringView const& needle) const { return StringUtils::find_all(*this, needle); } - [[nodiscard]] Optional find_first_of(StringView const&) const; - [[nodiscard]] Optional find_last_of(StringView const&) const; + using SearchDirection = StringUtils::SearchDirection; + [[nodiscard]] Optional find_any_of(StringView const& needles, SearchDirection direction = SearchDirection::Forward) { return StringUtils::find_any_of(*this, needles, direction); } [[nodiscard]] constexpr StringView substring_view(size_t start, size_t length) const { diff --git a/Tests/AK/TestStringView.cpp b/Tests/AK/TestStringView.cpp index fa079c9ebb8..a01b908a4dd 100644 --- a/Tests/AK/TestStringView.cpp +++ b/Tests/AK/TestStringView.cpp @@ -123,26 +123,19 @@ TEST_CASE(find_last) EXPECT_EQ(test_string_view.find_last('/'), 0U); } -TEST_CASE(find_first_of) +TEST_CASE(find_any_of) { auto test_string_view = "aabbcc_xy_ccbbaa"sv; - EXPECT_EQ(test_string_view.find_first_of("bc"), 2U); - EXPECT_EQ(test_string_view.find_first_of("yx"), 7U); - EXPECT_EQ(test_string_view.find_first_of("defg").has_value(), false); + EXPECT_EQ(test_string_view.find_any_of("bc", StringView::SearchDirection::Forward), 2U); + EXPECT_EQ(test_string_view.find_any_of("yx", StringView::SearchDirection::Forward), 7U); + EXPECT_EQ(test_string_view.find_any_of("defg", StringView::SearchDirection::Forward).has_value(), false); + EXPECT_EQ(test_string_view.find_any_of("bc", StringView::SearchDirection::Backward), 13U); + EXPECT_EQ(test_string_view.find_any_of("yx", StringView::SearchDirection::Backward), 8U); + EXPECT_EQ(test_string_view.find_any_of("fghi", StringView::SearchDirection::Backward).has_value(), false); test_string_view = "/"sv; - EXPECT_EQ(test_string_view.find_first_of("/"), 0U); -} - -TEST_CASE(find_last_of) -{ - auto test_string_view = "aabbcc_xy_ccbbaa"sv; - EXPECT_EQ(test_string_view.find_last_of("bc"), 13U); - EXPECT_EQ(test_string_view.find_last_of("yx"), 8U); - EXPECT_EQ(test_string_view.find_last_of("fghi").has_value(), false); - - test_string_view = "/"sv; - EXPECT_EQ(test_string_view.find_last_of("/"), 0U); + EXPECT_EQ(test_string_view.find_any_of("/", StringView::SearchDirection::Forward), 0U); + EXPECT_EQ(test_string_view.find_any_of("/", StringView::SearchDirection::Backward), 0U); } TEST_CASE(split_view) diff --git a/Userland/DevTools/HackStudio/LanguageServers/Cpp/CppComprehensionEngine.cpp b/Userland/DevTools/HackStudio/LanguageServers/Cpp/CppComprehensionEngine.cpp index 87c0eb96cee..7eec6b927a6 100644 --- a/Userland/DevTools/HackStudio/LanguageServers/Cpp/CppComprehensionEngine.cpp +++ b/Userland/DevTools/HackStudio/LanguageServers/Cpp/CppComprehensionEngine.cpp @@ -637,7 +637,7 @@ Optional> CppComprehensionEngine::try_a } else return {}; - auto last_slash = partial_include.find_last_of("/"); + auto last_slash = partial_include.find_last('/'); auto include_dir = String::empty(); auto partial_basename = partial_include.substring_view((last_slash.has_value() ? last_slash.value() : 0) + 1); if (last_slash.has_value()) { diff --git a/Userland/DevTools/Profiler/Process.cpp b/Userland/DevTools/Profiler/Process.cpp index 1fbbddd5ec2..85418292b42 100644 --- a/Userland/DevTools/Profiler/Process.cpp +++ b/Userland/DevTools/Profiler/Process.cpp @@ -74,7 +74,7 @@ void LibraryMetadata::handle_mmap(FlatPtr base, size_t size, const String& name) else if (!name.contains(":")) return; else - path = name.substring(0, name.view().find_first_of(":").value()); + path = name.substring(0, name.view().find(':').value()); String full_path; if (name.contains(".so")) diff --git a/Userland/Libraries/LibELF/CoreDump.h b/Userland/Libraries/LibELF/CoreDump.h index 2a87785cc94..2e3b6e63419 100644 --- a/Userland/Libraries/LibELF/CoreDump.h +++ b/Userland/Libraries/LibELF/CoreDump.h @@ -60,9 +60,10 @@ struct [[gnu::packed]] MemoryRegionInfo { StringView memory_region_name { region_name }; if (memory_region_name.contains("Loader.so")) return "Loader.so"; - if (!memory_region_name.contains(":")) + auto maybe_colon_index = memory_region_name.find(':'); + if (!maybe_colon_index.has_value()) return {}; - return memory_region_name.substring_view(0, memory_region_name.find_first_of(":").value()).to_string(); + return memory_region_name.substring_view(0, *maybe_colon_index).to_string(); } }; diff --git a/Userland/Libraries/LibGemini/Line.cpp b/Userland/Libraries/LibGemini/Line.cpp index 034b640bcda..8e5dde59a78 100644 --- a/Userland/Libraries/LibGemini/Line.cpp +++ b/Userland/Libraries/LibGemini/Line.cpp @@ -73,7 +73,7 @@ Link::Link(String text, const Document& document) while (index < m_text.length() && (m_text[index] == ' ' || m_text[index] == '\t')) ++index; auto url_string = m_text.substring_view(index, m_text.length() - index); - auto space_offset = url_string.find_first_of(" \t"); + auto space_offset = url_string.find_any_of(" \t"); String url = url_string; if (space_offset.has_value()) { url = url_string.substring_view(0, space_offset.value()); diff --git a/Userland/Libraries/LibLine/Editor.cpp b/Userland/Libraries/LibLine/Editor.cpp index bd970a6b883..dca044a2271 100644 --- a/Userland/Libraries/LibLine/Editor.cpp +++ b/Userland/Libraries/LibLine/Editor.cpp @@ -252,7 +252,7 @@ bool Editor::load_history(const String& path) auto data = history_file->read_all(); auto hist = StringView { data.data(), data.size() }; for (auto& str : hist.split_view("\n\n")) { - auto it = str.find_first_of("::").value_or(0); + auto it = str.find("::").value_or(0); auto time = str.substring_view(0, it).to_uint().value_or(0); auto string = str.substring_view(it == 0 ? it : it + 2); m_history.append({ string, time }); diff --git a/Userland/Libraries/LibWeb/CSS/Parser/DeprecatedCSSParser.cpp b/Userland/Libraries/LibWeb/CSS/Parser/DeprecatedCSSParser.cpp index 5c72d245986..6de38c9d98a 100644 --- a/Userland/Libraries/LibWeb/CSS/Parser/DeprecatedCSSParser.cpp +++ b/Userland/Libraries/LibWeb/CSS/Parser/DeprecatedCSSParser.cpp @@ -222,7 +222,7 @@ static StringView parse_custom_property_name(const StringView& value) if (!value.starts_with("var(") || !value.ends_with(")")) return {}; // FIXME: Allow for fallback - auto first_comma_index = value.find_first_of(","); + auto first_comma_index = value.find(','); auto length = value.length(); auto substring_length = first_comma_index.has_value() ? first_comma_index.value() - 4 - 1 : length - 4 - 1;