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.
This commit is contained in:
2025-10-05 06:37:20 +03:00
parent 160b51da94
commit 06f9a8c9ee
3 changed files with 47 additions and 2 deletions

View File

@@ -7,12 +7,20 @@
#include <optional>
#include <string_view>
#include <vector>
#include <span>
// 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<std::span<std::byte>::const_iterator>
scan_for_pattern(const std::string_view& pattern, const std::span<std::byte>& range);
private:
[[nodiscard]]
static std::expected<std::vector<std::optional<std::byte>>, PatternScanError>

View File

@@ -7,6 +7,33 @@
namespace omath
{
std::optional<std::span<std::byte>::const_iterator>
PatternScanner::scan_for_pattern(const std::string_view& pattern, const std::span<std::byte>& range)
{
const auto parsed_pattern = parse_pattern(pattern);
if (!parsed_pattern)
return std::nullopt;
const std::ptrdiff_t scan_size =
static_cast<std::ptrdiff_t>(range.size()) - static_cast<std::ptrdiff_t>(pattern.size());
for (std::ptrdiff_t i = 0; i < scan_size; i++)
{
bool found = true;
for (std::ptrdiff_t j = 0; j < static_cast<std::ptrdiff_t>(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<std::vector<std::optional<std::byte>>, 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<std::byte>(value));
start = end != pattern_string.end() ? std::next(end) : end;
}
return pattern;

View File

@@ -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<std::byte>(0xE9));
}
TEST(unit_test_pattern_scan, corner_case_4)
{
const auto result = omath::PatternScanner::parse_pattern("X ? ?? E9 ");
EXPECT_FALSE(result.has_value());
}