From 6d2b003b6e652e807a1021151cccf41a396f18b7 Mon Sep 17 00:00:00 2001 From: Idan Horowitz Date: Sat, 11 Sep 2021 01:02:24 +0300 Subject: [PATCH] AK: Make String::count not use strstr and take a StringView This was needlessly copying StringView arguments, and was also using strstr internally, which meant it was doing a bunch of unnecessary strlen calls on it. This also moves the implementation to StringUtils to allow API consistency between String and StringView. --- AK/String.cpp | 16 ---------------- AK/String.h | 2 +- AK/StringUtils.cpp | 14 ++++++++++++++ AK/StringUtils.h | 2 ++ AK/StringView.h | 2 ++ 5 files changed, 19 insertions(+), 17 deletions(-) diff --git a/AK/String.cpp b/AK/String.cpp index fcb44006c68..1d8a6e844a3 100644 --- a/AK/String.cpp +++ b/AK/String.cpp @@ -382,22 +382,6 @@ int String::replace(const String& needle, const String& replacement, bool all_oc return positions.size(); } -size_t String::count(const String& needle) const -{ - size_t count = 0; - size_t start = 0, pos; - for (;;) { - const char* ptr = strstr(characters() + start, needle.characters()); - if (!ptr) - break; - - pos = ptr - characters(); - count++; - start = pos + 1; - } - return count; -} - String String::reverse() const { StringBuilder reversed_string(length()); diff --git a/AK/String.h b/AK/String.h index f0437296242..623c23fbb48 100644 --- a/AK/String.h +++ b/AK/String.h @@ -286,7 +286,7 @@ public: } int replace(const String& needle, const String& replacement, bool all_occurrences = false); - [[nodiscard]] size_t count(const String& needle) const; + [[nodiscard]] size_t count(StringView const& needle) const { return StringUtils::count(*this, needle); } [[nodiscard]] String reverse() const; template diff --git a/AK/StringUtils.cpp b/AK/StringUtils.cpp index a73abfa09b1..ee0594e7e91 100644 --- a/AK/StringUtils.cpp +++ b/AK/StringUtils.cpp @@ -427,6 +427,20 @@ String to_titlecase(StringView const& str) return builder.to_string(); } +// TODO: Benchmark against KMP (AK/MemMem.h) and switch over if it's faster for short strings too +size_t count(StringView const& str, StringView const& needle) +{ + if (needle.is_empty()) + return str.length(); + + size_t count = 0; + for (size_t i = 0; i < str.length() - needle.length() + 1; ++i) { + if (str.substring_view(i).starts_with(needle)) + count++; + } + return count; +} + } } diff --git a/AK/StringUtils.h b/AK/StringUtils.h index df1c13b13b5..661ce963dbd 100644 --- a/AK/StringUtils.h +++ b/AK/StringUtils.h @@ -71,6 +71,8 @@ Optional find_any_of(StringView const& haystack, StringView const& needl String to_snakecase(const StringView&); String to_titlecase(StringView const&); +size_t count(StringView const&, StringView const& needle); + } } diff --git a/AK/StringView.h b/AK/StringView.h index ba87d2c0c14..f6868d1b8a9 100644 --- a/AK/StringView.h +++ b/AK/StringView.h @@ -220,6 +220,8 @@ public: [[nodiscard]] bool is_whitespace() const { return StringUtils::is_whitespace(*this); } + [[nodiscard]] size_t count(StringView const& needle) const { return StringUtils::count(*this, needle); } + template [[nodiscard]] ALWAYS_INLINE constexpr bool is_one_of(Ts&&... strings) const {