From 06f9a8c9eed45529b13c8477fdea9abb1164e3a5 Mon Sep 17 00:00:00 2001 From: Orange Date: Sun, 5 Oct 2025 06:37:20 +0300 Subject: [PATCH] Adds pattern scanning functionality and corner case tests Implements a pattern scanning feature that allows searching for byte patterns within a byte range. Adds `scan_for_pattern` method to `PatternScanner` to locate a pattern within a byte span. Adds corner case tests to verify functionality and handle invalid inputs. --- include/omath/utility/pattern_scan.hpp | 12 ++++++++++ source/utility/pattern_scan.cpp | 30 ++++++++++++++++++++++-- tests/general/unit_test_pattern_scan.cpp | 7 ++++++ 3 files changed, 47 insertions(+), 2 deletions(-) diff --git a/include/omath/utility/pattern_scan.hpp b/include/omath/utility/pattern_scan.hpp index 3f5ce6d..c24e778 100644 --- a/include/omath/utility/pattern_scan.hpp +++ b/include/omath/utility/pattern_scan.hpp @@ -7,12 +7,20 @@ #include #include #include +#include // ReSharper disable once CppInconsistentNaming class unit_test_pattern_scan_read_test_Test; +// ReSharper disable once CppInconsistentNaming class unit_test_pattern_scan_corner_case_1_Test; +// ReSharper disable once CppInconsistentNaming class unit_test_pattern_scan_corner_case_2_Test; +// ReSharper disable once CppInconsistentNaming class unit_test_pattern_scan_corner_case_3_Test; +// ReSharper disable once CppInconsistentNaming +class unit_test_pattern_scan_corner_case_4_Test; + + namespace omath { enum class PatternScanError @@ -25,7 +33,11 @@ namespace omath friend unit_test_pattern_scan_corner_case_1_Test; friend unit_test_pattern_scan_corner_case_2_Test; friend unit_test_pattern_scan_corner_case_3_Test; + friend unit_test_pattern_scan_corner_case_4_Test; public: + [[nodiscard]] + static std::optional::const_iterator> + scan_for_pattern(const std::string_view& pattern, const std::span& range); private: [[nodiscard]] static std::expected>, PatternScanError> diff --git a/source/utility/pattern_scan.cpp b/source/utility/pattern_scan.cpp index b2ae08e..c15ff68 100644 --- a/source/utility/pattern_scan.cpp +++ b/source/utility/pattern_scan.cpp @@ -7,6 +7,33 @@ namespace omath { + std::optional::const_iterator> + PatternScanner::scan_for_pattern(const std::string_view& pattern, const std::span& range) + { + const auto parsed_pattern = parse_pattern(pattern); + + if (!parsed_pattern) + return std::nullopt; + + const std::ptrdiff_t scan_size = + static_cast(range.size()) - static_cast(pattern.size()); + + for (std::ptrdiff_t i = 0; i < scan_size; i++) + { + bool found = true; + + for (std::ptrdiff_t j = 0; j < static_cast(pattern.size()); j++) + { + found = parsed_pattern->at(j) == std::nullopt || parsed_pattern->at(j) == *(range.data() + i + j); + + if (!found) + break; + } + if (found) + return range.begin() + i; + } + return std::nullopt; + } std::expected>, PatternScanError> PatternScanner::parse_pattern(const std::string_view& pattern_string) { @@ -39,14 +66,13 @@ namespace omath std::uint8_t value = 0; // ReSharper disable once CppTooWideScopeInitStatement - const auto [_, error_code] = std::from_chars(byte_str.data(), byte_str.data()+byte_str.size(), value, 16); + const auto [_, error_code] = std::from_chars(byte_str.data(), byte_str.data() + byte_str.size(), value, 16); if (error_code != std::errc{}) return std::unexpected(PatternScanError::INVALID_PATTERN_STRING); pattern.emplace_back(static_cast(value)); - start = end != pattern_string.end() ? std::next(end) : end; } return pattern; diff --git a/tests/general/unit_test_pattern_scan.cpp b/tests/general/unit_test_pattern_scan.cpp index 9b03486..d0da462 100644 --- a/tests/general/unit_test_pattern_scan.cpp +++ b/tests/general/unit_test_pattern_scan.cpp @@ -44,4 +44,11 @@ TEST(unit_test_pattern_scan, corner_case_3) EXPECT_EQ(result->at(1), std::nullopt); EXPECT_EQ(result->at(2), std::nullopt); EXPECT_EQ(result->at(3), static_cast(0xE9)); +} + +TEST(unit_test_pattern_scan, corner_case_4) +{ + const auto result = omath::PatternScanner::parse_pattern("X ? ?? E9 "); + + EXPECT_FALSE(result.has_value()); } \ No newline at end of file