diff --git a/.idea/editor.xml b/.idea/editor.xml
index fde5348..373c50f 100644
--- a/.idea/editor.xml
+++ b/.idea/editor.xml
@@ -201,7 +201,7 @@
-
+
@@ -215,7 +215,7 @@
-
+
diff --git a/include/omath/utility/pattern_scan.hpp b/include/omath/utility/pattern_scan.hpp
new file mode 100644
index 0000000..c24e778
--- /dev/null
+++ b/include/omath/utility/pattern_scan.hpp
@@ -0,0 +1,46 @@
+//
+// Created by Vlad on 10/4/2025.
+//
+
+#pragma once
+#include
+#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
+ {
+ 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::const_iterator>
+ scan_for_pattern(const std::string_view& pattern, const std::span& range);
+ private:
+ [[nodiscard]]
+ static std::expected>, PatternScanError>
+ parse_pattern(const std::string_view& pattern_string);
+ };
+} // namespace omath
\ No newline at end of file
diff --git a/source/utility/pattern_scan.cpp b/source/utility/pattern_scan.cpp
new file mode 100644
index 0000000..8e37c90
--- /dev/null
+++ b/source/utility/pattern_scan.cpp
@@ -0,0 +1,80 @@
+//
+// Created by Vlad on 10/4/2025.
+//
+#include "omath/utility/pattern_scan.hpp"
+#include
+#include
+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(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>, PatternScanError>
+ PatternScanner::parse_pattern(const std::string_view& pattern_string)
+ {
+ std::vector> 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(value));
+
+ start = end != pattern_string.end() ? std::next(end) : end;
+ }
+ return pattern;
+ }
+} // namespace omath
\ No newline at end of file
diff --git a/tests/general/unit_test_pattern_scan.cpp b/tests/general/unit_test_pattern_scan.cpp
new file mode 100644
index 0000000..d0da462
--- /dev/null
+++ b/tests/general/unit_test_pattern_scan.cpp
@@ -0,0 +1,54 @@
+//
+// Created by Vlad on 10/4/2025.
+//
+#include "gtest/gtest.h"
+#include
+#include
+
+
+TEST(unit_test_pattern_scan, read_test)
+{
+ const auto result = omath::PatternScanner::parse_pattern("FF ? ?? E9");
+
+ EXPECT_EQ(result->at(0), static_cast(0xFF));
+ 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_1)
+{
+ const auto result = omath::PatternScanner::parse_pattern(" FF ? ?? E9");
+
+ EXPECT_EQ(result->at(0), static_cast(0xFF));
+ 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_2)
+{
+ const auto result = omath::PatternScanner::parse_pattern(" FF ? ?? E9 ");
+
+ EXPECT_EQ(result->at(0), static_cast(0xFF));
+ 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_3)
+{
+ const auto result = omath::PatternScanner::parse_pattern(" FF ? ?? E9 ");
+
+ EXPECT_EQ(result->at(0), static_cast(0xFF));
+ 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