mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2024-11-22 07:30:19 +00:00
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:
parent
4fa8435310
commit
39442e6d4f
Notes:
sideshowbarker
2024-07-18 23:53:03 +09:00
Author: https://github.com/alimpfard Commit: https://github.com/SerenityOS/serenity/commit/39442e6d4f0 Pull-request: https://github.com/SerenityOS/serenity/pull/4929 Issue: https://github.com/SerenityOS/serenity/issues/4926
8 changed files with 70 additions and 11 deletions
43
AK/MemMem.h
43
AK/MemMem.h
|
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 }; }
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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)
|
||||||
|
|
Loading…
Reference in a new issue