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.
This commit is contained in:
AnotherTest 2021-01-12 23:28:45 +03:30 committed by Andreas Kling
parent 4fa8435310
commit 39442e6d4f
Notes: sideshowbarker 2024-07-18 23:53:03 +09:00
8 changed files with 70 additions and 11 deletions

View file

@ -120,28 +120,39 @@ static inline Optional<size_t> memmem(const HaystackIterT& haystack_begin, const
return {}; return {};
} }
static inline const void* memmem(const void* haystack, size_t haystack_length, const void* needle, size_t needle_length) static inline Optional<size_t> memmem_optional(const void* haystack, size_t haystack_length, const void* needle, size_t needle_length)
{ {
if (needle_length == 0) if (needle_length == 0)
return haystack; return 0;
if (haystack_length < needle_length) if (haystack_length < needle_length)
return nullptr; return {};
if (haystack_length == needle_length)
return __builtin_memcmp(haystack, needle, haystack_length) == 0 ? haystack : nullptr;
if (needle_length < 32)
return bitap_bitwise(haystack, haystack_length, needle, needle_length);
// Fallback to KMP.
Array<Span<const u8>, 1> spans { Span<const u8> { (const u8*)haystack, haystack_length } };
auto result = memmem(spans.begin(), spans.end(), { (const u8*)needle, needle_length });
if (result.has_value())
return (const u8*)haystack + result.value();
if (haystack_length == needle_length) {
if (__builtin_memcmp(haystack, needle, haystack_length) == 0)
return 0;
return {}; return {};
} }
if (needle_length < 32) {
auto ptr = bitap_bitwise(haystack, haystack_length, needle, needle_length);
if (ptr)
return static_cast<size_t>((FlatPtr)ptr - (FlatPtr)haystack);
return {};
}
// Fallback to KMP.
Array<Span<const u8>, 1> spans { Span<const u8> { (const u8*)haystack, haystack_length } };
return memmem(spans.begin(), spans.end(), { (const u8*)needle, needle_length });
}
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 nullptr;
}
} }

View file

@ -469,4 +469,14 @@ String String::vformatted(StringView fmtstr, TypeErasedFormatParams params)
return builder.to_string(); return builder.to_string();
} }
Optional<size_t> String::find(char c) const
{
return find(StringView { &c, 1 });
}
Optional<size_t> String::find(const StringView& view) const
{
return StringUtils::find(*this, view);
}
} }

View file

@ -138,6 +138,10 @@ public:
Vector<String> split_limit(char separator, size_t limit, bool keep_empty = false) const; Vector<String> split_limit(char separator, size_t limit, bool keep_empty = false) const;
Vector<String> split(char separator, bool keep_empty = false) const; Vector<String> split(char separator, bool keep_empty = false) const;
Optional<size_t> find(char) const;
Optional<size_t> find(const StringView&) const;
String substring(size_t start) const; String substring(size_t start) const;
String substring(size_t start, size_t length) const; String substring(size_t start, size_t length) const;

View file

@ -25,6 +25,7 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
#include <AK/MemMem.h>
#include <AK/Memory.h> #include <AK/Memory.h>
#include <AK/Optional.h> #include <AK/Optional.h>
#include <AK/String.h> #include <AK/String.h>
@ -342,6 +343,13 @@ StringView trim_whitespace(const StringView& str, TrimMode mode)
return str.substring_view(substring_start, substring_length); return str.substring_view(substring_start, substring_length);
} }
Optional<size_t> 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());
}
} }
} }

View file

@ -71,6 +71,7 @@ bool starts_with(const StringView&, const StringView&, CaseSensitivity);
bool contains(const StringView&, const StringView&, CaseSensitivity); bool contains(const StringView&, const StringView&, CaseSensitivity);
bool is_whitespace(const StringView&); bool is_whitespace(const StringView&);
StringView trim_whitespace(const StringView&, TrimMode mode); StringView trim_whitespace(const StringView&, TrimMode mode);
Optional<size_t> find(const StringView& haystack, const StringView& needle);
} }
} }

View file

@ -310,6 +310,16 @@ Optional<size_t> StringView::find_last_of(const StringView& view) const
return {}; return {};
} }
Optional<size_t> StringView::find(char c) const
{
return find(StringView { &c, 1 });
}
Optional<size_t> StringView::find(const StringView& view) const
{
return StringUtils::find(*this, view);
}
String StringView::to_string() const { return String { *this }; } String StringView::to_string() const { return String { *this }; }
} }

View file

@ -100,6 +100,9 @@ public:
Optional<size_t> find_last_of(char) const; Optional<size_t> find_last_of(char) const;
Optional<size_t> find_last_of(const StringView&) const; Optional<size_t> find_last_of(const StringView&) const;
Optional<size_t> find(const StringView&) const;
Optional<size_t> find(char c) const;
StringView substring_view(size_t start, size_t length) const; StringView substring_view(size_t start, size_t length) const;
StringView substring_view(size_t start) const; StringView substring_view(size_t start) const;
Vector<StringView> split_view(char, bool keep_empty = false) const; Vector<StringView> split_view(char, bool keep_empty = false) const;

View file

@ -298,4 +298,16 @@ TEST_CASE(is_whitespace)
EXPECT(!AK::StringUtils::is_whitespace("a\t")); 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) TEST_MAIN(StringUtils)