mirror of
https://github.com/orange-cpp/omath.git
synced 2026-02-14 15:33:26 +00:00
Merge pull request #81 from orange-cpp/feature/pattern_scan
Improves pattern scanning functionality and adds corner case tests
This commit is contained in:
4
.idea/editor.xml
generated
4
.idea/editor.xml
generated
@@ -201,7 +201,7 @@
|
|||||||
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppStaticDataMemberInUnnamedStruct/@EntryIndexedValue" value="WARNING" type="string" />
|
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppStaticDataMemberInUnnamedStruct/@EntryIndexedValue" value="WARNING" type="string" />
|
||||||
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppStaticSpecifierOnAnonymousNamespaceMember/@EntryIndexedValue" value="SUGGESTION" type="string" />
|
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppStaticSpecifierOnAnonymousNamespaceMember/@EntryIndexedValue" value="SUGGESTION" type="string" />
|
||||||
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppStringLiteralToCharPointerConversion/@EntryIndexedValue" value="WARNING" type="string" />
|
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppStringLiteralToCharPointerConversion/@EntryIndexedValue" value="WARNING" type="string" />
|
||||||
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppTabsAreDisallowed/@EntryIndexedValue" value="WARNING" type="string" />
|
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppTabsAreDisallowed/@EntryIndexedValue" value="DO_NOT_SHOW" type="string" />
|
||||||
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppTemplateArgumentsCanBeDeduced/@EntryIndexedValue" value="HINT" type="string" />
|
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppTemplateArgumentsCanBeDeduced/@EntryIndexedValue" value="HINT" type="string" />
|
||||||
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppTemplateParameterNeverUsed/@EntryIndexedValue" value="HINT" type="string" />
|
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppTemplateParameterNeverUsed/@EntryIndexedValue" value="HINT" type="string" />
|
||||||
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppTemplateParameterShadowing/@EntryIndexedValue" value="WARNING" type="string" />
|
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppTemplateParameterShadowing/@EntryIndexedValue" value="WARNING" type="string" />
|
||||||
@@ -215,7 +215,7 @@
|
|||||||
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppUnmatchedPragmaEndRegionDirective/@EntryIndexedValue" value="WARNING" type="string" />
|
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppUnmatchedPragmaEndRegionDirective/@EntryIndexedValue" value="WARNING" type="string" />
|
||||||
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppUnmatchedPragmaRegionDirective/@EntryIndexedValue" value="WARNING" type="string" />
|
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppUnmatchedPragmaRegionDirective/@EntryIndexedValue" value="WARNING" type="string" />
|
||||||
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppUnnamedNamespaceInHeaderFile/@EntryIndexedValue" value="WARNING" type="string" />
|
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppUnnamedNamespaceInHeaderFile/@EntryIndexedValue" value="WARNING" type="string" />
|
||||||
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppUnnecessaryWhitespace/@EntryIndexedValue" value="WARNING" type="string" />
|
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppUnnecessaryWhitespace/@EntryIndexedValue" value="DO_NOT_SHOW" type="string" />
|
||||||
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppUnsignedZeroComparison/@EntryIndexedValue" value="WARNING" type="string" />
|
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppUnsignedZeroComparison/@EntryIndexedValue" value="WARNING" type="string" />
|
||||||
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppUnusedIncludeDirective/@EntryIndexedValue" value="WARNING" type="string" />
|
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppUnusedIncludeDirective/@EntryIndexedValue" value="WARNING" type="string" />
|
||||||
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppUseAlgorithmWithCount/@EntryIndexedValue" value="SUGGESTION" type="string" />
|
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppUseAlgorithmWithCount/@EntryIndexedValue" value="SUGGESTION" type="string" />
|
||||||
|
|||||||
46
include/omath/utility/pattern_scan.hpp
Normal file
46
include/omath/utility/pattern_scan.hpp
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
//
|
||||||
|
// Created by Vlad on 10/4/2025.
|
||||||
|
//
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include <expected>
|
||||||
|
#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
|
||||||
|
{
|
||||||
|
INVALID_PATTERN_STRING
|
||||||
|
};
|
||||||
|
class PatternScanner
|
||||||
|
{
|
||||||
|
friend unit_test_pattern_scan_read_test_Test;
|
||||||
|
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>
|
||||||
|
parse_pattern(const std::string_view& pattern_string);
|
||||||
|
};
|
||||||
|
} // namespace omath
|
||||||
80
source/utility/pattern_scan.cpp
Normal file
80
source/utility/pattern_scan.cpp
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
//
|
||||||
|
// Created by Vlad on 10/4/2025.
|
||||||
|
//
|
||||||
|
#include "omath/utility/pattern_scan.hpp"
|
||||||
|
#include <charconv>
|
||||||
|
#include <cstdint>
|
||||||
|
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>(parsed_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)
|
||||||
|
{
|
||||||
|
std::vector<std::optional<std::byte>> pattern;
|
||||||
|
|
||||||
|
auto start = pattern_string.cbegin();
|
||||||
|
|
||||||
|
while (start != pattern_string.cend())
|
||||||
|
{
|
||||||
|
const auto end = std::ranges::find(start, pattern_string.cend(), ' ');
|
||||||
|
|
||||||
|
const auto sting_view_start = std::distance(pattern_string.cbegin(), start);
|
||||||
|
const auto sting_view_end = std::distance(start, end);
|
||||||
|
|
||||||
|
const std::string_view byte_str = pattern_string.substr(sting_view_start, sting_view_end);
|
||||||
|
|
||||||
|
if (byte_str.empty())
|
||||||
|
{
|
||||||
|
start = end != pattern_string.end() ? std::next(end) : end;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (byte_str == "?" || byte_str == "??")
|
||||||
|
{
|
||||||
|
pattern.emplace_back(std::nullopt);
|
||||||
|
|
||||||
|
start = end != pattern_string.end() ? std::next(end) : end;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
} // namespace omath
|
||||||
54
tests/general/unit_test_pattern_scan.cpp
Normal file
54
tests/general/unit_test_pattern_scan.cpp
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
//
|
||||||
|
// Created by Vlad on 10/4/2025.
|
||||||
|
//
|
||||||
|
#include "gtest/gtest.h"
|
||||||
|
#include <omath/utility/pattern_scan.hpp>
|
||||||
|
#include <source_location>
|
||||||
|
|
||||||
|
|
||||||
|
TEST(unit_test_pattern_scan, read_test)
|
||||||
|
{
|
||||||
|
const auto result = omath::PatternScanner::parse_pattern("FF ? ?? E9");
|
||||||
|
|
||||||
|
EXPECT_EQ(result->at(0), static_cast<std::byte>(0xFF));
|
||||||
|
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_1)
|
||||||
|
{
|
||||||
|
const auto result = omath::PatternScanner::parse_pattern(" FF ? ?? E9");
|
||||||
|
|
||||||
|
EXPECT_EQ(result->at(0), static_cast<std::byte>(0xFF));
|
||||||
|
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_2)
|
||||||
|
{
|
||||||
|
const auto result = omath::PatternScanner::parse_pattern(" FF ? ?? E9 ");
|
||||||
|
|
||||||
|
EXPECT_EQ(result->at(0), static_cast<std::byte>(0xFF));
|
||||||
|
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_3)
|
||||||
|
{
|
||||||
|
const auto result = omath::PatternScanner::parse_pattern(" FF ? ?? E9 ");
|
||||||
|
|
||||||
|
EXPECT_EQ(result->at(0), static_cast<std::byte>(0xFF));
|
||||||
|
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());
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user