From 60a798cdce574021886e3a3f72df2099cf825d54 Mon Sep 17 00:00:00 2001 From: Stenzek Date: Mon, 21 Sep 2026 15:00:17 +1000 Subject: [PATCH] StringUtil: Return first match in BytePatternSearch() Also validate byte pattern syntax and improve unit test coverage. --- src/common-tests/string_tests.cpp | 22 ++++++++++++++++++++++ src/common/string_util.cpp | 6 +++--- 2 files changed, 25 insertions(+), 3 deletions(-) diff --git a/src/common-tests/string_tests.cpp b/src/common-tests/string_tests.cpp index 91f5052a8..96a586f4a 100644 --- a/src/common-tests/string_tests.cpp +++ b/src/common-tests/string_tests.cpp @@ -1008,6 +1008,12 @@ TEST(StringUtil, BytePatternSearch) ASSERT_TRUE(result.has_value()); ASSERT_EQ(result.value(), 2u); + // Test that the first match is returned. + const std::vector duplicate_data = {0x01, 0x02, 0x01, 0x02}; + result = StringUtil::BytePatternSearch(std::span(duplicate_data), "01 02"); + ASSERT_TRUE(result.has_value()); + ASSERT_EQ(result.value(), 0u); + // Test with wildcards result = StringUtil::BytePatternSearch(std::span(data), "01 ?? 03"); ASSERT_TRUE(result.has_value()); @@ -1021,6 +1027,12 @@ TEST(StringUtil, BytePatternSearch) result = StringUtil::BytePatternSearch(std::span(data), ""); ASSERT_FALSE(result.has_value()); + // Test malformed patterns. + result = StringUtil::BytePatternSearch(std::span(data), "01 2"); + ASSERT_FALSE(result.has_value()); + result = StringUtil::BytePatternSearch(std::span(data), "01 GG"); + ASSERT_FALSE(result.has_value()); + // Test lowercase hex result = StringUtil::BytePatternSearch(std::span(data), "01 02 03"); ASSERT_TRUE(result.has_value()); @@ -1036,6 +1048,16 @@ TEST(StringUtil, BytePatternSearch) ASSERT_TRUE(result.has_value()); ASSERT_EQ(result.value(), 5u); + // Test a wildcard match at the final valid offset. + result = StringUtil::BytePatternSearch(std::span(data), "06 ?? 08"); + ASSERT_TRUE(result.has_value()); + ASSERT_EQ(result.value(), 5u); + + // Test a pattern exactly the size of the input. + result = StringUtil::BytePatternSearch(std::span(data), "01 02 03 04 05 06 07 08"); + ASSERT_TRUE(result.has_value()); + ASSERT_EQ(result.value(), 0u); + // Test a pattern longer than the input. result = StringUtil::BytePatternSearch(std::span(data), "01 02 03 04 05 06 07 08 09"); ASSERT_FALSE(result.has_value()); diff --git a/src/common/string_util.cpp b/src/common/string_util.cpp index 4f953d2f2..4b6fa37e2 100644 --- a/src/common/string_util.cpp +++ b/src/common/string_util.cpp @@ -1095,10 +1095,10 @@ std::optional StringUtil::BytePatternSearch(const std::span by } else { - break; + return std::nullopt; } } - if (pattern_length == 0) + if (pattern_length == 0 || !hinibble) return std::nullopt; if (pattern_length > bytes.size()) return std::nullopt; @@ -1145,7 +1145,7 @@ std::optional StringUtil::BytePatternSearch(const std::span by std::optional ret; const size_t max_search_offset = bytes.size() - pattern_length; - for (size_t offset = 0; offset <= max_search_offset; offset++) + for (size_t offset = 0; offset <= max_search_offset && !ret.has_value(); offset++) { const u8* start = bytes.data() + offset; for (size_t match_offset = 0;;)