From 39442e6d4f0ace5ccae377a320e4a922908cd0c8 Mon Sep 17 00:00:00 2001 From: AnotherTest Date: Tue, 12 Jan 2021 23:28:45 +0330 Subject: [PATCH] AK: Add String{View,}::find(StringView) I personally mistook `find_first_of(StringView)` to be analogous to this so let's add a `find()` method that actually searches the string. --- AK/MemMem.h | 33 ++++++++++++++++++++++----------- AK/String.cpp | 10 ++++++++++ AK/String.h | 4 ++++ AK/StringUtils.cpp | 8 ++++++++ AK/StringUtils.h | 1 + AK/StringView.cpp | 10 ++++++++++ AK/StringView.h | 3 +++ AK/Tests/TestStringUtils.cpp | 12 ++++++++++++ 8 files changed, 70 insertions(+), 11 deletions(-) diff --git a/AK/MemMem.h b/AK/MemMem.h index 38b7f8527e2..33af7e16306 100644 --- a/AK/MemMem.h +++ b/AK/MemMem.h @@ -120,28 +120,39 @@ static inline Optional memmem(const HaystackIterT& haystack_begin, const return {}; } -static inline const void* memmem(const void* haystack, size_t haystack_length, const void* needle, size_t needle_length) +static inline Optional memmem_optional(const void* haystack, size_t haystack_length, const void* needle, size_t needle_length) { if (needle_length == 0) - return haystack; + return 0; if (haystack_length < needle_length) - return nullptr; + return {}; - if (haystack_length == needle_length) - return __builtin_memcmp(haystack, needle, haystack_length) == 0 ? haystack : nullptr; + if (haystack_length == needle_length) { + if (__builtin_memcmp(haystack, needle, haystack_length) == 0) + return 0; + return {}; + } - if (needle_length < 32) - return bitap_bitwise(haystack, haystack_length, needle, needle_length); + if (needle_length < 32) { + auto ptr = bitap_bitwise(haystack, haystack_length, needle, needle_length); + if (ptr) + return static_cast((FlatPtr)ptr - (FlatPtr)haystack); + return {}; + } // Fallback to KMP. Array, 1> spans { Span { (const u8*)haystack, haystack_length } }; - auto result = memmem(spans.begin(), spans.end(), { (const u8*)needle, needle_length }); + return memmem(spans.begin(), spans.end(), { (const u8*)needle, needle_length }); +} - if (result.has_value()) - return (const u8*)haystack + result.value(); +static inline const void* memmem(const void* haystack, size_t haystack_length, const void* needle, size_t needle_length) +{ + auto offset = memmem_optional(haystack, haystack_length, needle, needle_length); + if (offset.has_value()) + return ((const u8*)haystack) + offset.value(); - return {}; + return nullptr; } } diff --git a/AK/String.cpp b/AK/String.cpp index 2e25f7b4643..33f65824cea 100644 --- a/AK/String.cpp +++ b/AK/String.cpp @@ -469,4 +469,14 @@ String String::vformatted(StringView fmtstr, TypeErasedFormatParams params) return builder.to_string(); } +Optional String::find(char c) const +{ + return find(StringView { &c, 1 }); +} + +Optional String::find(const StringView& view) const +{ + return StringUtils::find(*this, view); +} + } diff --git a/AK/String.h b/AK/String.h index c30f72e2584..cb935088759 100644 --- a/AK/String.h +++ b/AK/String.h @@ -138,6 +138,10 @@ public: Vector split_limit(char separator, size_t limit, bool keep_empty = false) const; Vector split(char separator, bool keep_empty = false) const; + + Optional find(char) const; + Optional find(const StringView&) const; + String substring(size_t start) const; String substring(size_t start, size_t length) const; diff --git a/AK/StringUtils.cpp b/AK/StringUtils.cpp index e808d7d499b..7e65caab857 100644 --- a/AK/StringUtils.cpp +++ b/AK/StringUtils.cpp @@ -25,6 +25,7 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +#include #include #include #include @@ -342,6 +343,13 @@ StringView trim_whitespace(const StringView& str, TrimMode mode) return str.substring_view(substring_start, substring_length); } + +Optional find(const StringView& haystack, const StringView& needle) +{ + return AK::memmem_optional( + haystack.characters_without_null_termination(), haystack.length(), + needle.characters_without_null_termination(), needle.length()); +} } } diff --git a/AK/StringUtils.h b/AK/StringUtils.h index e32c5126950..1f830caa6ed 100644 --- a/AK/StringUtils.h +++ b/AK/StringUtils.h @@ -71,6 +71,7 @@ bool starts_with(const StringView&, const StringView&, CaseSensitivity); bool contains(const StringView&, const StringView&, CaseSensitivity); bool is_whitespace(const StringView&); StringView trim_whitespace(const StringView&, TrimMode mode); +Optional find(const StringView& haystack, const StringView& needle); } } diff --git a/AK/StringView.cpp b/AK/StringView.cpp index 82c7b946197..42e7cb467c5 100644 --- a/AK/StringView.cpp +++ b/AK/StringView.cpp @@ -310,6 +310,16 @@ Optional StringView::find_last_of(const StringView& view) const return {}; } +Optional StringView::find(char c) const +{ + return find(StringView { &c, 1 }); +} + +Optional StringView::find(const StringView& view) const +{ + return StringUtils::find(*this, view); +} + String StringView::to_string() const { return String { *this }; } } diff --git a/AK/StringView.h b/AK/StringView.h index e83af7642cc..7ff37437c36 100644 --- a/AK/StringView.h +++ b/AK/StringView.h @@ -100,6 +100,9 @@ public: Optional find_last_of(char) const; Optional find_last_of(const StringView&) const; + Optional find(const StringView&) const; + Optional find(char c) const; + StringView substring_view(size_t start, size_t length) const; StringView substring_view(size_t start) const; Vector split_view(char, bool keep_empty = false) const; diff --git a/AK/Tests/TestStringUtils.cpp b/AK/Tests/TestStringUtils.cpp index abb6891144f..d1edda37edf 100644 --- a/AK/Tests/TestStringUtils.cpp +++ b/AK/Tests/TestStringUtils.cpp @@ -298,4 +298,16 @@ TEST_CASE(is_whitespace) EXPECT(!AK::StringUtils::is_whitespace("a\t")); } +TEST_CASE(find) +{ + String test_string = "1234567"; + EXPECT_EQ(AK::StringUtils::find(test_string, "1").value_or(1), 0u); + EXPECT_EQ(AK::StringUtils::find(test_string, "2").value_or(2), 1u); + EXPECT_EQ(AK::StringUtils::find(test_string, "3").value_or(3), 2u); + EXPECT_EQ(AK::StringUtils::find(test_string, "4").value_or(4), 3u); + EXPECT_EQ(AK::StringUtils::find(test_string, "5").value_or(5), 4u); + EXPECT_EQ(AK::StringUtils::find(test_string, "34").value_or(3), 2u); + EXPECT_EQ(AK::StringUtils::find(test_string, "78").has_value(), false); +} + TEST_MAIN(StringUtils)