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..f524549
--- /dev/null
+++ b/include/omath/utility/pattern_scan.hpp
@@ -0,0 +1,28 @@
+//
+// Created by Vlad on 10/4/2025.
+//
+
+#pragma once
+#include
+#include
+#include
+#include
+
+// ReSharper disable once CppInconsistentNaming
+class unit_test_pattern_scan_read_test_Test;
+namespace omath
+{
+ enum class PatternScanError
+ {
+ INVALID_PATTERN_STRING
+ };
+ class PatternScanner
+ {
+ friend unit_test_pattern_scan_read_test_Test;
+ public:
+ 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..7c6b4d9
--- /dev/null
+++ b/source/utility/pattern_scan.cpp
@@ -0,0 +1,51 @@
+//
+// Created by Vlad on 10/4/2025.
+//
+#include "omath/utility/pattern_scan.hpp"
+#include
+
+namespace omath
+{
+
+ 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::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())
+ 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..0233495
--- /dev/null
+++ b/tests/general/unit_test_pattern_scan.cpp
@@ -0,0 +1,11 @@
+//
+// Created by Vlad on 10/4/2025.
+//
+#include "gtest/gtest.h"
+#include
+#include
+
+TEST(unit_test_pattern_scan, read_test)
+{
+ std::ignore = omath::PatternScanner::parse_pattern("FF ? ?? E9");
+}
\ No newline at end of file