From 18f78b291e14c7bb2565362cec074c53ce41fcce Mon Sep 17 00:00:00 2001 From: Brady Date: Thu, 30 Jul 2026 19:33:04 -0500 Subject: [PATCH 01/20] Initial adaptive anchor implementation --- include/libhat/scanner.hpp | 158 +++++++++++----------- src/Scanner.cpp | 265 +++++++++++++++++++++++++++++++------ src/Utils.hpp | 52 ++++++-- src/arch/arm/Neon.cpp | 19 +-- src/arch/x86/AVX2.cpp | 21 +-- src/arch/x86/AVX512.cpp | 21 +-- src/arch/x86/SSE.cpp | 21 +-- 7 files changed, 370 insertions(+), 187 deletions(-) diff --git a/include/libhat/scanner.hpp b/include/libhat/scanner.hpp index 39a8e44..462c456 100644 --- a/include/libhat/scanner.hpp +++ b/include/libhat/scanner.hpp @@ -141,12 +141,9 @@ namespace hat::detail { using scan_function_t = const_scan_result(*)(const std::byte* begin, const std::byte* end, const scan_context& context); - struct scanner_context { - std::size_t vectorSize{}; - }; - enum class scan_mode { Auto, // Picks a mode at runtime + Search, // std::search Single, // std::find + std::equal SSE, // x86/x64 SSE 4.1 AVX2, // x86/x64 AVX2 @@ -155,28 +152,56 @@ namespace hat::detail { }; class scan_context { + static constexpr size_t impl_buffer_size = 32; + static constexpr size_t impl_buffer_align = alignof(std::max_align_t); public: - signature_view signature{}; - scan_function_t scanner{}; - scan_alignment alignment{}; - scan_hint hints{}; - std::size_t cmpIndex{}; - std::optional pairIndex{}; + scan_context(const scan_context&) = delete; + scan_context(scan_context&&) = delete; + scan_context& operator=(const scan_context&) = delete; + scan_context& operator=(scan_context&&) = delete; + + constexpr ~scan_context() { + if (impl_deleter_) { + impl_deleter_(impl_buffer_.data()); + impl_deleter_ = nullptr; + } + } + + [[nodiscard]] constexpr signature_view signature() const { + return signature_; + } + + template + void emplace(Args&&... args); + + template + [[nodiscard]] const T& get() const; [[nodiscard]] constexpr const_scan_result scan(const std::byte* begin, const std::byte* end) const { - if (signature.size() > static_cast(std::distance(begin, end))) LIBHAT_UNLIKELY { + if (signature_.size() > static_cast(std::distance(begin, end))) LIBHAT_UNLIKELY { return {}; } - return this->scanner(begin, end, *this); + return scanner_(begin, end, *this); } - void apply_hints(const scanner_context&); - template static constexpr scan_context create(signature_view signature, scan_alignment alignment, scan_hint hints); + template + scan_context(const signature_view signature, const scan_function_t scanner, std::type_identity, Args&&... args) + : signature_(signature), scanner_(scanner) + { + emplace(std::forward(args)...); + } + + constexpr scan_context(const signature_view signature, const scan_function_t scanner) + : signature_(signature), scanner_(scanner) {} + private: - scan_context() = default; + signature_view signature_{}; + scan_function_t scanner_{}; + void(*impl_deleter_)(const void*){}; + alignas(impl_buffer_align) std::array impl_buffer_; }; LIBHAT_FORCEINLINE constexpr auto to_stride(const scan_alignment alignment) { @@ -202,77 +227,48 @@ namespace hat::detail { return std::assume_aligned(ptr); } + struct scan_parameters { + signature_view signature{}; + scan_alignment alignment{}; + scan_hint hints{}; + }; + template - scan_function_t resolve_scanner(scan_context&); + scan_context create_context(const scan_parameters&); template<> - scan_function_t resolve_scanner(scan_context&); + scan_context create_context(const scan_parameters&); template - const_scan_result find_pattern_single(const std::byte* begin, const std::byte* end, const scan_context& context) { + const_scan_result find_pattern_search(const std::byte* begin, const std::byte* end, const scan_context& context) { static constexpr auto stride = alignment_stride; - const auto signature = context.signature; - const auto cmpByte = *signature[context.cmpIndex]; - - const auto scanBegin = align_up(begin) + context.cmpIndex; - const auto scanEnd = align_up(end - signature.size() + 1) + context.cmpIndex; - if (scanBegin >= scanEnd) { - return nullptr; - } + const auto sig = context.signature(); + const auto scanBegin = align_up(begin); + const auto scanEnd = align_up(end - sig.size() + 1); + // intentionally kept simple/inefficient since this will only be used for small buffers for (auto i = scanBegin; i != scanEnd; i += stride) { - if (*i == cmpByte) { - const auto start = i - context.cmpIndex; - const auto match = std::equal(signature.begin(), signature.end(), start); - if (match) LIBHAT_UNLIKELY { - return start; - } + if (std::equal(sig.begin(), sig.end(), i)) { + return i; } } - return nullptr; } template<> - constexpr const_scan_result find_pattern_single(const std::byte* begin, const std::byte* end, const scan_context& context) { - const auto signature = context.signature; - const auto firstByte = *signature[context.cmpIndex]; - const auto scanEnd = end - signature.size() + 1 + context.cmpIndex; - - for (auto i = begin + context.cmpIndex; i != scanEnd; i++) { - // Use std::find to efficiently find the first byte - if LIBHAT_IF_CONSTEVAL { - i = std::find(i, scanEnd, firstByte); - if (i == scanEnd) LIBHAT_UNLIKELY break; - } else { - #ifndef _MSC_VER - i = static_cast( - std::memchr(i, static_cast(firstByte), static_cast(scanEnd - i))); - if (!i) LIBHAT_UNLIKELY break; - #elif __cpp_lib_execution >= 201902L - i = std::find(std::execution::unseq, i, scanEnd, firstByte); - if (i == scanEnd) LIBHAT_UNLIKELY break; - #else - i = std::find(i, scanEnd, firstByte); - if (i == scanEnd) LIBHAT_UNLIKELY break; - #endif - } - const auto start = i - context.cmpIndex; - const auto match = std::equal(signature.begin(), signature.end(), start); - if (match) LIBHAT_UNLIKELY { - return start; - } - } - return nullptr; + constexpr const_scan_result find_pattern_search(const std::byte* begin, const std::byte* end, const scan_context& context) { + const auto sig = context.signature(); + const auto it = std::search(begin, end, sig.begin(), sig.end()); + return it != end ? it : nullptr; } template<> - constexpr scan_function_t resolve_scanner(scan_context& context) { - switch (context.alignment) { - case scan_alignment::X1: return &find_pattern_single; - case scan_alignment::X4: return &find_pattern_single; - case scan_alignment::X16: return &find_pattern_single; + inline scan_context create_context(const scan_parameters& params) { + switch (params.alignment) { + case scan_alignment::X1: return {params.signature, &find_pattern_search}; + case scan_alignment::X4: return {params.signature, &find_pattern_search}; + case scan_alignment::X16: return {params.signature, &find_pattern_search}; } LIBHAT_UNREACHABLE(); } @@ -283,25 +279,19 @@ namespace hat::detail { template constexpr scan_context scan_context::create(const signature_view signature, const scan_alignment alignment, const scan_hint hints) { - std::size_t cmpIndex{}; - for (const auto& elem : signature) { - if (elem.all()) { - break; - } - cmpIndex++; - } - - scan_context ctx{}; - ctx.signature = signature; - ctx.alignment = alignment; - ctx.hints = hints; - ctx.cmpIndex = cmpIndex; + const scan_parameters params{ + .signature = signature, + .alignment = alignment, + .hints = hints, + }; if LIBHAT_IF_CONSTEVAL { - ctx.scanner = resolve_scanner(ctx); + if (alignment != scan_alignment::X1) { + std::abort(); + } + return {signature, &find_pattern_search}; } else { - ctx.scanner = resolve_scanner(ctx); + return create_context(params); } - return ctx; } } diff --git a/src/Scanner.cpp b/src/Scanner.cpp index ea3f472..be7fc42 100644 --- a/src/Scanner.cpp +++ b/src/Scanner.cpp @@ -11,6 +11,94 @@ #include "arch/arm/Frequency.hpp" #endif +#include "Utils.hpp" + +namespace { + + struct anchor_matcher { + + anchor_matcher(const hat::signature_view& signature, const std::span& anchors, const std::size_t literals) : + signature_(signature), + anchors_(anchors), + literals_(literals) {} + + [[nodiscard]] std::pair top() const { + return anchor(0); + } + + [[nodiscard]] bool matches(const std::byte* buffer) const { + for (size_t i = 1; i < literals_; i++) { + const auto [element, offset] = anchor(i); + if (buffer[offset] != element) { + std::swap(anchors_[i], anchors_[i - 1]); + return false; + } + } + + for (size_t i = literals_; i < anchors_.size(); i++) { + const auto [element, offset] = anchor(i); + if (buffer[offset] != element) { + return false; + } + } + + return true; + } + + [[nodiscard]] std::size_t literals() const { + return literals_; + } + + private: + [[nodiscard]] std::pair anchor(const size_t index) const { + const auto offset = anchors_[index]; + return {signature_[offset], offset}; + } + + hat::signature_view signature_; + std::span anchors_; + std::size_t literals_; + }; + + struct signature_info { + std::optional optimalByteIndex{}; // The index of the least common fully masked element + std::size_t anyMaskCount{}; // The number of elements with a non-zero mask + }; + + struct std_context { + mutable std::vector anchors{}; + std::size_t literals{}; + + explicit std_context(const hat::detail::scan_parameters& params) { + this->anchors.resize(static_cast( + std::ranges::count_if(params.signature, &hat::signature_element::any))); + auto it = this->anchors.begin(); + + // Add fully masked bytes + for (size_t i{}; auto e : params.signature) { + if (e.all()) { + *it++ = i; + } + i++; + } + this->literals = static_cast(std::distance(this->anchors.begin(), it)); + + // Add partially masked bytes + for (size_t i{}; auto e : params.signature) { + if (!e.all() && !e.none()) { + *it++ = i; + } + i++; + } + } + }; + + anchor_matcher create_anchor_matcher(const hat::detail::scan_context& context) { + const auto& std = context.get(); + return {context.signature(), std.anchors, std.literals}; + } +} + namespace hat::detail { static constexpr std::uint16_t NUM_PAIRS = 512; @@ -34,11 +122,11 @@ namespace hat::detail { return std::nullopt; } - void scan_context::apply_hints(const scanner_context& scanner) { - const bool pair0 = static_cast(this->hints & scan_hint::pair0); + std::optional get_optimal_pair(const scan_parameters& params) { + const bool pair0 = static_cast(params.hints & scan_hint::pair0); - const auto pair_hint = get_pair_hint(this->hints); - if (pair_hint && !pair0 && scanner.vectorSize) { + const auto pair_hint = get_pair_hint(params.hints); + if (pair_hint && !pair0) { const auto getScore = [&](const std::byte a, const std::byte b) -> std::uint16_t { const auto& [pairs, scores] = *pair_hint; const std::pair pair{a, b}; @@ -47,71 +135,168 @@ namespace hat::detail { return it != pairs.end() && *it == pair ? scores[index] : NUM_PAIRS; }; - std::optional> bestPair{}; - for (auto it = this->signature.begin(); it != std::prev(this->signature.end()); it++) { - const auto i = static_cast(it - this->signature.begin()); + std::optional> best{}; + for (auto it = params.signature.begin(); it != std::prev(params.signature.end()); it++) { + const auto i = static_cast(it - params.signature.begin()); auto& a = *it; auto& b = *std::next(it); if (a.all() && b.all()) { const auto score = getScore(a.value(), b.value()); - if (!bestPair || score > bestPair->second) { - bestPair.emplace(i, score); + if (!best || score > best->second) { + best.emplace(i, score); } } } - if (bestPair) { - this->pairIndex = bestPair->first; + if (best) { + return best->first; } } // If no "optimal" pair was found, find the first byte pair in the signature - if (!this->pairIndex.has_value()) { - for (auto it = this->signature.begin(); it != std::prev(this->signature.end()); it++) { - const auto i = static_cast(it - this->signature.begin()); - auto& a = *it; - auto& b = *std::next(it); + for (auto it = params.signature.begin(); it != std::prev(params.signature.end()); it++) { + const auto i = static_cast(it - params.signature.begin()); + auto& a = *it; + auto& b = *std::next(it); - if (a.all() && b.all()) { - this->pairIndex = i; - break; - } - if (i == 0 && pair0) { - break; + if (a.all() && b.all()) { + return i; + } + if (i == 0 && pair0) { + break; + } + } + + return {}; + } + + std::optional get_optimal_byte(const scan_parameters& params) { + const auto signature = params.signature; + + std::array counts{}; + for (const auto element : signature) { + const auto value = std::to_integer(element.value()); + auto& count = counts[value]; + if (element.all() && count < 0xFF) { + count++; + } + } + + std::optional min{}; + uint8_t minCount = 0xFF; + for (std::size_t i = 0; i < signature.size(); i++) { + auto& element = signature[i]; + const auto count = std::to_integer(element.value()); + if (!min || count < minCount) { + min = i; + minCount = count; + } + } + + return min; + } + + template + static const_scan_result find_pattern_single(const std::byte* begin, const std::byte* end, const scan_context& context) { + static constexpr auto stride = alignment_stride; + + const auto scanBegin = align_up(begin); + const auto scanEnd = align_up(end - context.signature().size() + 1); + if (scanBegin >= scanEnd) { + return nullptr; + } + + const auto matcher = create_anchor_matcher(context); + for (auto i = scanBegin; i != scanEnd; i += stride) { + if (matcher.literals() > 0) { + const auto [anchor, offset] = matcher.top(); + while (i != scanEnd) { + if (i[offset] == anchor) { + break; + } + i += stride; } + if (i == scanEnd) LIBHAT_UNLIKELY break; + } + if (matcher.matches(i)) { + return i; } } + + return nullptr; } template<> - scan_function_t resolve_scanner(scan_context& context) { - const auto& ext = get_system().extensions; -#if defined(LIBHAT_X86) || defined(LIBHAT_X86_64) - if (compiled_extensions.bmi || ext.bmi) { -#if defined(LIBHAT_X86_64) && defined(LIBHAT_FEATURE_AVX512) - if ((compiled_extensions.avx512f || ext.avx512f) - && (compiled_extensions.avx512bw || ext.avx512bw)) { - return resolve_scanner(context); + const_scan_result find_pattern_single(const std::byte* begin, const std::byte* end, const scan_context& context) { + const auto signature = context.signature(); + const auto scanEnd = end - signature.size() + 1; + + const auto matcher = create_anchor_matcher(context); + for (auto i = begin; i != scanEnd; i++) { + // This check should get hoisted out by the optimizer + if (matcher.literals() > 0) { + const auto [anchor, offset] = matcher.top(); + + // Use std::find to efficiently find the first byte + #ifndef _MSC_VER + i = static_cast( + std::memchr(i + offset, static_cast(anchor.value()), static_cast(scanEnd - i))); + if (!i) LIBHAT_UNLIKELY break; + i -= offset; + #elif __cpp_lib_execution >= 201902L + i = std::find(std::execution::unseq, i + offset, scanEnd + offset, anchor.value()) - offset; + if (i == scanEnd) LIBHAT_UNLIKELY break; + #else + i = std::find(i + offset, scanEnd + offset, anchor.value()) - offset; + if (i == scanEnd) LIBHAT_UNLIKELY break; + #endif } -#endif - if (compiled_extensions.avx2 || ext.avx2) { - return resolve_scanner(context); + if (matcher.matches(i)) { + return i; } } -#if defined(LIBHAT_FEATURE_SSE) - if (compiled_extensions.sse41 || ext.sse41) { - return resolve_scanner(context); + return nullptr; + } + + template<> + scan_context create_context(const scan_parameters& params) { + switch (params.alignment) { + case scan_alignment::X1: return {params.signature, &find_pattern_single, std::type_identity{}, params}; + case scan_alignment::X4: return {params.signature, &find_pattern_single, std::type_identity{}, params}; + case scan_alignment::X16: return {params.signature, &find_pattern_single, std::type_identity{}, params}; } -#endif -#endif + LIBHAT_UNREACHABLE(); + } + + template<> + scan_context create_context(const scan_parameters& params) { +// const auto& ext = get_system().extensions; +// #if defined(LIBHAT_X86) || defined(LIBHAT_X86_64) +// if (compiled_extensions.bmi || ext.bmi) { +// #if defined(LIBHAT_X86_64) && defined(LIBHAT_FEATURE_AVX512) +// if ((compiled_extensions.avx512f || ext.avx512f) +// && (compiled_extensions.avx512bw || ext.avx512bw)) { +// return create_context(params); +// } +// #endif +// if (compiled_extensions.avx2 || ext.avx2) { +// return create_context(params); +// } +// } +// #if defined(LIBHAT_FEATURE_SSE) +// if (compiled_extensions.sse41 || ext.sse41) { +// return create_context(params); +// } +// #endif +// #endif #if defined(LIBHAT_ARM) || defined(LIBHAT_AARCH64) if (compiled_extensions.neon || ext.neon) { - return resolve_scanner(context); + return create_context(params); } #endif // If none of the vectorized implementations are available/supported, then fallback to scanning per-byte - return resolve_scanner(context); + return create_context(params); } } diff --git a/src/Utils.hpp b/src/Utils.hpp index 906f29b..fd90514 100644 --- a/src/Utils.hpp +++ b/src/Utils.hpp @@ -10,28 +10,64 @@ namespace hat::detail { - constexpr std::uintptr_t fast_align_down(std::uintptr_t address, std::size_t alignment) { + template + void scan_context::emplace(Args&&... args) { + static_assert(std::is_same_v>); + static_assert(sizeof(T) <= impl_buffer_size); + static_assert(alignof(T) <= impl_buffer_align); + + std::construct_at(reinterpret_cast(impl_buffer_.data()), std::forward(args)...); + + if constexpr (not std::is_trivially_destructible_v) { + impl_deleter_ = [](const void* buffer) { + std::destroy_at(static_cast(buffer)); + }; + } + } + + template + const T& scan_context::get() const { + return *reinterpret_cast(impl_buffer_.data()); + } + + constexpr std::uintptr_t fast_align_down(const std::uintptr_t address, const std::size_t alignment) { return address & ~static_cast(alignment - 1); } - constexpr std::uintptr_t fast_align_up(std::uintptr_t address, std::size_t alignment) { + constexpr std::uintptr_t fast_align_up(const std::uintptr_t address, const std::size_t alignment) { return (address + alignment - 1) & ~static_cast(alignment - 1); } + std::optional get_optimal_pair(const scan_parameters& params); + std::optional get_optimal_byte(const scan_parameters& params); + + struct simd_context { + std::size_t cmpIndex{}; + }; + template - auto* find_specialization_switch(const scan_alignment alignment, const bool cmpeq2, const bool veccmp) { - const auto with_alignment = [&](std::integral_constant) { + scan_context create_simd_scanner(const scan_parameters& params, const size_t width) { + const bool veccmp = params.signature.size() <= width; + auto cmpIndex = get_optimal_pair(params); + const bool cmpeq2 = cmpIndex.has_value(); + if (!cmpIndex) cmpIndex = get_optimal_byte(params); + + const auto resolve = [&](std::integral_constant) { if (cmpeq2 && veccmp) return impl.template operator()(); if (cmpeq2) return impl.template operator()(); if (veccmp) return impl.template operator()(); return impl.template operator()(); }; - switch (alignment) { + const auto create = [&](std::integral_constant a) { + return scan_context{params.signature, resolve(a), std::type_identity{}, *cmpIndex}; + }; + + switch (params.alignment) { using enum scan_alignment; - case X1: return with_alignment(std::integral_constant{}); - case X4: return with_alignment(std::integral_constant{}); - case X16: return with_alignment(std::integral_constant{}); + case X1: return create(std::integral_constant{}); + case X4: return create(std::integral_constant{}); + case X16: return create(std::integral_constant{}); } LIBHAT_UNREACHABLE(); } diff --git a/src/arch/arm/Neon.cpp b/src/arch/arm/Neon.cpp index 264910b..3f44e3b 100644 --- a/src/arch/arm/Neon.cpp +++ b/src/arch/arm/Neon.cpp @@ -53,7 +53,7 @@ namespace hat::detail { template static const_scan_result find_pattern_neon(const std::byte* begin, const std::byte* end, const scan_context& context) { const auto signature = context.signature; - const auto cmpIndex = cmpeq2 ? *context.pairIndex : context.cmpIndex; + const auto cmpIndex = context.get().cmpIndex; // 128 bit vector containing first signature byte repeated const auto firstByte = vdupq_n_u8(static_cast(*signature[cmpIndex])); @@ -71,7 +71,7 @@ namespace hat::detail { auto [pre, vec, post] = segment_scan(begin, end, signature.size(), cmpIndex); if (!pre.empty()) { - const auto result = find_pattern_single(pre.data(), pre.data() + pre.size(), context); + const auto result = find_pattern_search(pre.data(), pre.data() + pre.size(), context); if (result.has_result()) { return result; } @@ -115,23 +115,16 @@ namespace hat::detail { } if (!post.empty()) { - return find_pattern_single(post.data(), post.data() + post.size(), context); + return find_pattern_search(post.data(), post.data() + post.size(), context); } return {}; } template<> - scan_function_t resolve_scanner(scan_context& context) { - context.apply_hints({.vectorSize = 16}); - - const auto alignment = context.alignment; - const auto signature = context.signature; - const bool cmpeq2 = context.pairIndex.has_value(); - const bool veccmp = signature.size() <= 16; - - return find_specialization_switch<[]() consteval { + scan_context create_context(const scan_parameters& params) { + return create_simd_scanner<[]() consteval { return &find_pattern_neon; - }>(alignment, cmpeq2, veccmp); + }>(params, 16); } } #endif diff --git a/src/arch/x86/AVX2.cpp b/src/arch/x86/AVX2.cpp index 22c352f..92c92d0 100644 --- a/src/arch/x86/AVX2.cpp +++ b/src/arch/x86/AVX2.cpp @@ -25,8 +25,8 @@ namespace hat::detail { template LIBHAT_TARGET("avx,avx2,bmi") static const_scan_result find_pattern_avx2(const std::byte* begin, const std::byte* end, const scan_context& context) { - const auto signature = context.signature; - const auto cmpIndex = cmpeq2 ? *context.pairIndex : context.cmpIndex; + const auto signature = context.signature(); + const auto cmpIndex = context.get().cmpIndex; // 256 bit vector containing first signature byte repeated const auto firstByte = _mm256_set1_epi8(static_cast(*signature[cmpIndex])); @@ -44,7 +44,7 @@ namespace hat::detail { auto [pre, vec, post] = segment_scan<__m256i, 32, veccmp>(begin, end, signature.size(), cmpIndex); if (!pre.empty()) { - const auto result = find_pattern_single(pre.data(), pre.data() + pre.size(), context); + const auto result = find_pattern_search(pre.data(), pre.data() + pre.size(), context); if (result.has_result()) { return result; } @@ -90,23 +90,16 @@ namespace hat::detail { } if (!post.empty()) { - return find_pattern_single(post.data(), post.data() + post.size(), context); + return find_pattern_search(post.data(), post.data() + post.size(), context); } return {}; } template<> - scan_function_t resolve_scanner(scan_context& context) { - context.apply_hints({.vectorSize = 32}); - - const auto alignment = context.alignment; - const auto signature = context.signature; - const bool cmpeq2 = context.pairIndex.has_value(); - const bool veccmp = signature.size() <= 32; - - return find_specialization_switch<[]() consteval { + scan_context create_context(const scan_parameters& params) { + return create_simd_scanner<[]() consteval { return &find_pattern_avx2; - }>(alignment, cmpeq2, veccmp); + }>(params, 32); } } #endif diff --git a/src/arch/x86/AVX512.cpp b/src/arch/x86/AVX512.cpp index 0f7fb67..c70db16 100644 --- a/src/arch/x86/AVX512.cpp +++ b/src/arch/x86/AVX512.cpp @@ -25,8 +25,8 @@ namespace hat::detail { template LIBHAT_TARGET("avx512f,avx512bw,bmi") static const_scan_result find_pattern_avx512(const std::byte* begin, const std::byte* end, const scan_context& context) { - const auto signature = context.signature; - const auto cmpIndex = cmpeq2 ? *context.pairIndex : context.cmpIndex; + const auto signature = context.signature(); + const auto cmpIndex = context.get().cmpIndex; // 512 bit vector containing first signature byte repeated const auto firstByte = _mm512_set1_epi8(static_cast(*signature[cmpIndex])); @@ -45,7 +45,7 @@ namespace hat::detail { auto [pre, vec, post] = segment_scan<__m512i, 64, veccmp>(begin, end, signature.size(), cmpIndex); if (!pre.empty()) { - const auto result = find_pattern_single(pre.data(), pre.data() + pre.size(), context); + const auto result = find_pattern_search(pre.data(), pre.data() + pre.size(), context); if (result.has_result()) { return result; } @@ -87,23 +87,16 @@ namespace hat::detail { } if (!post.empty()) { - return find_pattern_single(post.data(), post.data() + post.size(), context); + return find_pattern_search(post.data(), post.data() + post.size(), context); } return {}; } template<> - scan_function_t resolve_scanner(scan_context& context) { - context.apply_hints({.vectorSize = 64}); - - const auto alignment = context.alignment; - const auto signature = context.signature; - const bool cmpeq2 = context.pairIndex.has_value(); - const bool veccmp = signature.size() <= 64; - - return find_specialization_switch<[]() consteval { + scan_context create_context(const scan_parameters& params) { + return create_simd_scanner<[]() consteval { return &find_pattern_avx512; - }>(alignment, cmpeq2, veccmp); + }>(params, 64); } } #endif diff --git a/src/arch/x86/SSE.cpp b/src/arch/x86/SSE.cpp index 9b9db84..1040d3e 100644 --- a/src/arch/x86/SSE.cpp +++ b/src/arch/x86/SSE.cpp @@ -38,8 +38,8 @@ namespace hat::detail { template LIBHAT_TARGET("sse4.1") static const_scan_result find_pattern_sse(const std::byte* begin, const std::byte* end, const scan_context& context) { - const auto signature = context.signature; - const auto cmpIndex = cmpeq2 ? *context.pairIndex : context.cmpIndex; + const auto signature = context.signature(); + const auto cmpIndex = context.get().cmpIndex; // 128 bit vector containing first signature byte repeated const auto firstByte = _mm_set1_epi8(static_cast(*signature[cmpIndex])); @@ -57,7 +57,7 @@ namespace hat::detail { auto [pre, vec, post] = segment_scan<__m128i, 16, veccmp>(begin, end, signature.size(), cmpIndex); if (!pre.empty()) { - const auto result = find_pattern_single(pre.data(), pre.data() + pre.size(), context); + const auto result = find_pattern_search(pre.data(), pre.data() + pre.size(), context); if (result.has_result()) { return result; } @@ -100,23 +100,16 @@ namespace hat::detail { } if (!post.empty()) { - return find_pattern_single(post.data(), post.data() + post.size(), context); + return find_pattern_search(post.data(), post.data() + post.size(), context); } return {}; } template<> - scan_function_t resolve_scanner(scan_context& context) { - context.apply_hints({.vectorSize = 16}); - - const auto alignment = context.alignment; - const auto signature = context.signature; - const bool cmpeq2 = context.pairIndex.has_value(); - const bool veccmp = signature.size() <= 16; - - return find_specialization_switch<[]() consteval { + scan_context create_context(const scan_parameters& params) { + return create_simd_scanner<[]() consteval { return &find_pattern_sse; - }>(alignment, cmpeq2, veccmp); + }>(params, 16); } } #endif From f3211189766b585bab630e31dffc592087e04822 Mon Sep 17 00:00:00 2001 From: Brady Date: Thu, 30 Jul 2026 20:19:19 -0500 Subject: [PATCH 02/20] Move scanner implementations to `src/scanner` --- src/Scanner.cpp | 32 ++--- src/arch/arm/Frequency.hpp | 117 ------------------ src/arch/x86/Frequency.hpp | 117 ------------------ src/{arch/x86/AVX2.cpp => scanner/avx2.cpp} | 2 +- .../x86/AVX512.cpp => scanner/avx512.cpp} | 2 +- src/scanner/frequency/aarch64.inl | 103 +++++++++++++++ src/scanner/frequency/x86_64.inl | 103 +++++++++++++++ src/{arch/arm/Neon.cpp => scanner/neon.cpp} | 2 +- src/{Utils.hpp => scanner/simd.hpp} | 39 +----- src/{arch/x86/SSE.cpp => scanner/sse.cpp} | 2 +- src/utils.hpp | 43 +++++++ 11 files changed, 271 insertions(+), 291 deletions(-) delete mode 100644 src/arch/arm/Frequency.hpp delete mode 100644 src/arch/x86/Frequency.hpp rename src/{arch/x86/AVX2.cpp => scanner/avx2.cpp} (99%) rename src/{arch/x86/AVX512.cpp => scanner/avx512.cpp} (99%) create mode 100644 src/scanner/frequency/aarch64.inl create mode 100644 src/scanner/frequency/x86_64.inl rename src/{arch/arm/Neon.cpp => scanner/neon.cpp} (99%) rename src/{Utils.hpp => scanner/simd.hpp} (71%) rename src/{arch/x86/SSE.cpp => scanner/sse.cpp} (99%) create mode 100644 src/utils.hpp diff --git a/src/Scanner.cpp b/src/Scanner.cpp index be7fc42..9cc950b 100644 --- a/src/Scanner.cpp +++ b/src/Scanner.cpp @@ -3,15 +3,12 @@ #include #include -#ifdef LIBHAT_HINT_X86_64 -#include "arch/x86/Frequency.hpp" -#endif +#include +#include +#include +#include -#ifdef LIBHAT_HINT_AARCH64 -#include "arch/arm/Frequency.hpp" -#endif - -#include "Utils.hpp" +#include "utils.hpp" namespace { @@ -108,17 +105,22 @@ namespace hat::detail { const std::array& >; - static constexpr auto get_pair_hint(const scan_hint hints) -> std::optional { -#ifdef LIBHAT_HINT_X86_64 + static auto get_pair_hint(const scan_hint hints) -> std::optional { + [[maybe_unused]] constexpr auto p = [](const std::uint8_t a, const std::uint8_t b) { + return std::pair{std::byte{a}, std::byte{b}}; + }; if (static_cast(hints & scan_hint::x86_64)) { - return std::tie(hat::detail::x86_64::pairs_x1, hat::detail::x86_64::scores_x1); - } +#ifdef LIBHAT_HINT_X86_64 +#include "scanner/frequency/x86_64.inl" + return std::tie(pairs_x1, scores_x1); #endif -#ifdef LIBHAT_HINT_AARCH64 - if (static_cast(hints & scan_hint::aarch64)) { - return std::tie(hat::detail::aarch64::pairs_x1, hat::detail::aarch64::scores_x1); } + if (static_cast(hints & scan_hint::aarch64)) { +#ifdef LIBHAT_HINT_AARCH64 +#include "scanner/frequency/aarch64.inl" + return std::tie(pairs_x1, scores_x1); #endif + } return std::nullopt; } diff --git a/src/arch/arm/Frequency.hpp b/src/arch/arm/Frequency.hpp deleted file mode 100644 index 5e7f4f9..0000000 --- a/src/arch/arm/Frequency.hpp +++ /dev/null @@ -1,117 +0,0 @@ -#pragma once - -#include -#include -#include -#include - -namespace hat::detail::aarch64 { - - static constexpr auto p(std::uint8_t a, std::uint8_t b) { - return std::pair{std::byte{a}, std::byte{b}}; - } - - // Top 512 byte pair occurrences on 1 byte alignment, sorted. Sourced from all PE (.exe and .dll) files included in - // the "clang+llvm-22.1.8-aarch64-pc-windows-msvc" archive. This list accounts for ~50.9% of all byte pairs. - static constexpr inline auto pairs_x1 = std::to_array>({ - p(0x00, 0x00), p(0x00, 0x01), p(0x00, 0x08), p(0x00, 0x11), p(0x00, 0x12), p(0x00, 0x14), p(0x00, 0x20), p(0x00, 0x2A), - p(0x00, 0x34), p(0x00, 0x35), p(0x00, 0x36), p(0x00, 0x37), p(0x00, 0x39), p(0x00, 0x40), p(0x00, 0x51), p(0x00, 0x54), - p(0x00, 0x71), p(0x00, 0x79), p(0x00, 0x80), p(0x00, 0x90), p(0x00, 0x91), p(0x00, 0x94), p(0x00, 0xA9), p(0x00, 0xAA), - p(0x00, 0xB0), p(0x00, 0xB4), p(0x00, 0xB5), p(0x00, 0xB9), p(0x00, 0xD0), p(0x00, 0xD1), p(0x00, 0xF0), p(0x00, 0xF1), - p(0x00, 0xF9), p(0x00, 0xFD), p(0x01, 0x00), p(0x01, 0x01), p(0x01, 0x05), p(0x01, 0x08), p(0x01, 0x09), p(0x01, 0x0A), - p(0x01, 0x0B), p(0x01, 0x0C), p(0x01, 0x15), p(0x01, 0x3F), p(0x01, 0x40), p(0x01, 0x54), p(0x01, 0x71), p(0x01, 0x80), - p(0x01, 0x90), p(0x01, 0x91), p(0x01, 0xA9), p(0x01, 0xAA), p(0x01, 0xB0), p(0x01, 0xB9), p(0x01, 0xD0), p(0x01, 0xD1), - p(0x01, 0xF0), p(0x01, 0xF9), p(0x02, 0x00), p(0x02, 0x01), p(0x02, 0x05), p(0x02, 0x08), p(0x02, 0x40), p(0x02, 0x80), - p(0x02, 0x91), p(0x02, 0xA9), p(0x02, 0xAA), p(0x02, 0xF9), p(0x03, 0x00), p(0x03, 0x01), p(0x03, 0x02), p(0x03, 0x03), - p(0x03, 0x04), p(0x03, 0x05), p(0x03, 0x08), p(0x03, 0x09), p(0x03, 0x13), p(0x03, 0x14), p(0x03, 0x15), p(0x03, 0x16), - p(0x03, 0x17), p(0x03, 0x18), p(0x03, 0x19), p(0x03, 0x1A), p(0x03, 0x1B), p(0x03, 0x1C), p(0x03, 0x1F), p(0x03, 0x40), - p(0x03, 0x5F), p(0x03, 0x80), p(0x03, 0x91), p(0x03, 0xA9), p(0x03, 0xAA), p(0x04, 0x00), p(0x04, 0x40), p(0x04, 0x80), - p(0x04, 0x91), p(0x04, 0xA9), p(0x05, 0x00), p(0x05, 0x01), p(0x05, 0x40), p(0x05, 0x91), p(0x06, 0x00), p(0x06, 0x40), - p(0x06, 0x80), p(0x06, 0x91), p(0x07, 0x00), p(0x07, 0x40), p(0x07, 0x80), p(0x07, 0x91), p(0x08, 0x00), p(0x08, 0x01), - p(0x08, 0x05), p(0x08, 0x09), p(0x08, 0x10), p(0x08, 0x11), p(0x08, 0x21), p(0x08, 0x40), p(0x08, 0x41), p(0x08, 0x61), - p(0x08, 0x81), p(0x08, 0x8B), p(0x08, 0x91), p(0x08, 0xA1), p(0x08, 0xAA), p(0x08, 0xC1), p(0x08, 0xCB), p(0x08, 0xEB), - p(0x09, 0x00), p(0x09, 0x01), p(0x09, 0x05), p(0x09, 0x40), p(0x09, 0x6B), p(0x09, 0x8B), p(0x09, 0x91), p(0x09, 0xEB), - p(0x0A, 0x00), p(0x0A, 0x01), p(0x0A, 0x40), p(0x0A, 0xEB), p(0x0B, 0x00), p(0x0B, 0x40), p(0x0C, 0x00), p(0x0D, 0x00), - p(0x0D, 0x40), p(0x0E, 0x00), p(0x0E, 0x40), p(0x0F, 0x00), p(0x0F, 0x40), p(0x10, 0x00), p(0x10, 0x40), p(0x11, 0x00), - p(0x11, 0x40), p(0x12, 0x00), p(0x12, 0x40), p(0x13, 0x00), p(0x13, 0x40), p(0x13, 0xAA), p(0x14, 0x00), p(0x14, 0xAA), - p(0x14, 0xE0), p(0x14, 0xE8), p(0x14, 0xEB), p(0x15, 0x00), p(0x15, 0xAA), p(0x15, 0xEB), p(0x16, 0x40), p(0x16, 0xAA), - p(0x17, 0x00), p(0x17, 0x08), p(0x17, 0x40), p(0x17, 0x9F), p(0x17, 0xAA), p(0x17, 0xE0), p(0x17, 0xE8), p(0x18, 0xAA), - p(0x19, 0x00), p(0x19, 0xAA), p(0x1A, 0x00), p(0x1A, 0x40), p(0x1A, 0xAA), p(0x1B, 0x00), p(0x1B, 0x40), p(0x1B, 0xAA), - p(0x1C, 0xAA), p(0x1D, 0x00), p(0x1F, 0x00), p(0x1F, 0x01), p(0x1F, 0x05), p(0x1F, 0x09), p(0x1F, 0x20), p(0x1F, 0x2A), - p(0x1F, 0x40), p(0x1F, 0x41), p(0x1F, 0x7C), p(0x1F, 0xAA), p(0x20, 0x00), p(0x20, 0x01), p(0x20, 0x80), p(0x20, 0xD4), - p(0x21, 0x00), p(0x21, 0x40), p(0x22, 0x00), p(0x22, 0x40), p(0x23, 0x00), p(0x23, 0x01), p(0x23, 0x40), p(0x27, 0x00), - p(0x27, 0x40), p(0x28, 0x00), p(0x28, 0x01), p(0x29, 0x00), p(0x29, 0x01), p(0x29, 0x05), p(0x2A, 0x00), p(0x2A, 0xE0), - p(0x2B, 0x00), p(0x2B, 0x40), p(0x2F, 0x00), p(0x2F, 0x40), p(0x33, 0x00), p(0x33, 0x40), p(0x39, 0x08), p(0x39, 0x1F), - p(0x39, 0xE8), p(0x3D, 0x00), p(0x3D, 0xE0), p(0x3F, 0x00), p(0x3F, 0x01), p(0x3F, 0x05), p(0x3F, 0xD6), p(0x40, 0x00), - p(0x40, 0x01), p(0x40, 0x39), p(0x40, 0x79), p(0x40, 0x91), p(0x40, 0x92), p(0x40, 0xA9), p(0x40, 0xB1), p(0x40, 0xB9), - p(0x40, 0xF2), p(0x40, 0xF8), p(0x40, 0xF9), p(0x40, 0xFD), p(0x41, 0x00), p(0x41, 0x39), p(0x41, 0xA9), p(0x41, 0xB9), - p(0x41, 0xF8), p(0x41, 0xF9), p(0x42, 0x00), p(0x42, 0xA9), p(0x42, 0xB9), p(0x42, 0xF9), p(0x43, 0x00), p(0x43, 0x01), - p(0x43, 0x40), p(0x43, 0xA9), p(0x43, 0xF9), p(0x44, 0xA9), p(0x44, 0xF9), p(0x45, 0xF9), p(0x4A, 0x01), p(0x51, 0x1F), - p(0x52, 0x08), p(0x52, 0x09), p(0x52, 0x1F), p(0x52, 0xE0), p(0x52, 0xE3), p(0x52, 0xE8), p(0x53, 0xC2), p(0x54, 0x08), - p(0x54, 0x09), p(0x54, 0x1F), p(0x54, 0x28), p(0x54, 0x29), p(0x54, 0x3F), p(0x54, 0x68), p(0x54, 0x88), p(0x54, 0xE0), - p(0x54, 0xE8), p(0x54, 0xE9), p(0x54, 0xFE), p(0x5B, 0x01), p(0x5B, 0x41), p(0x5F, 0x01), p(0x5F, 0xD6), p(0x5F, 0xF8), - p(0x60, 0x00), p(0x60, 0x02), p(0x61, 0x00), p(0x62, 0x00), p(0x63, 0x00), p(0x68, 0x00), p(0x68, 0x02), p(0x68, 0x0A), - p(0x69, 0x02), p(0x6B, 0x01), p(0x71, 0x61), p(0x7C, 0x92), p(0x7C, 0xD3), p(0x7D, 0x92), p(0x7D, 0xD3), p(0x7F, 0x01), - p(0x7F, 0x02), p(0x80, 0x00), p(0x80, 0x3D), p(0x80, 0x52), p(0x81, 0x00), p(0x82, 0x00), p(0x83, 0x00), p(0x83, 0x01), - p(0x83, 0x02), p(0x88, 0x00), p(0x88, 0x02), p(0x88, 0x9A), p(0x89, 0x9A), p(0x90, 0x08), p(0x91, 0x00), p(0x91, 0x01), - p(0x91, 0x02), p(0x91, 0x03), p(0x91, 0x08), p(0x91, 0x1F), p(0x91, 0x22), p(0x91, 0xC0), p(0x91, 0xE0), p(0x91, 0xE1), - p(0x91, 0xE2), p(0x91, 0xE3), p(0x91, 0xE4), p(0x91, 0xE8), p(0x91, 0xE9), p(0x91, 0xFF), p(0x94, 0x20), p(0x94, 0x68), - p(0x94, 0xE0), p(0x94, 0xE1), p(0x94, 0xE8), p(0x97, 0x20), p(0x97, 0x68), p(0x97, 0xE0), p(0x97, 0xE1), p(0x97, 0xE8), - p(0x9F, 0x02), p(0x9F, 0x1A), p(0xA0, 0x00), p(0xA1, 0x00), p(0xA3, 0x00), p(0xA8, 0x02), p(0xA8, 0xC0), p(0xA9, 0x08), - p(0xA9, 0x1F), p(0xA9, 0xE0), p(0xA9, 0xE8), p(0xA9, 0xF3), p(0xA9, 0xF5), p(0xA9, 0xF7), p(0xA9, 0xF9), p(0xA9, 0xFE), - p(0xA9, 0xFF), p(0xAA, 0x00), p(0xAA, 0x01), p(0xAA, 0x02), p(0xAA, 0x03), p(0xAA, 0x08), p(0xAA, 0x1F), p(0xAA, 0xE0), - p(0xAA, 0xE1), p(0xAA, 0xE2), p(0xAA, 0xE3), p(0xAA, 0xE4), p(0xAA, 0xE5), p(0xAA, 0xE8), p(0xAA, 0xF3), p(0xAA, 0xF4), - p(0xAA, 0xF5), p(0xAA, 0xF6), p(0xAA, 0xF7), p(0xAA, 0xFE), p(0xAA, 0xFF), p(0xB0, 0x08), p(0xB4, 0x08), p(0xB4, 0xE0), - p(0xB9, 0x08), p(0xB9, 0x09), p(0xB9, 0x1F), p(0xB9, 0x29), p(0xB9, 0x3F), p(0xB9, 0x68), p(0xB9, 0xE0), p(0xB9, 0xE1), - p(0xB9, 0xE8), p(0xB9, 0xE9), p(0xB9, 0xFF), p(0xBF, 0x02), p(0xC0, 0x03), p(0xC0, 0x3D), p(0xC1, 0x00), p(0xC2, 0xA8), - p(0xC3, 0x00), p(0xC3, 0x01), p(0xC8, 0x02), p(0xD0, 0x08), p(0xD1, 0xF3), p(0xD4, 0xFF), p(0xD6, 0xE0), p(0xDF, 0x02), - p(0xE0, 0x03), p(0xE0, 0x07), p(0xE0, 0x0F), p(0xE0, 0x23), p(0xE0, 0x43), p(0xE0, 0x63), p(0xE0, 0x83), p(0xE0, 0xC3), - p(0xE1, 0x03), p(0xE1, 0x23), p(0xE1, 0x43), p(0xE1, 0x63), p(0xE1, 0x83), p(0xE1, 0xC3), p(0xE2, 0x03), p(0xE3, 0x00), - p(0xE3, 0x03), p(0xE4, 0x00), p(0xE4, 0x03), p(0xE5, 0x03), p(0xE6, 0x03), p(0xE7, 0x03), p(0xE8, 0x03), p(0xE8, 0x07), - p(0xE8, 0x0B), p(0xE8, 0x0F), p(0xE8, 0x13), p(0xE8, 0x17), p(0xE8, 0x23), p(0xE8, 0x27), p(0xE8, 0x43), p(0xE9, 0x03), - p(0xEA, 0x03), p(0xEB, 0x01), p(0xEB, 0x40), p(0xEB, 0x61), p(0xEB, 0xA1), p(0xEB, 0xC1), p(0xEB, 0xE1), p(0xED, 0x7C), - p(0xF0, 0x08), p(0xF1, 0x7D), p(0xF3, 0x03), p(0xF3, 0x53), p(0xF4, 0x03), p(0xF5, 0x03), p(0xF5, 0x5B), p(0xF5, 0x7B), - p(0xF6, 0x03), p(0xF7, 0x03), p(0xF7, 0x63), p(0xF8, 0x03), p(0xF9, 0x00), p(0xF9, 0x01), p(0xF9, 0x02), p(0xF9, 0x03), - p(0xF9, 0x08), p(0xF9, 0x09), p(0xF9, 0x0A), p(0xF9, 0x1F), p(0xF9, 0x20), p(0xF9, 0x21), p(0xF9, 0x28), p(0xF9, 0x29), - p(0xF9, 0x2A), p(0xF9, 0x3F), p(0xF9, 0x40), p(0xF9, 0x48), p(0xF9, 0x68), p(0xF9, 0x69), p(0xF9, 0x6B), p(0xF9, 0x7F), - p(0xF9, 0x88), p(0xF9, 0xA8), p(0xF9, 0xC8), p(0xF9, 0xE0), p(0xF9, 0xE1), p(0xF9, 0xE2), p(0xF9, 0xE8), p(0xF9, 0xE9), - p(0xF9, 0xEA), p(0xF9, 0xF3), p(0xF9, 0xF4), p(0xF9, 0xF5), p(0xF9, 0xF7), p(0xF9, 0xFB), p(0xF9, 0xFF), p(0xFA, 0x03), - p(0xFA, 0xFF), p(0xFB, 0x73), p(0xFB, 0xFF), p(0xFC, 0xFF), p(0xFD, 0xFF), p(0xFE, 0x0B), p(0xFE, 0xFF), p(0xFF, 0x03), - p(0xFF, 0x17), p(0xFF, 0x43), p(0xFF, 0x54), p(0xFF, 0x83), p(0xFF, 0x97), p(0xFF, 0xB4), p(0xFF, 0xC3), p(0xFF, 0xFF), - }); - - static constexpr inline auto scores_x1 = std::to_array({ - 0x002, 0x040, 0x0D9, 0x04F, 0x0A3, 0x00E, 0x06F, 0x12A, 0x048, 0x0F0, 0x07E, 0x0AE, 0x06E, 0x032, 0x05F, 0x001, - 0x011, 0x0F4, 0x00D, 0x087, 0x006, 0x043, 0x092, 0x018, 0x086, 0x024, 0x0B8, 0x019, 0x08F, 0x0B5, 0x074, 0x01B, - 0x004, 0x0ED, 0x00A, 0x078, 0x14C, 0x073, 0x021, 0x071, 0x0C9, 0x18B, 0x1C0, 0x08C, 0x01C, 0x1A0, 0x05E, 0x046, - 0x10B, 0x027, 0x0A2, 0x05D, 0x114, 0x156, 0x112, 0x145, 0x110, 0x0A6, 0x016, 0x0EE, 0x12C, 0x137, 0x01E, 0x0CA, - 0x04E, 0x0DF, 0x0C6, 0x1A6, 0x009, 0x028, 0x05C, 0x0CF, 0x149, 0x1E7, 0x057, 0x16E, 0x013, 0x01A, 0x02A, 0x033, - 0x054, 0x076, 0x0A1, 0x0D3, 0x106, 0x171, 0x010, 0x072, 0x036, 0x0DB, 0x081, 0x126, 0x195, 0x050, 0x14E, 0x15D, - 0x0B7, 0x184, 0x00C, 0x155, 0x03E, 0x0E2, 0x04C, 0x066, 0x1D5, 0x101, 0x04A, 0x056, 0x1FE, 0x152, 0x02E, 0x026, - 0x058, 0x15A, 0x17B, 0x16D, 0x0D1, 0x0E6, 0x119, 0x1D7, 0x12D, 0x0CC, 0x13D, 0x150, 0x098, 0x136, 0x10E, 0x089, - 0x038, 0x096, 0x191, 0x08B, 0x0EC, 0x138, 0x1FD, 0x03B, 0x077, 0x1FF, 0x06A, 0x190, 0x068, 0x09E, 0x12B, 0x0D2, - 0x1A2, 0x133, 0x0E3, 0x0C5, 0x097, 0x117, 0x153, 0x0B2, 0x0F6, 0x0FB, 0x0E4, 0x0A9, 0x0D5, 0x014, 0x179, 0x01F, - 0x120, 0x1EB, 0x11D, 0x130, 0x02C, 0x180, 0x1E3, 0x03D, 0x0D4, 0x1E2, 0x0DC, 0x1CF, 0x064, 0x05A, 0x16A, 0x093, - 0x129, 0x0BF, 0x199, 0x13A, 0x0F9, 0x100, 0x108, 0x15F, 0x1CB, 0x11A, 0x03F, 0x015, 0x053, 0x1B9, 0x1EC, 0x02D, - 0x0E1, 0x167, 0x1CA, 0x025, 0x045, 0x10F, 0x146, 0x080, 0x052, 0x11B, 0x065, 0x13C, 0x04D, 0x1CD, 0x0DD, 0x169, - 0x139, 0x0AB, 0x10C, 0x0E0, 0x095, 0x0FA, 0x18F, 0x1DF, 0x148, 0x16B, 0x1F1, 0x19E, 0x18D, 0x1C3, 0x1F9, 0x0A4, - 0x1E1, 0x14F, 0x1E4, 0x161, 0x061, 0x160, 0x085, 0x0A7, 0x1F5, 0x029, 0x0D0, 0x1DD, 0x143, 0x0B3, 0x1A1, 0x007, - 0x1CE, 0x144, 0x000, 0x1B3, 0x079, 0x1FB, 0x07B, 0x0C7, 0x15B, 0x039, 0x0BA, 0x0C0, 0x1AC, 0x0AD, 0x088, 0x111, - 0x1ED, 0x103, 0x1A8, 0x165, 0x1BD, 0x1AD, 0x16C, 0x1AF, 0x0D6, 0x1BE, 0x19D, 0x173, 0x1DC, 0x13B, 0x1C7, 0x09D, - 0x0FF, 0x0B0, 0x172, 0x1A4, 0x187, 0x124, 0x17A, 0x044, 0x082, 0x125, 0x170, 0x1FA, 0x1C1, 0x0D7, 0x037, 0x185, - 0x11F, 0x142, 0x0A5, 0x14A, 0x0AA, 0x192, 0x0AF, 0x183, 0x1DE, 0x198, 0x197, 0x19C, 0x182, 0x132, 0x109, 0x188, - 0x1DA, 0x164, 0x069, 0x003, 0x0F1, 0x11C, 0x06B, 0x131, 0x1CC, 0x1B6, 0x0FD, 0x17D, 0x196, 0x1D3, 0x17E, 0x1EA, - 0x116, 0x1F7, 0x0C1, 0x0CB, 0x1A7, 0x099, 0x020, 0x02F, 0x07F, 0x0EB, 0x1F8, 0x094, 0x1D1, 0x1BA, 0x1AA, 0x151, - 0x060, 0x123, 0x063, 0x09A, 0x1B5, 0x09B, 0x186, 0x075, 0x1A3, 0x0B4, 0x1C6, 0x11E, 0x113, 0x166, 0x14D, 0x1D2, - 0x0AC, 0x1DB, 0x1F0, 0x06D, 0x03A, 0x083, 0x0D8, 0x0B6, 0x070, 0x12E, 0x134, 0x1BB, 0x1B0, 0x059, 0x1BC, 0x07C, - 0x030, 0x049, 0x0C4, 0x121, 0x1B1, 0x09C, 0x14B, 0x12F, 0x13F, 0x1E8, 0x1F3, 0x1E6, 0x15C, 0x1E0, 0x122, 0x105, - 0x062, 0x154, 0x07D, 0x1B8, 0x1C9, 0x1C4, 0x0E8, 0x1F6, 0x0DE, 0x128, 0x174, 0x19B, 0x035, 0x08A, 0x15E, 0x1B7, - 0x0BC, 0x19F, 0x1C5, 0x1D9, 0x0B1, 0x127, 0x0E5, 0x1D8, 0x005, 0x0F2, 0x194, 0x0C8, 0x0F7, 0x1D0, 0x0CD, 0x135, - 0x00F, 0x118, 0x0F8, 0x193, 0x104, 0x163, 0x01D, 0x177, 0x042, 0x1C8, 0x09F, 0x0DA, 0x13E, 0x1E5, 0x034, 0x0FC, - 0x1BF, 0x181, 0x176, 0x1C2, 0x168, 0x10A, 0x189, 0x0C3, 0x18C, 0x1AE, 0x102, 0x1D4, 0x18E, 0x1AB, 0x1FC, 0x140, - 0x1EE, 0x0BB, 0x03C, 0x023, 0x04B, 0x067, 0x041, 0x162, 0x091, 0x0BE, 0x084, 0x0E9, 0x051, 0x0C2, 0x1F4, 0x107, - 0x012, 0x06C, 0x157, 0x022, 0x18A, 0x1EF, 0x0A0, 0x0BD, 0x1E9, 0x10D, 0x1A9, 0x158, 0x0A8, 0x1F2, 0x0B9, 0x17C, - 0x0FE, 0x16F, 0x1B2, 0x02B, 0x047, 0x115, 0x031, 0x090, 0x1B4, 0x08D, 0x175, 0x0EF, 0x17F, 0x19A, 0x07A, 0x1A5, - 0x1D6, 0x147, 0x178, 0x0F3, 0x08E, 0x159, 0x055, 0x0CE, 0x008, 0x0F5, 0x017, 0x0E7, 0x05B, 0x141, 0x0EA, 0x00B, - }); -} diff --git a/src/arch/x86/Frequency.hpp b/src/arch/x86/Frequency.hpp deleted file mode 100644 index 7bfadcd..0000000 --- a/src/arch/x86/Frequency.hpp +++ /dev/null @@ -1,117 +0,0 @@ -#pragma once - -#include -#include -#include -#include - -namespace hat::detail::x86_64 { - - static constexpr auto p(std::uint8_t a, std::uint8_t b) { - return std::pair{std::byte{a}, std::byte{b}}; - } - - // Top 512 byte pair occurrences on 1 byte alignment, sorted. Sourced from multiple executables compiled for - // Windows using MSVC, ranging from versions 19.16 to 19.42. This list accounts for ~60.4% of all byte pairs. - static constexpr inline auto pairs_x1 = std::to_array>({ - p(0x00, 0x00), p(0x00, 0x01), p(0x00, 0x0F), p(0x00, 0x10), p(0x00, 0x33), p(0x00, 0x41), p(0x00, 0x44), p(0x00, 0x45), - p(0x00, 0x48), p(0x00, 0x49), p(0x00, 0x4C), p(0x00, 0x4D), p(0x00, 0x66), p(0x00, 0x72), p(0x00, 0x74), p(0x00, 0x75), - p(0x00, 0x80), p(0x00, 0x89), p(0x00, 0x8B), p(0x00, 0x90), p(0x00, 0xC6), p(0x00, 0xC7), p(0x00, 0xE8), p(0x00, 0xE9), - p(0x00, 0xEB), p(0x00, 0xF3), p(0x00, 0xFF), p(0x01, 0x00), p(0x01, 0x0F), p(0x01, 0x48), p(0x01, 0x4C), p(0x01, 0x74), - p(0x01, 0x75), p(0x01, 0xBA), p(0x02, 0x00), p(0x02, 0x48), p(0x03, 0x00), p(0x03, 0x48), p(0x03, 0x4C), p(0x04, 0x00), - p(0x04, 0x48), p(0x05, 0x00), p(0x05, 0x48), p(0x05, 0xE8), p(0x06, 0x00), p(0x06, 0x48), p(0x07, 0x00), p(0x07, 0x48), - p(0x07, 0xFF), p(0x08, 0x00), p(0x08, 0x01), p(0x08, 0x0F), p(0x08, 0x48), p(0x08, 0x49), p(0x08, 0x4C), p(0x08, 0x83), - p(0x08, 0xE8), p(0x08, 0xFF), p(0x09, 0x00), p(0x09, 0x48), p(0x0A, 0x00), p(0x0B, 0x48), p(0x0C, 0x00), p(0x0C, 0x48), - p(0x0C, 0x83), p(0x0F, 0x00), p(0x0F, 0x10), p(0x0F, 0x11), p(0x0F, 0x1F), p(0x0F, 0x28), p(0x0F, 0x29), p(0x0F, 0x48), - p(0x0F, 0x57), p(0x0F, 0x58), p(0x0F, 0x59), p(0x0F, 0x6F), p(0x0F, 0x76), p(0x0F, 0x7F), p(0x0F, 0x84), p(0x0F, 0x85), - p(0x0F, 0x87), p(0x0F, 0x95), p(0x0F, 0xAF), p(0x0F, 0xB6), p(0x0F, 0xB7), p(0x0F, 0xC1), p(0x10, 0x00), p(0x10, 0x01), - p(0x10, 0x05), p(0x10, 0x0F), p(0x10, 0x44), p(0x10, 0x48), p(0x10, 0x49), p(0x10, 0x4C), p(0x10, 0x72), p(0x10, 0xE8), - p(0x11, 0x00), p(0x11, 0x44), p(0x11, 0x45), p(0x11, 0x48), p(0x13, 0x48), p(0x18, 0x0F), p(0x18, 0x48), p(0x18, 0x49), - p(0x19, 0x48), p(0x1C, 0x48), p(0x1F, 0x76), p(0x20, 0x00), p(0x20, 0x01), p(0x20, 0x0F), p(0x20, 0x41), p(0x20, 0x48), - p(0x20, 0x49), p(0x20, 0x4C), p(0x20, 0x5B), p(0x20, 0x5F), p(0x20, 0xE8), p(0x20, 0xFF), p(0x24, 0x08), p(0x24, 0x10), - p(0x24, 0x18), p(0x24, 0x20), p(0x24, 0x28), p(0x24, 0x30), p(0x24, 0x38), p(0x24, 0x40), p(0x24, 0x48), p(0x24, 0x50), - p(0x24, 0x58), p(0x24, 0x60), p(0x24, 0x68), p(0x24, 0x70), p(0x24, 0x78), p(0x24, 0x80), p(0x27, 0x48), p(0x28, 0x00), - p(0x28, 0x48), p(0x28, 0x49), p(0x28, 0x4C), p(0x28, 0xE8), p(0x2B, 0x48), p(0x2B, 0xC1), p(0x2B, 0xC8), p(0x30, 0x0F), - p(0x30, 0x48), p(0x30, 0x49), p(0x30, 0x4C), p(0x30, 0xE8), p(0x33, 0xC0), p(0x33, 0xC4), p(0x33, 0xC9), p(0x33, 0xCC), - p(0x33, 0xD2), p(0x33, 0xF6), p(0x38, 0x00), p(0x38, 0x48), p(0x3B, 0xCA), p(0x40, 0x00), p(0x40, 0x08), p(0x40, 0x0F), - p(0x40, 0x10), p(0x40, 0x20), p(0x40, 0x48), p(0x40, 0x53), p(0x40, 0x88), p(0x40, 0xE8), p(0x41, 0x08), p(0x41, 0x0F), - p(0x41, 0x10), p(0x41, 0x54), p(0x41, 0x55), p(0x41, 0x56), p(0x41, 0x57), p(0x41, 0x5C), p(0x41, 0x5D), p(0x41, 0x5E), - p(0x41, 0x5F), p(0x41, 0x8B), p(0x41, 0xB8), p(0x41, 0xF8), p(0x42, 0x08), p(0x43, 0x08), p(0x43, 0x0C), p(0x43, 0x10), - p(0x43, 0x18), p(0x44, 0x0F), p(0x44, 0x24), p(0x44, 0x88), p(0x44, 0x89), p(0x44, 0x8B), p(0x44, 0x8D), p(0x45, 0x0F), - p(0x45, 0x33), p(0x46, 0x08), p(0x47, 0x08), p(0x47, 0x10), p(0x48, 0x03), p(0x48, 0x0F), p(0x48, 0x2B), p(0x48, 0x33), - p(0x48, 0x3B), p(0x48, 0x48), p(0x48, 0x81), p(0x48, 0x83), p(0x48, 0x85), p(0x48, 0x89), p(0x48, 0x8B), p(0x48, 0x8D), - p(0x48, 0xB8), p(0x48, 0xC1), p(0x48, 0xC7), p(0x48, 0xFF), p(0x49, 0x0F), p(0x49, 0x2B), p(0x49, 0x3B), p(0x49, 0x83), - p(0x49, 0x89), p(0x49, 0x8B), p(0x49, 0x8D), p(0x49, 0xF8), p(0x4B, 0x08), p(0x4C, 0x24), p(0x4C, 0x3B), p(0x4C, 0x89), - p(0x4C, 0x8B), p(0x4C, 0x8D), p(0x4D, 0x85), p(0x4D, 0x89), p(0x4D, 0x8B), p(0x50, 0x08), p(0x50, 0x48), p(0x53, 0x48), - p(0x54, 0x24), p(0x54, 0x41), p(0x55, 0x41), p(0x55, 0x48), p(0x55, 0x56), p(0x56, 0x41), p(0x56, 0x48), p(0x56, 0x57), - p(0x57, 0x41), p(0x57, 0x48), p(0x57, 0xC0), p(0x57, 0xC9), p(0x58, 0x00), p(0x58, 0x48), p(0x5B, 0xC3), p(0x5C, 0x24), - p(0x5C, 0x5F), p(0x5D, 0x41), p(0x5D, 0xC3), p(0x5E, 0x41), p(0x5E, 0x5D), p(0x5E, 0x5F), p(0x5F, 0x41), p(0x5F, 0x5E), - p(0x5F, 0xC3), p(0x60, 0x48), p(0x64, 0x24), p(0x66, 0x0F), p(0x66, 0x89), p(0x68, 0x48), p(0x6C, 0x24), p(0x70, 0x48), - p(0x72, 0x1C), p(0x74, 0x05), p(0x74, 0x24), p(0x75, 0x10), p(0x76, 0x07), p(0x78, 0x48), p(0x7C, 0x24), p(0x7F, 0x44), - p(0x7F, 0x45), p(0x80, 0x00), p(0x80, 0x48), p(0x81, 0xC4), p(0x81, 0xEC), p(0x81, 0xFA), p(0x83, 0xC0), p(0x83, 0xC1), - p(0x83, 0xC2), p(0x83, 0xC4), p(0x83, 0xEC), p(0x83, 0xF8), p(0x83, 0xFA), p(0x83, 0xFF), p(0x84, 0x00), p(0x84, 0x24), - p(0x84, 0xC0), p(0x85, 0xC0), p(0x85, 0xC9), p(0x85, 0xD2), p(0x85, 0xDB), p(0x85, 0xF6), p(0x85, 0xFF), p(0x88, 0x00), - p(0x89, 0x01), p(0x89, 0x02), p(0x89, 0x03), p(0x89, 0x05), p(0x89, 0x41), p(0x89, 0x42), p(0x89, 0x43), p(0x89, 0x44), - p(0x89, 0x45), p(0x89, 0x46), p(0x89, 0x47), p(0x89, 0x4C), p(0x89, 0x4D), p(0x89, 0x54), p(0x89, 0x5C), p(0x89, 0x5D), - p(0x89, 0x6B), p(0x89, 0x6C), p(0x89, 0x73), p(0x89, 0x74), p(0x89, 0x75), p(0x89, 0x7C), p(0x89, 0x7D), p(0x89, 0x84), - p(0x89, 0x85), p(0x8B, 0x00), p(0x8B, 0x01), p(0x8B, 0x03), p(0x8B, 0x04), p(0x8B, 0x05), p(0x8B, 0x06), p(0x8B, 0x07), - p(0x8B, 0x08), p(0x8B, 0x0D), p(0x8B, 0x40), p(0x8B, 0x41), p(0x8B, 0x42), p(0x8B, 0x43), p(0x8B, 0x44), p(0x8B, 0x45), - p(0x8B, 0x46), p(0x8B, 0x47), p(0x8B, 0x48), p(0x8B, 0x49), p(0x8B, 0x4B), p(0x8B, 0x4C), p(0x8B, 0x4D), p(0x8B, 0x4E), - p(0x8B, 0x4F), p(0x8B, 0x54), p(0x8B, 0x55), p(0x8B, 0x5B), p(0x8B, 0x5C), p(0x8B, 0x5D), p(0x8B, 0x73), p(0x8B, 0x74), - p(0x8B, 0x7C), p(0x8B, 0x80), p(0x8B, 0x85), p(0x8B, 0x8C), p(0x8B, 0x8D), p(0x8B, 0x9C), p(0x8B, 0xC0), p(0x8B, 0xC1), - p(0x8B, 0xC2), p(0x8B, 0xC3), p(0x8B, 0xC6), p(0x8B, 0xC7), p(0x8B, 0xC8), p(0x8B, 0xCB), p(0x8B, 0xCD), p(0x8B, 0xCE), - p(0x8B, 0xCF), p(0x8B, 0xD0), p(0x8B, 0xD3), p(0x8B, 0xD6), p(0x8B, 0xD7), p(0x8B, 0xD8), p(0x8B, 0xD9), p(0x8B, 0xDA), - p(0x8B, 0xE3), p(0x8B, 0xEA), p(0x8B, 0xF0), p(0x8B, 0xF1), p(0x8B, 0xF2), p(0x8B, 0xF8), p(0x8B, 0xF9), p(0x8B, 0xFA), - p(0x8C, 0x24), p(0x8D, 0x05), p(0x8D, 0x0D), p(0x8D, 0x15), p(0x8D, 0x41), p(0x8D, 0x44), p(0x8D, 0x45), p(0x8D, 0x4B), - p(0x8D, 0x4C), p(0x8D, 0x4D), p(0x8D, 0x4F), p(0x8D, 0x54), p(0x8D, 0x55), p(0x8D, 0x85), p(0x8D, 0x8A), p(0x8D, 0x8D), - p(0x8D, 0x95), p(0x8D, 0x9C), p(0x90, 0x00), p(0x90, 0x48), p(0x90, 0x4C), p(0x95, 0xC2), p(0x98, 0x00), p(0x9C, 0x24), - p(0xA0, 0x00), p(0xA8, 0x00), p(0xAA, 0xAA), p(0xB0, 0x00), p(0xB0, 0x01), p(0xB8, 0x00), p(0xC0, 0x00), p(0xC0, 0x0F), - p(0xC0, 0x48), p(0xC0, 0x74), p(0xC0, 0x75), p(0xC0, 0xF3), p(0xC0, 0xF8), p(0xC1, 0x43), p(0xC1, 0x48), p(0xC1, 0xE8), - p(0xC1, 0xF8), p(0xC2, 0x27), p(0xC2, 0x48), p(0xC3, 0x48), p(0xC3, 0xCC), p(0xC3, 0xE8), p(0xC4, 0x20), p(0xC4, 0x28), - p(0xC4, 0x30), p(0xC4, 0x48), p(0xC6, 0x44), p(0xC6, 0x45), p(0xC6, 0x48), p(0xC6, 0xF0), p(0xC7, 0x43), p(0xC7, 0x44), - p(0xC7, 0x45), p(0xC7, 0x48), p(0xC7, 0x85), p(0xC7, 0xF0), p(0xC8, 0x00), p(0xC8, 0x48), p(0xC8, 0xE8), p(0xC9, 0x0F), - p(0xC9, 0x48), p(0xC9, 0x74), p(0xC9, 0xF3), p(0xCB, 0x48), p(0xCB, 0xE8), p(0xCB, 0xFF), p(0xCC, 0x40), p(0xCC, 0x48), - p(0xCC, 0xCC), p(0xCC, 0xE8), p(0xCE, 0x48), p(0xCE, 0xE8), p(0xCF, 0x48), p(0xCF, 0xE8), p(0xD0, 0x00), p(0xD0, 0x48), - p(0xD2, 0x48), p(0xD3, 0x48), p(0xD6, 0x48), p(0xD7, 0x48), p(0xD8, 0x00), p(0xD8, 0x48), p(0xD9, 0x48), p(0xDA, 0x48), - p(0xDB, 0x74), p(0xE0, 0x00), p(0xE0, 0x48), p(0xE8, 0x00), p(0xE8, 0x48), p(0xEB, 0x03), p(0xEC, 0x20), p(0xF0, 0x00), - p(0xF0, 0x0F), p(0xF0, 0x48), p(0xF0, 0xFF), p(0xF2, 0x0F), p(0xF2, 0x48), p(0xF3, 0x0F), p(0xF8, 0x01), p(0xF8, 0x1F), - p(0xF8, 0x48), p(0xF9, 0x48), p(0xFA, 0x00), p(0xFA, 0x0F), p(0xFA, 0x10), p(0xFA, 0x48), p(0xFB, 0x48), p(0xFC, 0x48), - p(0xFD, 0x48), p(0xFD, 0x90), p(0xFD, 0xFF), p(0xFE, 0x48), p(0xFE, 0x90), p(0xFE, 0xFF), p(0xFF, 0x0F), p(0xFF, 0x15), - p(0xFF, 0x48), p(0xFF, 0x4C), p(0xFF, 0x50), p(0xFF, 0x74), p(0xFF, 0x90), p(0xFF, 0xC2), p(0xFF, 0xCC), p(0xFF, 0xFF), - }); - - static constexpr inline auto scores_x1 = std::to_array({ - 0x000, 0x0C0, 0x028, 0x084, 0x1C5, 0x083, 0x11B, 0x1E3, 0x005, 0x042, 0x02F, 0x18B, 0x146, 0x08B, 0x0F6, 0x137, - 0x15B, 0x1DC, 0x10B, 0x0BD, 0x0D6, 0x129, 0x01D, 0x0C1, 0x111, 0x182, 0x065, 0x008, 0x11E, 0x016, 0x1CB, 0x17E, - 0x051, 0x1F9, 0x021, 0x034, 0x03E, 0x014, 0x1DB, 0x07C, 0x05B, 0x0C4, 0x0B7, 0x159, 0x0FA, 0x0A2, 0x102, 0x06D, - 0x0E9, 0x0DE, 0x1A1, 0x197, 0x012, 0x132, 0x141, 0x0E1, 0x1C0, 0x0A3, 0x1C6, 0x19C, 0x1AD, 0x18F, 0x1C3, 0x1D3, - 0x0CA, 0x087, 0x024, 0x00D, 0x0D1, 0x08E, 0x126, 0x134, 0x033, 0x1D0, 0x1A7, 0x181, 0x106, 0x056, 0x038, 0x0DD, - 0x1DD, 0x19F, 0x13D, 0x046, 0x10F, 0x05A, 0x053, 0x1DE, 0x1D1, 0x135, 0x1E4, 0x015, 0x144, 0x123, 0x101, 0x1E6, - 0x1EF, 0x0D4, 0x0B8, 0x160, 0x1CE, 0x15D, 0x044, 0x1FD, 0x1FF, 0x0CE, 0x0FD, 0x125, 0x194, 0x18A, 0x1CF, 0x01F, - 0x13F, 0x0C8, 0x18D, 0x127, 0x0F3, 0x1B3, 0x0BB, 0x0A4, 0x0A5, 0x011, 0x039, 0x01B, 0x045, 0x031, 0x052, 0x04B, - 0x079, 0x067, 0x0B2, 0x080, 0x0EF, 0x192, 0x08C, 0x1BA, 0x04F, 0x1EE, 0x1BF, 0x173, 0x1D4, 0x0B1, 0x1B9, 0x1F0, - 0x027, 0x1A0, 0x17C, 0x16B, 0x06C, 0x0D9, 0x171, 0x0D7, 0x131, 0x1A3, 0x1D5, 0x049, 0x190, 0x17F, 0x064, 0x1B0, - 0x195, 0x1A5, 0x047, 0x12B, 0x19E, 0x1F3, 0x0DC, 0x05D, 0x172, 0x175, 0x1E0, 0x0C5, 0x115, 0x170, 0x1D7, 0x0BE, - 0x10E, 0x0AA, 0x0C6, 0x183, 0x12E, 0x0B3, 0x1A2, 0x138, 0x1C9, 0x0E5, 0x007, 0x1BC, 0x0DB, 0x0F4, 0x1A6, 0x1D2, - 0x081, 0x19A, 0x136, 0x1AF, 0x14C, 0x076, 0x060, 0x050, 0x030, 0x07E, 0x020, 0x006, 0x00B, 0x003, 0x001, 0x004, - 0x1E7, 0x075, 0x02B, 0x072, 0x1EC, 0x13A, 0x0D8, 0x15A, 0x091, 0x009, 0x0E4, 0x0EB, 0x1F7, 0x00F, 0x199, 0x010, - 0x00C, 0x018, 0x14E, 0x1BD, 0x073, 0x1BE, 0x068, 0x152, 0x026, 0x177, 0x1A4, 0x1BB, 0x153, 0x113, 0x1FA, 0x0E8, - 0x0F0, 0x054, 0x04C, 0x179, 0x1FE, 0x0AE, 0x0E3, 0x013, 0x1AC, 0x1F5, 0x0C2, 0x178, 0x140, 0x1D8, 0x114, 0x0E0, - 0x07B, 0x09C, 0x1EB, 0x04D, 0x154, 0x0D5, 0x082, 0x0BF, 0x0F2, 0x16E, 0x02E, 0x158, 0x0FF, 0x12F, 0x05E, 0x16C, - 0x163, 0x07F, 0x1C1, 0x11A, 0x0B9, 0x09A, 0x08D, 0x128, 0x08A, 0x02C, 0x03B, 0x023, 0x0BC, 0x1EA, 0x1B5, 0x098, - 0x097, 0x02A, 0x032, 0x16A, 0x092, 0x1D6, 0x13C, 0x104, 0x1B6, 0x1B1, 0x17B, 0x1C8, 0x10D, 0x148, 0x0B4, 0x01A, - 0x03F, 0x1ED, 0x0EC, 0x0A9, 0x168, 0x119, 0x035, 0x180, 0x1AA, 0x105, 0x1FC, 0x061, 0x0ED, 0x0AD, 0x0FC, 0x11C, - 0x0E7, 0x071, 0x04E, 0x077, 0x116, 0x07A, 0x143, 0x0FB, 0x11F, 0x1DA, 0x03C, 0x0A1, 0x12A, 0x120, 0x0D0, 0x0AC, - 0x15E, 0x145, 0x112, 0x09F, 0x155, 0x089, 0x06E, 0x1E2, 0x19D, 0x164, 0x0F9, 0x13B, 0x063, 0x196, 0x1B8, 0x0E6, - 0x1CC, 0x198, 0x1F6, 0x166, 0x122, 0x107, 0x13E, 0x095, 0x10C, 0x06F, 0x088, 0x05F, 0x057, 0x025, 0x1CA, 0x06A, - 0x040, 0x0B5, 0x0F5, 0x0F8, 0x109, 0x0A6, 0x0CF, 0x147, 0x185, 0x1E8, 0x110, 0x12C, 0x169, 0x103, 0x0D3, 0x167, - 0x0DA, 0x01E, 0x043, 0x066, 0x189, 0x093, 0x099, 0x130, 0x048, 0x037, 0x191, 0x059, 0x07D, 0x19B, 0x16F, 0x062, - 0x139, 0x1E5, 0x0C7, 0x01C, 0x1B2, 0x1E9, 0x142, 0x09D, 0x0E2, 0x162, 0x1CD, 0x0F7, 0x1B7, 0x12D, 0x0F1, 0x036, - 0x03A, 0x04A, 0x0EA, 0x184, 0x0CB, 0x0FE, 0x03D, 0x1A9, 0x187, 0x0A0, 0x070, 0x055, 0x02D, 0x1FB, 0x078, 0x1F4, - 0x188, 0x0B0, 0x118, 0x150, 0x10A, 0x1AE, 0x1AB, 0x069, 0x0AF, 0x0A7, 0x1C2, 0x121, 0x17A, 0x09B, 0x0CD, 0x16D, - 0x1F1, 0x041, 0x1DF, 0x094, 0x074, 0x1F8, 0x0CC, 0x019, 0x002, 0x05C, 0x1A8, 0x0C9, 0x100, 0x086, 0x14F, 0x090, - 0x1F2, 0x149, 0x165, 0x117, 0x1E1, 0x0A8, 0x133, 0x18E, 0x0B6, 0x15C, 0x157, 0x1C7, 0x15F, 0x193, 0x0AB, 0x17D, - 0x058, 0x0EE, 0x124, 0x0C3, 0x186, 0x022, 0x06B, 0x09E, 0x029, 0x14D, 0x096, 0x161, 0x1D9, 0x156, 0x176, 0x108, - 0x0BA, 0x1B4, 0x1C4, 0x08F, 0x14B, 0x0DF, 0x11D, 0x00E, 0x017, 0x151, 0x174, 0x14A, 0x085, 0x0D2, 0x18C, 0x00A, - }); -} diff --git a/src/arch/x86/AVX2.cpp b/src/scanner/avx2.cpp similarity index 99% rename from src/arch/x86/AVX2.cpp rename to src/scanner/avx2.cpp index 92c92d0..c3c8796 100644 --- a/src/arch/x86/AVX2.cpp +++ b/src/scanner/avx2.cpp @@ -4,7 +4,7 @@ #include -#include "../../Utils.hpp" +#include "simd.hpp" #include diff --git a/src/arch/x86/AVX512.cpp b/src/scanner/avx512.cpp similarity index 99% rename from src/arch/x86/AVX512.cpp rename to src/scanner/avx512.cpp index c70db16..47c4b75 100644 --- a/src/arch/x86/AVX512.cpp +++ b/src/scanner/avx512.cpp @@ -4,7 +4,7 @@ #include -#include "../../Utils.hpp" +#include "simd.hpp" #include diff --git a/src/scanner/frequency/aarch64.inl b/src/scanner/frequency/aarch64.inl new file mode 100644 index 0000000..e289aee --- /dev/null +++ b/src/scanner/frequency/aarch64.inl @@ -0,0 +1,103 @@ +// Top 512 byte pair occurrences on 1 byte alignment, sorted. Sourced from all PE (.exe and .dll) files included in +// the "clang+llvm-22.1.8-aarch64-pc-windows-msvc" archive. This list accounts for ~50.9% of all byte pairs. +static constexpr auto pairs_x1 = std::to_array>({ + p(0x00, 0x00), p(0x00, 0x01), p(0x00, 0x08), p(0x00, 0x11), p(0x00, 0x12), p(0x00, 0x14), p(0x00, 0x20), p(0x00, 0x2A), + p(0x00, 0x34), p(0x00, 0x35), p(0x00, 0x36), p(0x00, 0x37), p(0x00, 0x39), p(0x00, 0x40), p(0x00, 0x51), p(0x00, 0x54), + p(0x00, 0x71), p(0x00, 0x79), p(0x00, 0x80), p(0x00, 0x90), p(0x00, 0x91), p(0x00, 0x94), p(0x00, 0xA9), p(0x00, 0xAA), + p(0x00, 0xB0), p(0x00, 0xB4), p(0x00, 0xB5), p(0x00, 0xB9), p(0x00, 0xD0), p(0x00, 0xD1), p(0x00, 0xF0), p(0x00, 0xF1), + p(0x00, 0xF9), p(0x00, 0xFD), p(0x01, 0x00), p(0x01, 0x01), p(0x01, 0x05), p(0x01, 0x08), p(0x01, 0x09), p(0x01, 0x0A), + p(0x01, 0x0B), p(0x01, 0x0C), p(0x01, 0x15), p(0x01, 0x3F), p(0x01, 0x40), p(0x01, 0x54), p(0x01, 0x71), p(0x01, 0x80), + p(0x01, 0x90), p(0x01, 0x91), p(0x01, 0xA9), p(0x01, 0xAA), p(0x01, 0xB0), p(0x01, 0xB9), p(0x01, 0xD0), p(0x01, 0xD1), + p(0x01, 0xF0), p(0x01, 0xF9), p(0x02, 0x00), p(0x02, 0x01), p(0x02, 0x05), p(0x02, 0x08), p(0x02, 0x40), p(0x02, 0x80), + p(0x02, 0x91), p(0x02, 0xA9), p(0x02, 0xAA), p(0x02, 0xF9), p(0x03, 0x00), p(0x03, 0x01), p(0x03, 0x02), p(0x03, 0x03), + p(0x03, 0x04), p(0x03, 0x05), p(0x03, 0x08), p(0x03, 0x09), p(0x03, 0x13), p(0x03, 0x14), p(0x03, 0x15), p(0x03, 0x16), + p(0x03, 0x17), p(0x03, 0x18), p(0x03, 0x19), p(0x03, 0x1A), p(0x03, 0x1B), p(0x03, 0x1C), p(0x03, 0x1F), p(0x03, 0x40), + p(0x03, 0x5F), p(0x03, 0x80), p(0x03, 0x91), p(0x03, 0xA9), p(0x03, 0xAA), p(0x04, 0x00), p(0x04, 0x40), p(0x04, 0x80), + p(0x04, 0x91), p(0x04, 0xA9), p(0x05, 0x00), p(0x05, 0x01), p(0x05, 0x40), p(0x05, 0x91), p(0x06, 0x00), p(0x06, 0x40), + p(0x06, 0x80), p(0x06, 0x91), p(0x07, 0x00), p(0x07, 0x40), p(0x07, 0x80), p(0x07, 0x91), p(0x08, 0x00), p(0x08, 0x01), + p(0x08, 0x05), p(0x08, 0x09), p(0x08, 0x10), p(0x08, 0x11), p(0x08, 0x21), p(0x08, 0x40), p(0x08, 0x41), p(0x08, 0x61), + p(0x08, 0x81), p(0x08, 0x8B), p(0x08, 0x91), p(0x08, 0xA1), p(0x08, 0xAA), p(0x08, 0xC1), p(0x08, 0xCB), p(0x08, 0xEB), + p(0x09, 0x00), p(0x09, 0x01), p(0x09, 0x05), p(0x09, 0x40), p(0x09, 0x6B), p(0x09, 0x8B), p(0x09, 0x91), p(0x09, 0xEB), + p(0x0A, 0x00), p(0x0A, 0x01), p(0x0A, 0x40), p(0x0A, 0xEB), p(0x0B, 0x00), p(0x0B, 0x40), p(0x0C, 0x00), p(0x0D, 0x00), + p(0x0D, 0x40), p(0x0E, 0x00), p(0x0E, 0x40), p(0x0F, 0x00), p(0x0F, 0x40), p(0x10, 0x00), p(0x10, 0x40), p(0x11, 0x00), + p(0x11, 0x40), p(0x12, 0x00), p(0x12, 0x40), p(0x13, 0x00), p(0x13, 0x40), p(0x13, 0xAA), p(0x14, 0x00), p(0x14, 0xAA), + p(0x14, 0xE0), p(0x14, 0xE8), p(0x14, 0xEB), p(0x15, 0x00), p(0x15, 0xAA), p(0x15, 0xEB), p(0x16, 0x40), p(0x16, 0xAA), + p(0x17, 0x00), p(0x17, 0x08), p(0x17, 0x40), p(0x17, 0x9F), p(0x17, 0xAA), p(0x17, 0xE0), p(0x17, 0xE8), p(0x18, 0xAA), + p(0x19, 0x00), p(0x19, 0xAA), p(0x1A, 0x00), p(0x1A, 0x40), p(0x1A, 0xAA), p(0x1B, 0x00), p(0x1B, 0x40), p(0x1B, 0xAA), + p(0x1C, 0xAA), p(0x1D, 0x00), p(0x1F, 0x00), p(0x1F, 0x01), p(0x1F, 0x05), p(0x1F, 0x09), p(0x1F, 0x20), p(0x1F, 0x2A), + p(0x1F, 0x40), p(0x1F, 0x41), p(0x1F, 0x7C), p(0x1F, 0xAA), p(0x20, 0x00), p(0x20, 0x01), p(0x20, 0x80), p(0x20, 0xD4), + p(0x21, 0x00), p(0x21, 0x40), p(0x22, 0x00), p(0x22, 0x40), p(0x23, 0x00), p(0x23, 0x01), p(0x23, 0x40), p(0x27, 0x00), + p(0x27, 0x40), p(0x28, 0x00), p(0x28, 0x01), p(0x29, 0x00), p(0x29, 0x01), p(0x29, 0x05), p(0x2A, 0x00), p(0x2A, 0xE0), + p(0x2B, 0x00), p(0x2B, 0x40), p(0x2F, 0x00), p(0x2F, 0x40), p(0x33, 0x00), p(0x33, 0x40), p(0x39, 0x08), p(0x39, 0x1F), + p(0x39, 0xE8), p(0x3D, 0x00), p(0x3D, 0xE0), p(0x3F, 0x00), p(0x3F, 0x01), p(0x3F, 0x05), p(0x3F, 0xD6), p(0x40, 0x00), + p(0x40, 0x01), p(0x40, 0x39), p(0x40, 0x79), p(0x40, 0x91), p(0x40, 0x92), p(0x40, 0xA9), p(0x40, 0xB1), p(0x40, 0xB9), + p(0x40, 0xF2), p(0x40, 0xF8), p(0x40, 0xF9), p(0x40, 0xFD), p(0x41, 0x00), p(0x41, 0x39), p(0x41, 0xA9), p(0x41, 0xB9), + p(0x41, 0xF8), p(0x41, 0xF9), p(0x42, 0x00), p(0x42, 0xA9), p(0x42, 0xB9), p(0x42, 0xF9), p(0x43, 0x00), p(0x43, 0x01), + p(0x43, 0x40), p(0x43, 0xA9), p(0x43, 0xF9), p(0x44, 0xA9), p(0x44, 0xF9), p(0x45, 0xF9), p(0x4A, 0x01), p(0x51, 0x1F), + p(0x52, 0x08), p(0x52, 0x09), p(0x52, 0x1F), p(0x52, 0xE0), p(0x52, 0xE3), p(0x52, 0xE8), p(0x53, 0xC2), p(0x54, 0x08), + p(0x54, 0x09), p(0x54, 0x1F), p(0x54, 0x28), p(0x54, 0x29), p(0x54, 0x3F), p(0x54, 0x68), p(0x54, 0x88), p(0x54, 0xE0), + p(0x54, 0xE8), p(0x54, 0xE9), p(0x54, 0xFE), p(0x5B, 0x01), p(0x5B, 0x41), p(0x5F, 0x01), p(0x5F, 0xD6), p(0x5F, 0xF8), + p(0x60, 0x00), p(0x60, 0x02), p(0x61, 0x00), p(0x62, 0x00), p(0x63, 0x00), p(0x68, 0x00), p(0x68, 0x02), p(0x68, 0x0A), + p(0x69, 0x02), p(0x6B, 0x01), p(0x71, 0x61), p(0x7C, 0x92), p(0x7C, 0xD3), p(0x7D, 0x92), p(0x7D, 0xD3), p(0x7F, 0x01), + p(0x7F, 0x02), p(0x80, 0x00), p(0x80, 0x3D), p(0x80, 0x52), p(0x81, 0x00), p(0x82, 0x00), p(0x83, 0x00), p(0x83, 0x01), + p(0x83, 0x02), p(0x88, 0x00), p(0x88, 0x02), p(0x88, 0x9A), p(0x89, 0x9A), p(0x90, 0x08), p(0x91, 0x00), p(0x91, 0x01), + p(0x91, 0x02), p(0x91, 0x03), p(0x91, 0x08), p(0x91, 0x1F), p(0x91, 0x22), p(0x91, 0xC0), p(0x91, 0xE0), p(0x91, 0xE1), + p(0x91, 0xE2), p(0x91, 0xE3), p(0x91, 0xE4), p(0x91, 0xE8), p(0x91, 0xE9), p(0x91, 0xFF), p(0x94, 0x20), p(0x94, 0x68), + p(0x94, 0xE0), p(0x94, 0xE1), p(0x94, 0xE8), p(0x97, 0x20), p(0x97, 0x68), p(0x97, 0xE0), p(0x97, 0xE1), p(0x97, 0xE8), + p(0x9F, 0x02), p(0x9F, 0x1A), p(0xA0, 0x00), p(0xA1, 0x00), p(0xA3, 0x00), p(0xA8, 0x02), p(0xA8, 0xC0), p(0xA9, 0x08), + p(0xA9, 0x1F), p(0xA9, 0xE0), p(0xA9, 0xE8), p(0xA9, 0xF3), p(0xA9, 0xF5), p(0xA9, 0xF7), p(0xA9, 0xF9), p(0xA9, 0xFE), + p(0xA9, 0xFF), p(0xAA, 0x00), p(0xAA, 0x01), p(0xAA, 0x02), p(0xAA, 0x03), p(0xAA, 0x08), p(0xAA, 0x1F), p(0xAA, 0xE0), + p(0xAA, 0xE1), p(0xAA, 0xE2), p(0xAA, 0xE3), p(0xAA, 0xE4), p(0xAA, 0xE5), p(0xAA, 0xE8), p(0xAA, 0xF3), p(0xAA, 0xF4), + p(0xAA, 0xF5), p(0xAA, 0xF6), p(0xAA, 0xF7), p(0xAA, 0xFE), p(0xAA, 0xFF), p(0xB0, 0x08), p(0xB4, 0x08), p(0xB4, 0xE0), + p(0xB9, 0x08), p(0xB9, 0x09), p(0xB9, 0x1F), p(0xB9, 0x29), p(0xB9, 0x3F), p(0xB9, 0x68), p(0xB9, 0xE0), p(0xB9, 0xE1), + p(0xB9, 0xE8), p(0xB9, 0xE9), p(0xB9, 0xFF), p(0xBF, 0x02), p(0xC0, 0x03), p(0xC0, 0x3D), p(0xC1, 0x00), p(0xC2, 0xA8), + p(0xC3, 0x00), p(0xC3, 0x01), p(0xC8, 0x02), p(0xD0, 0x08), p(0xD1, 0xF3), p(0xD4, 0xFF), p(0xD6, 0xE0), p(0xDF, 0x02), + p(0xE0, 0x03), p(0xE0, 0x07), p(0xE0, 0x0F), p(0xE0, 0x23), p(0xE0, 0x43), p(0xE0, 0x63), p(0xE0, 0x83), p(0xE0, 0xC3), + p(0xE1, 0x03), p(0xE1, 0x23), p(0xE1, 0x43), p(0xE1, 0x63), p(0xE1, 0x83), p(0xE1, 0xC3), p(0xE2, 0x03), p(0xE3, 0x00), + p(0xE3, 0x03), p(0xE4, 0x00), p(0xE4, 0x03), p(0xE5, 0x03), p(0xE6, 0x03), p(0xE7, 0x03), p(0xE8, 0x03), p(0xE8, 0x07), + p(0xE8, 0x0B), p(0xE8, 0x0F), p(0xE8, 0x13), p(0xE8, 0x17), p(0xE8, 0x23), p(0xE8, 0x27), p(0xE8, 0x43), p(0xE9, 0x03), + p(0xEA, 0x03), p(0xEB, 0x01), p(0xEB, 0x40), p(0xEB, 0x61), p(0xEB, 0xA1), p(0xEB, 0xC1), p(0xEB, 0xE1), p(0xED, 0x7C), + p(0xF0, 0x08), p(0xF1, 0x7D), p(0xF3, 0x03), p(0xF3, 0x53), p(0xF4, 0x03), p(0xF5, 0x03), p(0xF5, 0x5B), p(0xF5, 0x7B), + p(0xF6, 0x03), p(0xF7, 0x03), p(0xF7, 0x63), p(0xF8, 0x03), p(0xF9, 0x00), p(0xF9, 0x01), p(0xF9, 0x02), p(0xF9, 0x03), + p(0xF9, 0x08), p(0xF9, 0x09), p(0xF9, 0x0A), p(0xF9, 0x1F), p(0xF9, 0x20), p(0xF9, 0x21), p(0xF9, 0x28), p(0xF9, 0x29), + p(0xF9, 0x2A), p(0xF9, 0x3F), p(0xF9, 0x40), p(0xF9, 0x48), p(0xF9, 0x68), p(0xF9, 0x69), p(0xF9, 0x6B), p(0xF9, 0x7F), + p(0xF9, 0x88), p(0xF9, 0xA8), p(0xF9, 0xC8), p(0xF9, 0xE0), p(0xF9, 0xE1), p(0xF9, 0xE2), p(0xF9, 0xE8), p(0xF9, 0xE9), + p(0xF9, 0xEA), p(0xF9, 0xF3), p(0xF9, 0xF4), p(0xF9, 0xF5), p(0xF9, 0xF7), p(0xF9, 0xFB), p(0xF9, 0xFF), p(0xFA, 0x03), + p(0xFA, 0xFF), p(0xFB, 0x73), p(0xFB, 0xFF), p(0xFC, 0xFF), p(0xFD, 0xFF), p(0xFE, 0x0B), p(0xFE, 0xFF), p(0xFF, 0x03), + p(0xFF, 0x17), p(0xFF, 0x43), p(0xFF, 0x54), p(0xFF, 0x83), p(0xFF, 0x97), p(0xFF, 0xB4), p(0xFF, 0xC3), p(0xFF, 0xFF), +}); + +static constexpr auto scores_x1 = std::to_array({ + 0x002, 0x040, 0x0D9, 0x04F, 0x0A3, 0x00E, 0x06F, 0x12A, 0x048, 0x0F0, 0x07E, 0x0AE, 0x06E, 0x032, 0x05F, 0x001, + 0x011, 0x0F4, 0x00D, 0x087, 0x006, 0x043, 0x092, 0x018, 0x086, 0x024, 0x0B8, 0x019, 0x08F, 0x0B5, 0x074, 0x01B, + 0x004, 0x0ED, 0x00A, 0x078, 0x14C, 0x073, 0x021, 0x071, 0x0C9, 0x18B, 0x1C0, 0x08C, 0x01C, 0x1A0, 0x05E, 0x046, + 0x10B, 0x027, 0x0A2, 0x05D, 0x114, 0x156, 0x112, 0x145, 0x110, 0x0A6, 0x016, 0x0EE, 0x12C, 0x137, 0x01E, 0x0CA, + 0x04E, 0x0DF, 0x0C6, 0x1A6, 0x009, 0x028, 0x05C, 0x0CF, 0x149, 0x1E7, 0x057, 0x16E, 0x013, 0x01A, 0x02A, 0x033, + 0x054, 0x076, 0x0A1, 0x0D3, 0x106, 0x171, 0x010, 0x072, 0x036, 0x0DB, 0x081, 0x126, 0x195, 0x050, 0x14E, 0x15D, + 0x0B7, 0x184, 0x00C, 0x155, 0x03E, 0x0E2, 0x04C, 0x066, 0x1D5, 0x101, 0x04A, 0x056, 0x1FE, 0x152, 0x02E, 0x026, + 0x058, 0x15A, 0x17B, 0x16D, 0x0D1, 0x0E6, 0x119, 0x1D7, 0x12D, 0x0CC, 0x13D, 0x150, 0x098, 0x136, 0x10E, 0x089, + 0x038, 0x096, 0x191, 0x08B, 0x0EC, 0x138, 0x1FD, 0x03B, 0x077, 0x1FF, 0x06A, 0x190, 0x068, 0x09E, 0x12B, 0x0D2, + 0x1A2, 0x133, 0x0E3, 0x0C5, 0x097, 0x117, 0x153, 0x0B2, 0x0F6, 0x0FB, 0x0E4, 0x0A9, 0x0D5, 0x014, 0x179, 0x01F, + 0x120, 0x1EB, 0x11D, 0x130, 0x02C, 0x180, 0x1E3, 0x03D, 0x0D4, 0x1E2, 0x0DC, 0x1CF, 0x064, 0x05A, 0x16A, 0x093, + 0x129, 0x0BF, 0x199, 0x13A, 0x0F9, 0x100, 0x108, 0x15F, 0x1CB, 0x11A, 0x03F, 0x015, 0x053, 0x1B9, 0x1EC, 0x02D, + 0x0E1, 0x167, 0x1CA, 0x025, 0x045, 0x10F, 0x146, 0x080, 0x052, 0x11B, 0x065, 0x13C, 0x04D, 0x1CD, 0x0DD, 0x169, + 0x139, 0x0AB, 0x10C, 0x0E0, 0x095, 0x0FA, 0x18F, 0x1DF, 0x148, 0x16B, 0x1F1, 0x19E, 0x18D, 0x1C3, 0x1F9, 0x0A4, + 0x1E1, 0x14F, 0x1E4, 0x161, 0x061, 0x160, 0x085, 0x0A7, 0x1F5, 0x029, 0x0D0, 0x1DD, 0x143, 0x0B3, 0x1A1, 0x007, + 0x1CE, 0x144, 0x000, 0x1B3, 0x079, 0x1FB, 0x07B, 0x0C7, 0x15B, 0x039, 0x0BA, 0x0C0, 0x1AC, 0x0AD, 0x088, 0x111, + 0x1ED, 0x103, 0x1A8, 0x165, 0x1BD, 0x1AD, 0x16C, 0x1AF, 0x0D6, 0x1BE, 0x19D, 0x173, 0x1DC, 0x13B, 0x1C7, 0x09D, + 0x0FF, 0x0B0, 0x172, 0x1A4, 0x187, 0x124, 0x17A, 0x044, 0x082, 0x125, 0x170, 0x1FA, 0x1C1, 0x0D7, 0x037, 0x185, + 0x11F, 0x142, 0x0A5, 0x14A, 0x0AA, 0x192, 0x0AF, 0x183, 0x1DE, 0x198, 0x197, 0x19C, 0x182, 0x132, 0x109, 0x188, + 0x1DA, 0x164, 0x069, 0x003, 0x0F1, 0x11C, 0x06B, 0x131, 0x1CC, 0x1B6, 0x0FD, 0x17D, 0x196, 0x1D3, 0x17E, 0x1EA, + 0x116, 0x1F7, 0x0C1, 0x0CB, 0x1A7, 0x099, 0x020, 0x02F, 0x07F, 0x0EB, 0x1F8, 0x094, 0x1D1, 0x1BA, 0x1AA, 0x151, + 0x060, 0x123, 0x063, 0x09A, 0x1B5, 0x09B, 0x186, 0x075, 0x1A3, 0x0B4, 0x1C6, 0x11E, 0x113, 0x166, 0x14D, 0x1D2, + 0x0AC, 0x1DB, 0x1F0, 0x06D, 0x03A, 0x083, 0x0D8, 0x0B6, 0x070, 0x12E, 0x134, 0x1BB, 0x1B0, 0x059, 0x1BC, 0x07C, + 0x030, 0x049, 0x0C4, 0x121, 0x1B1, 0x09C, 0x14B, 0x12F, 0x13F, 0x1E8, 0x1F3, 0x1E6, 0x15C, 0x1E0, 0x122, 0x105, + 0x062, 0x154, 0x07D, 0x1B8, 0x1C9, 0x1C4, 0x0E8, 0x1F6, 0x0DE, 0x128, 0x174, 0x19B, 0x035, 0x08A, 0x15E, 0x1B7, + 0x0BC, 0x19F, 0x1C5, 0x1D9, 0x0B1, 0x127, 0x0E5, 0x1D8, 0x005, 0x0F2, 0x194, 0x0C8, 0x0F7, 0x1D0, 0x0CD, 0x135, + 0x00F, 0x118, 0x0F8, 0x193, 0x104, 0x163, 0x01D, 0x177, 0x042, 0x1C8, 0x09F, 0x0DA, 0x13E, 0x1E5, 0x034, 0x0FC, + 0x1BF, 0x181, 0x176, 0x1C2, 0x168, 0x10A, 0x189, 0x0C3, 0x18C, 0x1AE, 0x102, 0x1D4, 0x18E, 0x1AB, 0x1FC, 0x140, + 0x1EE, 0x0BB, 0x03C, 0x023, 0x04B, 0x067, 0x041, 0x162, 0x091, 0x0BE, 0x084, 0x0E9, 0x051, 0x0C2, 0x1F4, 0x107, + 0x012, 0x06C, 0x157, 0x022, 0x18A, 0x1EF, 0x0A0, 0x0BD, 0x1E9, 0x10D, 0x1A9, 0x158, 0x0A8, 0x1F2, 0x0B9, 0x17C, + 0x0FE, 0x16F, 0x1B2, 0x02B, 0x047, 0x115, 0x031, 0x090, 0x1B4, 0x08D, 0x175, 0x0EF, 0x17F, 0x19A, 0x07A, 0x1A5, + 0x1D6, 0x147, 0x178, 0x0F3, 0x08E, 0x159, 0x055, 0x0CE, 0x008, 0x0F5, 0x017, 0x0E7, 0x05B, 0x141, 0x0EA, 0x00B, +}); diff --git a/src/scanner/frequency/x86_64.inl b/src/scanner/frequency/x86_64.inl new file mode 100644 index 0000000..e84e487 --- /dev/null +++ b/src/scanner/frequency/x86_64.inl @@ -0,0 +1,103 @@ +// Top 512 byte pair occurrences on 1 byte alignment, sorted. Sourced from multiple executables compiled for +// Windows using MSVC, ranging from versions 19.16 to 19.42. This list accounts for ~60.4% of all byte pairs. +static constexpr auto pairs_x1 = std::to_array>({ + p(0x00, 0x00), p(0x00, 0x01), p(0x00, 0x0F), p(0x00, 0x10), p(0x00, 0x33), p(0x00, 0x41), p(0x00, 0x44), p(0x00, 0x45), + p(0x00, 0x48), p(0x00, 0x49), p(0x00, 0x4C), p(0x00, 0x4D), p(0x00, 0x66), p(0x00, 0x72), p(0x00, 0x74), p(0x00, 0x75), + p(0x00, 0x80), p(0x00, 0x89), p(0x00, 0x8B), p(0x00, 0x90), p(0x00, 0xC6), p(0x00, 0xC7), p(0x00, 0xE8), p(0x00, 0xE9), + p(0x00, 0xEB), p(0x00, 0xF3), p(0x00, 0xFF), p(0x01, 0x00), p(0x01, 0x0F), p(0x01, 0x48), p(0x01, 0x4C), p(0x01, 0x74), + p(0x01, 0x75), p(0x01, 0xBA), p(0x02, 0x00), p(0x02, 0x48), p(0x03, 0x00), p(0x03, 0x48), p(0x03, 0x4C), p(0x04, 0x00), + p(0x04, 0x48), p(0x05, 0x00), p(0x05, 0x48), p(0x05, 0xE8), p(0x06, 0x00), p(0x06, 0x48), p(0x07, 0x00), p(0x07, 0x48), + p(0x07, 0xFF), p(0x08, 0x00), p(0x08, 0x01), p(0x08, 0x0F), p(0x08, 0x48), p(0x08, 0x49), p(0x08, 0x4C), p(0x08, 0x83), + p(0x08, 0xE8), p(0x08, 0xFF), p(0x09, 0x00), p(0x09, 0x48), p(0x0A, 0x00), p(0x0B, 0x48), p(0x0C, 0x00), p(0x0C, 0x48), + p(0x0C, 0x83), p(0x0F, 0x00), p(0x0F, 0x10), p(0x0F, 0x11), p(0x0F, 0x1F), p(0x0F, 0x28), p(0x0F, 0x29), p(0x0F, 0x48), + p(0x0F, 0x57), p(0x0F, 0x58), p(0x0F, 0x59), p(0x0F, 0x6F), p(0x0F, 0x76), p(0x0F, 0x7F), p(0x0F, 0x84), p(0x0F, 0x85), + p(0x0F, 0x87), p(0x0F, 0x95), p(0x0F, 0xAF), p(0x0F, 0xB6), p(0x0F, 0xB7), p(0x0F, 0xC1), p(0x10, 0x00), p(0x10, 0x01), + p(0x10, 0x05), p(0x10, 0x0F), p(0x10, 0x44), p(0x10, 0x48), p(0x10, 0x49), p(0x10, 0x4C), p(0x10, 0x72), p(0x10, 0xE8), + p(0x11, 0x00), p(0x11, 0x44), p(0x11, 0x45), p(0x11, 0x48), p(0x13, 0x48), p(0x18, 0x0F), p(0x18, 0x48), p(0x18, 0x49), + p(0x19, 0x48), p(0x1C, 0x48), p(0x1F, 0x76), p(0x20, 0x00), p(0x20, 0x01), p(0x20, 0x0F), p(0x20, 0x41), p(0x20, 0x48), + p(0x20, 0x49), p(0x20, 0x4C), p(0x20, 0x5B), p(0x20, 0x5F), p(0x20, 0xE8), p(0x20, 0xFF), p(0x24, 0x08), p(0x24, 0x10), + p(0x24, 0x18), p(0x24, 0x20), p(0x24, 0x28), p(0x24, 0x30), p(0x24, 0x38), p(0x24, 0x40), p(0x24, 0x48), p(0x24, 0x50), + p(0x24, 0x58), p(0x24, 0x60), p(0x24, 0x68), p(0x24, 0x70), p(0x24, 0x78), p(0x24, 0x80), p(0x27, 0x48), p(0x28, 0x00), + p(0x28, 0x48), p(0x28, 0x49), p(0x28, 0x4C), p(0x28, 0xE8), p(0x2B, 0x48), p(0x2B, 0xC1), p(0x2B, 0xC8), p(0x30, 0x0F), + p(0x30, 0x48), p(0x30, 0x49), p(0x30, 0x4C), p(0x30, 0xE8), p(0x33, 0xC0), p(0x33, 0xC4), p(0x33, 0xC9), p(0x33, 0xCC), + p(0x33, 0xD2), p(0x33, 0xF6), p(0x38, 0x00), p(0x38, 0x48), p(0x3B, 0xCA), p(0x40, 0x00), p(0x40, 0x08), p(0x40, 0x0F), + p(0x40, 0x10), p(0x40, 0x20), p(0x40, 0x48), p(0x40, 0x53), p(0x40, 0x88), p(0x40, 0xE8), p(0x41, 0x08), p(0x41, 0x0F), + p(0x41, 0x10), p(0x41, 0x54), p(0x41, 0x55), p(0x41, 0x56), p(0x41, 0x57), p(0x41, 0x5C), p(0x41, 0x5D), p(0x41, 0x5E), + p(0x41, 0x5F), p(0x41, 0x8B), p(0x41, 0xB8), p(0x41, 0xF8), p(0x42, 0x08), p(0x43, 0x08), p(0x43, 0x0C), p(0x43, 0x10), + p(0x43, 0x18), p(0x44, 0x0F), p(0x44, 0x24), p(0x44, 0x88), p(0x44, 0x89), p(0x44, 0x8B), p(0x44, 0x8D), p(0x45, 0x0F), + p(0x45, 0x33), p(0x46, 0x08), p(0x47, 0x08), p(0x47, 0x10), p(0x48, 0x03), p(0x48, 0x0F), p(0x48, 0x2B), p(0x48, 0x33), + p(0x48, 0x3B), p(0x48, 0x48), p(0x48, 0x81), p(0x48, 0x83), p(0x48, 0x85), p(0x48, 0x89), p(0x48, 0x8B), p(0x48, 0x8D), + p(0x48, 0xB8), p(0x48, 0xC1), p(0x48, 0xC7), p(0x48, 0xFF), p(0x49, 0x0F), p(0x49, 0x2B), p(0x49, 0x3B), p(0x49, 0x83), + p(0x49, 0x89), p(0x49, 0x8B), p(0x49, 0x8D), p(0x49, 0xF8), p(0x4B, 0x08), p(0x4C, 0x24), p(0x4C, 0x3B), p(0x4C, 0x89), + p(0x4C, 0x8B), p(0x4C, 0x8D), p(0x4D, 0x85), p(0x4D, 0x89), p(0x4D, 0x8B), p(0x50, 0x08), p(0x50, 0x48), p(0x53, 0x48), + p(0x54, 0x24), p(0x54, 0x41), p(0x55, 0x41), p(0x55, 0x48), p(0x55, 0x56), p(0x56, 0x41), p(0x56, 0x48), p(0x56, 0x57), + p(0x57, 0x41), p(0x57, 0x48), p(0x57, 0xC0), p(0x57, 0xC9), p(0x58, 0x00), p(0x58, 0x48), p(0x5B, 0xC3), p(0x5C, 0x24), + p(0x5C, 0x5F), p(0x5D, 0x41), p(0x5D, 0xC3), p(0x5E, 0x41), p(0x5E, 0x5D), p(0x5E, 0x5F), p(0x5F, 0x41), p(0x5F, 0x5E), + p(0x5F, 0xC3), p(0x60, 0x48), p(0x64, 0x24), p(0x66, 0x0F), p(0x66, 0x89), p(0x68, 0x48), p(0x6C, 0x24), p(0x70, 0x48), + p(0x72, 0x1C), p(0x74, 0x05), p(0x74, 0x24), p(0x75, 0x10), p(0x76, 0x07), p(0x78, 0x48), p(0x7C, 0x24), p(0x7F, 0x44), + p(0x7F, 0x45), p(0x80, 0x00), p(0x80, 0x48), p(0x81, 0xC4), p(0x81, 0xEC), p(0x81, 0xFA), p(0x83, 0xC0), p(0x83, 0xC1), + p(0x83, 0xC2), p(0x83, 0xC4), p(0x83, 0xEC), p(0x83, 0xF8), p(0x83, 0xFA), p(0x83, 0xFF), p(0x84, 0x00), p(0x84, 0x24), + p(0x84, 0xC0), p(0x85, 0xC0), p(0x85, 0xC9), p(0x85, 0xD2), p(0x85, 0xDB), p(0x85, 0xF6), p(0x85, 0xFF), p(0x88, 0x00), + p(0x89, 0x01), p(0x89, 0x02), p(0x89, 0x03), p(0x89, 0x05), p(0x89, 0x41), p(0x89, 0x42), p(0x89, 0x43), p(0x89, 0x44), + p(0x89, 0x45), p(0x89, 0x46), p(0x89, 0x47), p(0x89, 0x4C), p(0x89, 0x4D), p(0x89, 0x54), p(0x89, 0x5C), p(0x89, 0x5D), + p(0x89, 0x6B), p(0x89, 0x6C), p(0x89, 0x73), p(0x89, 0x74), p(0x89, 0x75), p(0x89, 0x7C), p(0x89, 0x7D), p(0x89, 0x84), + p(0x89, 0x85), p(0x8B, 0x00), p(0x8B, 0x01), p(0x8B, 0x03), p(0x8B, 0x04), p(0x8B, 0x05), p(0x8B, 0x06), p(0x8B, 0x07), + p(0x8B, 0x08), p(0x8B, 0x0D), p(0x8B, 0x40), p(0x8B, 0x41), p(0x8B, 0x42), p(0x8B, 0x43), p(0x8B, 0x44), p(0x8B, 0x45), + p(0x8B, 0x46), p(0x8B, 0x47), p(0x8B, 0x48), p(0x8B, 0x49), p(0x8B, 0x4B), p(0x8B, 0x4C), p(0x8B, 0x4D), p(0x8B, 0x4E), + p(0x8B, 0x4F), p(0x8B, 0x54), p(0x8B, 0x55), p(0x8B, 0x5B), p(0x8B, 0x5C), p(0x8B, 0x5D), p(0x8B, 0x73), p(0x8B, 0x74), + p(0x8B, 0x7C), p(0x8B, 0x80), p(0x8B, 0x85), p(0x8B, 0x8C), p(0x8B, 0x8D), p(0x8B, 0x9C), p(0x8B, 0xC0), p(0x8B, 0xC1), + p(0x8B, 0xC2), p(0x8B, 0xC3), p(0x8B, 0xC6), p(0x8B, 0xC7), p(0x8B, 0xC8), p(0x8B, 0xCB), p(0x8B, 0xCD), p(0x8B, 0xCE), + p(0x8B, 0xCF), p(0x8B, 0xD0), p(0x8B, 0xD3), p(0x8B, 0xD6), p(0x8B, 0xD7), p(0x8B, 0xD8), p(0x8B, 0xD9), p(0x8B, 0xDA), + p(0x8B, 0xE3), p(0x8B, 0xEA), p(0x8B, 0xF0), p(0x8B, 0xF1), p(0x8B, 0xF2), p(0x8B, 0xF8), p(0x8B, 0xF9), p(0x8B, 0xFA), + p(0x8C, 0x24), p(0x8D, 0x05), p(0x8D, 0x0D), p(0x8D, 0x15), p(0x8D, 0x41), p(0x8D, 0x44), p(0x8D, 0x45), p(0x8D, 0x4B), + p(0x8D, 0x4C), p(0x8D, 0x4D), p(0x8D, 0x4F), p(0x8D, 0x54), p(0x8D, 0x55), p(0x8D, 0x85), p(0x8D, 0x8A), p(0x8D, 0x8D), + p(0x8D, 0x95), p(0x8D, 0x9C), p(0x90, 0x00), p(0x90, 0x48), p(0x90, 0x4C), p(0x95, 0xC2), p(0x98, 0x00), p(0x9C, 0x24), + p(0xA0, 0x00), p(0xA8, 0x00), p(0xAA, 0xAA), p(0xB0, 0x00), p(0xB0, 0x01), p(0xB8, 0x00), p(0xC0, 0x00), p(0xC0, 0x0F), + p(0xC0, 0x48), p(0xC0, 0x74), p(0xC0, 0x75), p(0xC0, 0xF3), p(0xC0, 0xF8), p(0xC1, 0x43), p(0xC1, 0x48), p(0xC1, 0xE8), + p(0xC1, 0xF8), p(0xC2, 0x27), p(0xC2, 0x48), p(0xC3, 0x48), p(0xC3, 0xCC), p(0xC3, 0xE8), p(0xC4, 0x20), p(0xC4, 0x28), + p(0xC4, 0x30), p(0xC4, 0x48), p(0xC6, 0x44), p(0xC6, 0x45), p(0xC6, 0x48), p(0xC6, 0xF0), p(0xC7, 0x43), p(0xC7, 0x44), + p(0xC7, 0x45), p(0xC7, 0x48), p(0xC7, 0x85), p(0xC7, 0xF0), p(0xC8, 0x00), p(0xC8, 0x48), p(0xC8, 0xE8), p(0xC9, 0x0F), + p(0xC9, 0x48), p(0xC9, 0x74), p(0xC9, 0xF3), p(0xCB, 0x48), p(0xCB, 0xE8), p(0xCB, 0xFF), p(0xCC, 0x40), p(0xCC, 0x48), + p(0xCC, 0xCC), p(0xCC, 0xE8), p(0xCE, 0x48), p(0xCE, 0xE8), p(0xCF, 0x48), p(0xCF, 0xE8), p(0xD0, 0x00), p(0xD0, 0x48), + p(0xD2, 0x48), p(0xD3, 0x48), p(0xD6, 0x48), p(0xD7, 0x48), p(0xD8, 0x00), p(0xD8, 0x48), p(0xD9, 0x48), p(0xDA, 0x48), + p(0xDB, 0x74), p(0xE0, 0x00), p(0xE0, 0x48), p(0xE8, 0x00), p(0xE8, 0x48), p(0xEB, 0x03), p(0xEC, 0x20), p(0xF0, 0x00), + p(0xF0, 0x0F), p(0xF0, 0x48), p(0xF0, 0xFF), p(0xF2, 0x0F), p(0xF2, 0x48), p(0xF3, 0x0F), p(0xF8, 0x01), p(0xF8, 0x1F), + p(0xF8, 0x48), p(0xF9, 0x48), p(0xFA, 0x00), p(0xFA, 0x0F), p(0xFA, 0x10), p(0xFA, 0x48), p(0xFB, 0x48), p(0xFC, 0x48), + p(0xFD, 0x48), p(0xFD, 0x90), p(0xFD, 0xFF), p(0xFE, 0x48), p(0xFE, 0x90), p(0xFE, 0xFF), p(0xFF, 0x0F), p(0xFF, 0x15), + p(0xFF, 0x48), p(0xFF, 0x4C), p(0xFF, 0x50), p(0xFF, 0x74), p(0xFF, 0x90), p(0xFF, 0xC2), p(0xFF, 0xCC), p(0xFF, 0xFF), +}); + +static constexpr auto scores_x1 = std::to_array({ + 0x000, 0x0C0, 0x028, 0x084, 0x1C5, 0x083, 0x11B, 0x1E3, 0x005, 0x042, 0x02F, 0x18B, 0x146, 0x08B, 0x0F6, 0x137, + 0x15B, 0x1DC, 0x10B, 0x0BD, 0x0D6, 0x129, 0x01D, 0x0C1, 0x111, 0x182, 0x065, 0x008, 0x11E, 0x016, 0x1CB, 0x17E, + 0x051, 0x1F9, 0x021, 0x034, 0x03E, 0x014, 0x1DB, 0x07C, 0x05B, 0x0C4, 0x0B7, 0x159, 0x0FA, 0x0A2, 0x102, 0x06D, + 0x0E9, 0x0DE, 0x1A1, 0x197, 0x012, 0x132, 0x141, 0x0E1, 0x1C0, 0x0A3, 0x1C6, 0x19C, 0x1AD, 0x18F, 0x1C3, 0x1D3, + 0x0CA, 0x087, 0x024, 0x00D, 0x0D1, 0x08E, 0x126, 0x134, 0x033, 0x1D0, 0x1A7, 0x181, 0x106, 0x056, 0x038, 0x0DD, + 0x1DD, 0x19F, 0x13D, 0x046, 0x10F, 0x05A, 0x053, 0x1DE, 0x1D1, 0x135, 0x1E4, 0x015, 0x144, 0x123, 0x101, 0x1E6, + 0x1EF, 0x0D4, 0x0B8, 0x160, 0x1CE, 0x15D, 0x044, 0x1FD, 0x1FF, 0x0CE, 0x0FD, 0x125, 0x194, 0x18A, 0x1CF, 0x01F, + 0x13F, 0x0C8, 0x18D, 0x127, 0x0F3, 0x1B3, 0x0BB, 0x0A4, 0x0A5, 0x011, 0x039, 0x01B, 0x045, 0x031, 0x052, 0x04B, + 0x079, 0x067, 0x0B2, 0x080, 0x0EF, 0x192, 0x08C, 0x1BA, 0x04F, 0x1EE, 0x1BF, 0x173, 0x1D4, 0x0B1, 0x1B9, 0x1F0, + 0x027, 0x1A0, 0x17C, 0x16B, 0x06C, 0x0D9, 0x171, 0x0D7, 0x131, 0x1A3, 0x1D5, 0x049, 0x190, 0x17F, 0x064, 0x1B0, + 0x195, 0x1A5, 0x047, 0x12B, 0x19E, 0x1F3, 0x0DC, 0x05D, 0x172, 0x175, 0x1E0, 0x0C5, 0x115, 0x170, 0x1D7, 0x0BE, + 0x10E, 0x0AA, 0x0C6, 0x183, 0x12E, 0x0B3, 0x1A2, 0x138, 0x1C9, 0x0E5, 0x007, 0x1BC, 0x0DB, 0x0F4, 0x1A6, 0x1D2, + 0x081, 0x19A, 0x136, 0x1AF, 0x14C, 0x076, 0x060, 0x050, 0x030, 0x07E, 0x020, 0x006, 0x00B, 0x003, 0x001, 0x004, + 0x1E7, 0x075, 0x02B, 0x072, 0x1EC, 0x13A, 0x0D8, 0x15A, 0x091, 0x009, 0x0E4, 0x0EB, 0x1F7, 0x00F, 0x199, 0x010, + 0x00C, 0x018, 0x14E, 0x1BD, 0x073, 0x1BE, 0x068, 0x152, 0x026, 0x177, 0x1A4, 0x1BB, 0x153, 0x113, 0x1FA, 0x0E8, + 0x0F0, 0x054, 0x04C, 0x179, 0x1FE, 0x0AE, 0x0E3, 0x013, 0x1AC, 0x1F5, 0x0C2, 0x178, 0x140, 0x1D8, 0x114, 0x0E0, + 0x07B, 0x09C, 0x1EB, 0x04D, 0x154, 0x0D5, 0x082, 0x0BF, 0x0F2, 0x16E, 0x02E, 0x158, 0x0FF, 0x12F, 0x05E, 0x16C, + 0x163, 0x07F, 0x1C1, 0x11A, 0x0B9, 0x09A, 0x08D, 0x128, 0x08A, 0x02C, 0x03B, 0x023, 0x0BC, 0x1EA, 0x1B5, 0x098, + 0x097, 0x02A, 0x032, 0x16A, 0x092, 0x1D6, 0x13C, 0x104, 0x1B6, 0x1B1, 0x17B, 0x1C8, 0x10D, 0x148, 0x0B4, 0x01A, + 0x03F, 0x1ED, 0x0EC, 0x0A9, 0x168, 0x119, 0x035, 0x180, 0x1AA, 0x105, 0x1FC, 0x061, 0x0ED, 0x0AD, 0x0FC, 0x11C, + 0x0E7, 0x071, 0x04E, 0x077, 0x116, 0x07A, 0x143, 0x0FB, 0x11F, 0x1DA, 0x03C, 0x0A1, 0x12A, 0x120, 0x0D0, 0x0AC, + 0x15E, 0x145, 0x112, 0x09F, 0x155, 0x089, 0x06E, 0x1E2, 0x19D, 0x164, 0x0F9, 0x13B, 0x063, 0x196, 0x1B8, 0x0E6, + 0x1CC, 0x198, 0x1F6, 0x166, 0x122, 0x107, 0x13E, 0x095, 0x10C, 0x06F, 0x088, 0x05F, 0x057, 0x025, 0x1CA, 0x06A, + 0x040, 0x0B5, 0x0F5, 0x0F8, 0x109, 0x0A6, 0x0CF, 0x147, 0x185, 0x1E8, 0x110, 0x12C, 0x169, 0x103, 0x0D3, 0x167, + 0x0DA, 0x01E, 0x043, 0x066, 0x189, 0x093, 0x099, 0x130, 0x048, 0x037, 0x191, 0x059, 0x07D, 0x19B, 0x16F, 0x062, + 0x139, 0x1E5, 0x0C7, 0x01C, 0x1B2, 0x1E9, 0x142, 0x09D, 0x0E2, 0x162, 0x1CD, 0x0F7, 0x1B7, 0x12D, 0x0F1, 0x036, + 0x03A, 0x04A, 0x0EA, 0x184, 0x0CB, 0x0FE, 0x03D, 0x1A9, 0x187, 0x0A0, 0x070, 0x055, 0x02D, 0x1FB, 0x078, 0x1F4, + 0x188, 0x0B0, 0x118, 0x150, 0x10A, 0x1AE, 0x1AB, 0x069, 0x0AF, 0x0A7, 0x1C2, 0x121, 0x17A, 0x09B, 0x0CD, 0x16D, + 0x1F1, 0x041, 0x1DF, 0x094, 0x074, 0x1F8, 0x0CC, 0x019, 0x002, 0x05C, 0x1A8, 0x0C9, 0x100, 0x086, 0x14F, 0x090, + 0x1F2, 0x149, 0x165, 0x117, 0x1E1, 0x0A8, 0x133, 0x18E, 0x0B6, 0x15C, 0x157, 0x1C7, 0x15F, 0x193, 0x0AB, 0x17D, + 0x058, 0x0EE, 0x124, 0x0C3, 0x186, 0x022, 0x06B, 0x09E, 0x029, 0x14D, 0x096, 0x161, 0x1D9, 0x156, 0x176, 0x108, + 0x0BA, 0x1B4, 0x1C4, 0x08F, 0x14B, 0x0DF, 0x11D, 0x00E, 0x017, 0x151, 0x174, 0x14A, 0x085, 0x0D2, 0x18C, 0x00A, +}); diff --git a/src/arch/arm/Neon.cpp b/src/scanner/neon.cpp similarity index 99% rename from src/arch/arm/Neon.cpp rename to src/scanner/neon.cpp index 3f44e3b..219448a 100644 --- a/src/arch/arm/Neon.cpp +++ b/src/scanner/neon.cpp @@ -4,7 +4,7 @@ #include -#include "../../Utils.hpp" +#include "simd.hpp" #include diff --git a/src/Utils.hpp b/src/scanner/simd.hpp similarity index 71% rename from src/Utils.hpp rename to src/scanner/simd.hpp index fd90514..aed0d02 100644 --- a/src/Utils.hpp +++ b/src/scanner/simd.hpp @@ -1,46 +1,9 @@ #pragma once -#include -#include -#include -#include -#include - -#include +#include "../utils.hpp" namespace hat::detail { - template - void scan_context::emplace(Args&&... args) { - static_assert(std::is_same_v>); - static_assert(sizeof(T) <= impl_buffer_size); - static_assert(alignof(T) <= impl_buffer_align); - - std::construct_at(reinterpret_cast(impl_buffer_.data()), std::forward(args)...); - - if constexpr (not std::is_trivially_destructible_v) { - impl_deleter_ = [](const void* buffer) { - std::destroy_at(static_cast(buffer)); - }; - } - } - - template - const T& scan_context::get() const { - return *reinterpret_cast(impl_buffer_.data()); - } - - constexpr std::uintptr_t fast_align_down(const std::uintptr_t address, const std::size_t alignment) { - return address & ~static_cast(alignment - 1); - } - - constexpr std::uintptr_t fast_align_up(const std::uintptr_t address, const std::size_t alignment) { - return (address + alignment - 1) & ~static_cast(alignment - 1); - } - - std::optional get_optimal_pair(const scan_parameters& params); - std::optional get_optimal_byte(const scan_parameters& params); - struct simd_context { std::size_t cmpIndex{}; }; diff --git a/src/arch/x86/SSE.cpp b/src/scanner/sse.cpp similarity index 99% rename from src/arch/x86/SSE.cpp rename to src/scanner/sse.cpp index 1040d3e..88843e6 100644 --- a/src/arch/x86/SSE.cpp +++ b/src/scanner/sse.cpp @@ -4,7 +4,7 @@ #include -#include "../../Utils.hpp" +#include "simd.hpp" #include diff --git a/src/utils.hpp b/src/utils.hpp new file mode 100644 index 0000000..3b02497 --- /dev/null +++ b/src/utils.hpp @@ -0,0 +1,43 @@ +#pragma once + +#include +#include +#include +#include +#include + +#include + +namespace hat::detail { + + template + void scan_context::emplace(Args&&... args) { + static_assert(std::is_same_v>); + static_assert(sizeof(T) <= impl_buffer_size); + static_assert(alignof(T) <= impl_buffer_align); + + std::construct_at(reinterpret_cast(impl_buffer_.data()), std::forward(args)...); + + if constexpr (not std::is_trivially_destructible_v) { + impl_deleter_ = [](const void* buffer) { + std::destroy_at(static_cast(buffer)); + }; + } + } + + template + const T& scan_context::get() const { + return *reinterpret_cast(impl_buffer_.data()); + } + + constexpr std::uintptr_t fast_align_down(const std::uintptr_t address, const std::size_t alignment) { + return address & ~static_cast(alignment - 1); + } + + constexpr std::uintptr_t fast_align_up(const std::uintptr_t address, const std::size_t alignment) { + return (address + alignment - 1) & ~static_cast(alignment - 1); + } + + std::optional get_optimal_pair(const scan_parameters& params); + std::optional get_optimal_byte(const scan_parameters& params); +} From 38a048420a29f61bcf85bf82f4aaa75a1679dd67 Mon Sep 17 00:00:00 2001 From: Brady Date: Thu, 30 Jul 2026 22:00:50 -0500 Subject: [PATCH 03/20] Move `system_info` implementations to `src/system` --- src/{arch/arm/System.cpp => system/arm.cpp} | 0 src/{System.cpp => system/singleton.cpp} | 0 src/{os/unix/System.cpp => system/unix.cpp} | 0 src/{os/win32/System.cpp => system/win32.cpp} | 0 src/{arch/x86/System.cpp => system/x86.cpp} | 0 5 files changed, 0 insertions(+), 0 deletions(-) rename src/{arch/arm/System.cpp => system/arm.cpp} (100%) rename src/{System.cpp => system/singleton.cpp} (100%) rename src/{os/unix/System.cpp => system/unix.cpp} (100%) rename src/{os/win32/System.cpp => system/win32.cpp} (100%) rename src/{arch/x86/System.cpp => system/x86.cpp} (100%) diff --git a/src/arch/arm/System.cpp b/src/system/arm.cpp similarity index 100% rename from src/arch/arm/System.cpp rename to src/system/arm.cpp diff --git a/src/System.cpp b/src/system/singleton.cpp similarity index 100% rename from src/System.cpp rename to src/system/singleton.cpp diff --git a/src/os/unix/System.cpp b/src/system/unix.cpp similarity index 100% rename from src/os/unix/System.cpp rename to src/system/unix.cpp diff --git a/src/os/win32/System.cpp b/src/system/win32.cpp similarity index 100% rename from src/os/win32/System.cpp rename to src/system/win32.cpp diff --git a/src/arch/x86/System.cpp b/src/system/x86.cpp similarity index 100% rename from src/arch/x86/System.cpp rename to src/system/x86.cpp From cf59c2ccf0314616150138c23ef6dfa55ecdb534 Mon Sep 17 00:00:00 2001 From: Brady Date: Thu, 30 Jul 2026 22:30:11 -0500 Subject: [PATCH 04/20] Move `process.hpp` implementations to `src/process` --- src/{os/linux/Process.cpp => process/linux.cpp} | 0 src/{os/mac/Process.cpp => process/mac.cpp} | 0 src/{os/win32/Process.cpp => process/win32.cpp} | 0 3 files changed, 0 insertions(+), 0 deletions(-) rename src/{os/linux/Process.cpp => process/linux.cpp} (100%) rename src/{os/mac/Process.cpp => process/mac.cpp} (100%) rename src/{os/win32/Process.cpp => process/win32.cpp} (100%) diff --git a/src/os/linux/Process.cpp b/src/process/linux.cpp similarity index 100% rename from src/os/linux/Process.cpp rename to src/process/linux.cpp diff --git a/src/os/mac/Process.cpp b/src/process/mac.cpp similarity index 100% rename from src/os/mac/Process.cpp rename to src/process/mac.cpp diff --git a/src/os/win32/Process.cpp b/src/process/win32.cpp similarity index 100% rename from src/os/win32/Process.cpp rename to src/process/win32.cpp From 1a0ebb40fe5d71c4652d527f32b9243b351d5f38 Mon Sep 17 00:00:00 2001 From: Brady Date: Fri, 31 Jul 2026 20:31:59 -0500 Subject: [PATCH 05/20] More scanner refactoring --- src/Scanner.cpp | 158 ---------------------------------- src/scanner/scan_context.hpp | 30 +++++++ src/scanner/simd.hpp | 1 + src/scanner/single.cpp | 159 +++++++++++++++++++++++++++++++++++ src/utils.hpp | 26 +----- 5 files changed, 192 insertions(+), 182 deletions(-) create mode 100644 src/scanner/scan_context.hpp create mode 100644 src/scanner/single.cpp diff --git a/src/Scanner.cpp b/src/Scanner.cpp index 9cc950b..dd7ecc0 100644 --- a/src/Scanner.cpp +++ b/src/Scanner.cpp @@ -10,92 +10,6 @@ #include "utils.hpp" -namespace { - - struct anchor_matcher { - - anchor_matcher(const hat::signature_view& signature, const std::span& anchors, const std::size_t literals) : - signature_(signature), - anchors_(anchors), - literals_(literals) {} - - [[nodiscard]] std::pair top() const { - return anchor(0); - } - - [[nodiscard]] bool matches(const std::byte* buffer) const { - for (size_t i = 1; i < literals_; i++) { - const auto [element, offset] = anchor(i); - if (buffer[offset] != element) { - std::swap(anchors_[i], anchors_[i - 1]); - return false; - } - } - - for (size_t i = literals_; i < anchors_.size(); i++) { - const auto [element, offset] = anchor(i); - if (buffer[offset] != element) { - return false; - } - } - - return true; - } - - [[nodiscard]] std::size_t literals() const { - return literals_; - } - - private: - [[nodiscard]] std::pair anchor(const size_t index) const { - const auto offset = anchors_[index]; - return {signature_[offset], offset}; - } - - hat::signature_view signature_; - std::span anchors_; - std::size_t literals_; - }; - - struct signature_info { - std::optional optimalByteIndex{}; // The index of the least common fully masked element - std::size_t anyMaskCount{}; // The number of elements with a non-zero mask - }; - - struct std_context { - mutable std::vector anchors{}; - std::size_t literals{}; - - explicit std_context(const hat::detail::scan_parameters& params) { - this->anchors.resize(static_cast( - std::ranges::count_if(params.signature, &hat::signature_element::any))); - auto it = this->anchors.begin(); - - // Add fully masked bytes - for (size_t i{}; auto e : params.signature) { - if (e.all()) { - *it++ = i; - } - i++; - } - this->literals = static_cast(std::distance(this->anchors.begin(), it)); - - // Add partially masked bytes - for (size_t i{}; auto e : params.signature) { - if (!e.all() && !e.none()) { - *it++ = i; - } - i++; - } - } - }; - - anchor_matcher create_anchor_matcher(const hat::detail::scan_context& context) { - const auto& std = context.get(); - return {context.signature(), std.anchors, std.literals}; - } -} - namespace hat::detail { static constexpr std::uint16_t NUM_PAIRS = 512; @@ -199,78 +113,6 @@ namespace hat::detail { return min; } - template - static const_scan_result find_pattern_single(const std::byte* begin, const std::byte* end, const scan_context& context) { - static constexpr auto stride = alignment_stride; - - const auto scanBegin = align_up(begin); - const auto scanEnd = align_up(end - context.signature().size() + 1); - if (scanBegin >= scanEnd) { - return nullptr; - } - - const auto matcher = create_anchor_matcher(context); - for (auto i = scanBegin; i != scanEnd; i += stride) { - if (matcher.literals() > 0) { - const auto [anchor, offset] = matcher.top(); - while (i != scanEnd) { - if (i[offset] == anchor) { - break; - } - i += stride; - } - if (i == scanEnd) LIBHAT_UNLIKELY break; - } - if (matcher.matches(i)) { - return i; - } - } - - return nullptr; - } - - template<> - const_scan_result find_pattern_single(const std::byte* begin, const std::byte* end, const scan_context& context) { - const auto signature = context.signature(); - const auto scanEnd = end - signature.size() + 1; - - const auto matcher = create_anchor_matcher(context); - for (auto i = begin; i != scanEnd; i++) { - // This check should get hoisted out by the optimizer - if (matcher.literals() > 0) { - const auto [anchor, offset] = matcher.top(); - - // Use std::find to efficiently find the first byte - #ifndef _MSC_VER - i = static_cast( - std::memchr(i + offset, static_cast(anchor.value()), static_cast(scanEnd - i))); - if (!i) LIBHAT_UNLIKELY break; - i -= offset; - #elif __cpp_lib_execution >= 201902L - i = std::find(std::execution::unseq, i + offset, scanEnd + offset, anchor.value()) - offset; - if (i == scanEnd) LIBHAT_UNLIKELY break; - #else - i = std::find(i + offset, scanEnd + offset, anchor.value()) - offset; - if (i == scanEnd) LIBHAT_UNLIKELY break; - #endif - } - if (matcher.matches(i)) { - return i; - } - } - return nullptr; - } - - template<> - scan_context create_context(const scan_parameters& params) { - switch (params.alignment) { - case scan_alignment::X1: return {params.signature, &find_pattern_single, std::type_identity{}, params}; - case scan_alignment::X4: return {params.signature, &find_pattern_single, std::type_identity{}, params}; - case scan_alignment::X16: return {params.signature, &find_pattern_single, std::type_identity{}, params}; - } - LIBHAT_UNREACHABLE(); - } - template<> scan_context create_context(const scan_parameters& params) { // const auto& ext = get_system().extensions; diff --git a/src/scanner/scan_context.hpp b/src/scanner/scan_context.hpp new file mode 100644 index 0000000..daa5fc8 --- /dev/null +++ b/src/scanner/scan_context.hpp @@ -0,0 +1,30 @@ +#pragma once + +#include +#include +#include + +#include + +namespace hat::detail { + + template + void scan_context::emplace(Args&&... args) { + static_assert(std::is_same_v>); + static_assert(sizeof(T) <= impl_buffer_size); + static_assert(alignof(T) <= impl_buffer_align); + + std::construct_at(reinterpret_cast(impl_buffer_.data()), std::forward(args)...); + + if constexpr (not std::is_trivially_destructible_v) { + impl_deleter_ = [](const void* buffer) { + std::destroy_at(static_cast(buffer)); + }; + } + } + + template + const T& scan_context::get() const { + return *reinterpret_cast(impl_buffer_.data()); + } +} diff --git a/src/scanner/simd.hpp b/src/scanner/simd.hpp index aed0d02..8f4c078 100644 --- a/src/scanner/simd.hpp +++ b/src/scanner/simd.hpp @@ -1,5 +1,6 @@ #pragma once +#include "scan_context.hpp" #include "../utils.hpp" namespace hat::detail { diff --git a/src/scanner/single.cpp b/src/scanner/single.cpp new file mode 100644 index 0000000..e97f954 --- /dev/null +++ b/src/scanner/single.cpp @@ -0,0 +1,159 @@ +#include + +#include "scan_context.hpp" + +namespace { + + struct anchor_matcher { + + anchor_matcher(const hat::signature_view& signature, const std::span& anchors, const std::size_t literals) : + signature_(signature), + anchors_(anchors), + literals_(literals) {} + + [[nodiscard]] std::pair top() const { + return anchor(0); + } + + [[nodiscard]] bool matches(const std::byte* buffer) const { + for (size_t i = 1; i < literals_; i++) { + const auto [element, offset] = anchor(i); + if (buffer[offset] != element) { + std::swap(anchors_[i], anchors_[i - 1]); + return false; + } + } + + for (size_t i = literals_; i < anchors_.size(); i++) { + const auto [element, offset] = anchor(i); + if (buffer[offset] != element) { + return false; + } + } + + return true; + } + + [[nodiscard]] std::size_t literals() const { + return literals_; + } + + private: + [[nodiscard]] std::pair anchor(const size_t index) const { + const auto offset = anchors_[index]; + return {signature_[offset], offset}; + } + + hat::signature_view signature_; + std::span anchors_; + std::size_t literals_; + }; + + struct std_context { + mutable std::vector anchors{}; + std::size_t literals{}; + + explicit std_context(const hat::detail::scan_parameters& params) { + this->anchors.resize(static_cast( + std::ranges::count_if(params.signature, &hat::signature_element::any))); + auto it = this->anchors.begin(); + + // Add fully masked bytes + for (size_t i{}; auto e : params.signature) { + if (e.all()) { + *it++ = i; + } + i++; + } + this->literals = static_cast(std::distance(this->anchors.begin(), it)); + + // Add partially masked bytes + for (size_t i{}; auto e : params.signature) { + if (!e.all() && !e.none()) { + *it++ = i; + } + i++; + } + } + }; + + anchor_matcher create_anchor_matcher(const hat::detail::scan_context& context) { + const auto& std = context.get(); + return {context.signature(), std.anchors, std.literals}; + } +} + +namespace hat::detail { + + template + static const_scan_result find_pattern_single(const std::byte* begin, const std::byte* end, const scan_context& context) { + static constexpr auto stride = alignment_stride; + + const auto scanBegin = align_up(begin); + const auto scanEnd = align_up(end - context.signature().size() + 1); + if (scanBegin >= scanEnd) { + return nullptr; + } + + const auto matcher = create_anchor_matcher(context); + for (auto i = scanBegin; i != scanEnd; i += stride) { + if (matcher.literals() > 0) { + const auto [anchor, offset] = matcher.top(); + while (i != scanEnd) { + if (i[offset] == anchor) { + break; + } + i += stride; + } + if (i == scanEnd) LIBHAT_UNLIKELY break; + } + if (matcher.matches(i)) { + return i; + } + } + + return nullptr; + } + + template<> + const_scan_result find_pattern_single(const std::byte* begin, const std::byte* end, const scan_context& context) { + const auto signature = context.signature(); + const auto scanEnd = end - signature.size() + 1; + + const auto matcher = create_anchor_matcher(context); + for (auto i = begin; i != scanEnd; i++) { + // This check should get hoisted out by the optimizer + if (matcher.literals() > 0) { + const auto [anchor, offset] = matcher.top(); + + // Use std::find to efficiently find the first byte + #ifndef _MSC_VER + i = static_cast( + std::memchr(i + offset, static_cast(anchor.value()), static_cast(scanEnd - i))); + if (!i) LIBHAT_UNLIKELY break; + i -= offset; + #elif __cpp_lib_execution >= 201902L + i = std::find(std::execution::unseq, i + offset, scanEnd + offset, anchor.value()) - offset; + if (i == scanEnd) LIBHAT_UNLIKELY break; + #else + i = std::find(i + offset, scanEnd + offset, anchor.value()) - offset; + if (i == scanEnd) LIBHAT_UNLIKELY break; + #endif + } + if (matcher.matches(i)) { + return i; + } + } + return nullptr; + } + + template<> + scan_context create_context(const scan_parameters& params) { + switch (params.alignment) { + case scan_alignment::X1: return {params.signature, &find_pattern_single, std::type_identity{}, params}; + case scan_alignment::X4: return {params.signature, &find_pattern_single, std::type_identity{}, params}; + case scan_alignment::X16: return {params.signature, &find_pattern_single, std::type_identity{}, params}; + } + LIBHAT_UNREACHABLE(); + } +} diff --git a/src/utils.hpp b/src/utils.hpp index 3b02497..9868ac0 100644 --- a/src/utils.hpp +++ b/src/utils.hpp @@ -1,34 +1,12 @@ #pragma once -#include #include #include -#include -#include - -#include +#include namespace hat::detail { - template - void scan_context::emplace(Args&&... args) { - static_assert(std::is_same_v>); - static_assert(sizeof(T) <= impl_buffer_size); - static_assert(alignof(T) <= impl_buffer_align); - - std::construct_at(reinterpret_cast(impl_buffer_.data()), std::forward(args)...); - - if constexpr (not std::is_trivially_destructible_v) { - impl_deleter_ = [](const void* buffer) { - std::destroy_at(static_cast(buffer)); - }; - } - } - - template - const T& scan_context::get() const { - return *reinterpret_cast(impl_buffer_.data()); - } + struct scan_parameters; constexpr std::uintptr_t fast_align_down(const std::uintptr_t address, const std::size_t alignment) { return address & ~static_cast(alignment - 1); From 6e3161a45a948c185cf705f4c16cc9f2d8397481 Mon Sep 17 00:00:00 2001 From: Brady Date: Fri, 31 Jul 2026 20:54:48 -0500 Subject: [PATCH 06/20] Finish source refactoring --- .../MemoryProtector.cpp => memory_protector/linux.cpp} | 4 ++-- .../mac/MemoryProtector.cpp => memory_protector/mac.cpp} | 0 .../MemoryProtector.cpp => memory_protector/win32.cpp} | 0 src/process/linux.cpp | 9 ++++++--- .../win32/Scanner.cpp => scanner/experimental/win32.cpp} | 0 src/{os/linux/Common.hpp => utils_linux.hpp} | 0 6 files changed, 8 insertions(+), 5 deletions(-) rename src/{os/linux/MemoryProtector.cpp => memory_protector/linux.cpp} (97%) rename src/{os/mac/MemoryProtector.cpp => memory_protector/mac.cpp} (100%) rename src/{os/win32/MemoryProtector.cpp => memory_protector/win32.cpp} (100%) rename src/{os/win32/Scanner.cpp => scanner/experimental/win32.cpp} (100%) rename src/{os/linux/Common.hpp => utils_linux.hpp} (100%) diff --git a/src/os/linux/MemoryProtector.cpp b/src/memory_protector/linux.cpp similarity index 97% rename from src/os/linux/MemoryProtector.cpp rename to src/memory_protector/linux.cpp index 0624d8e..f313b42 100644 --- a/src/os/linux/MemoryProtector.cpp +++ b/src/memory_protector/linux.cpp @@ -3,9 +3,9 @@ #include #include -#include "../../Utils.hpp" -#include "Common.hpp" +#include "../utils.hpp" +#include "../utils_linux.hpp" #include diff --git a/src/os/mac/MemoryProtector.cpp b/src/memory_protector/mac.cpp similarity index 100% rename from src/os/mac/MemoryProtector.cpp rename to src/memory_protector/mac.cpp diff --git a/src/os/win32/MemoryProtector.cpp b/src/memory_protector/win32.cpp similarity index 100% rename from src/os/win32/MemoryProtector.cpp rename to src/memory_protector/win32.cpp diff --git a/src/process/linux.cpp b/src/process/linux.cpp index 3d66472..131bc19 100644 --- a/src/process/linux.cpp +++ b/src/process/linux.cpp @@ -6,15 +6,18 @@ #include #include -#include +#include // strnlen +#include #include #include #include #include +#include +#include -#include "Common.hpp" -#include "../../Utils.hpp" +#include "../utils.hpp" +#include "../utils_linux.hpp" namespace { diff --git a/src/os/win32/Scanner.cpp b/src/scanner/experimental/win32.cpp similarity index 100% rename from src/os/win32/Scanner.cpp rename to src/scanner/experimental/win32.cpp diff --git a/src/os/linux/Common.hpp b/src/utils_linux.hpp similarity index 100% rename from src/os/linux/Common.hpp rename to src/utils_linux.hpp From 050937f07fe72fe5571793bf29877931bf8630f8 Mon Sep 17 00:00:00 2001 From: Brady Date: Fri, 31 Jul 2026 22:29:38 -0500 Subject: [PATCH 07/20] Actually done with the refactoring --- src/scanner/scanner.cpp | 79 ++++++++++++++++++++++++++++++++++ src/{Scanner.cpp => utils.cpp} | 76 -------------------------------- 2 files changed, 79 insertions(+), 76 deletions(-) create mode 100644 src/scanner/scanner.cpp rename src/{Scanner.cpp => utils.cpp} (55%) diff --git a/src/scanner/scanner.cpp b/src/scanner/scanner.cpp new file mode 100644 index 0000000..d12ba2c --- /dev/null +++ b/src/scanner/scanner.cpp @@ -0,0 +1,79 @@ +#include + +#include +#include + +namespace hat::detail { + + template<> + scan_context create_context(const scan_parameters& params) { + const auto& ext = get_system().extensions; +#if defined(LIBHAT_X86) || defined(LIBHAT_X86_64) + if (compiled_extensions.bmi || ext.bmi) { +#if defined(LIBHAT_X86_64) && defined(LIBHAT_FEATURE_AVX512) + if ((compiled_extensions.avx512f || ext.avx512f) + && (compiled_extensions.avx512bw || ext.avx512bw)) { + return create_context(params); + } +#endif + if (compiled_extensions.avx2 || ext.avx2) { + return create_context(params); + } + } +#if defined(LIBHAT_FEATURE_SSE) + if (compiled_extensions.sse41 || ext.sse41) { + return create_context(params); + } +#endif +#endif +#if defined(LIBHAT_ARM) || defined(LIBHAT_AARCH64) + if (compiled_extensions.neon || ext.neon) { + return create_context(params); + } +#endif + // If none of the vectorized implementations are available/supported, then fallback to scanning per-byte + return create_context(params); + } +} + +// Validate return value const-ness for the root find_pattern impl +namespace hat { + static_assert(std::is_same_v(), + std::declval(), + std::declval()))>); + + static_assert(std::is_same_v(), + std::declval(), + std::declval()))>); + + consteval auto count_matches() { + constexpr std::array a{std::byte{1}, std::byte{2}, std::byte{3}, std::byte{4}, std::byte{1}}; + constexpr hat::fixed_signature<1> s{std::byte{1}}; + + std::vector results{}; + hat::find_all_pattern(a.cbegin(), a.cend(), std::back_inserter(results), s); + return results.size(); + } + static_assert(count_matches() == 2); + + static_assert([] { + constexpr std::array a{std::byte{1}, std::byte{2}, std::byte{3}, std::byte{4}, std::byte{1}}; + constexpr hat::fixed_signature<1> s{std::byte{1}}; + + std::vector results{}; + hat::find_all_pattern(a.cbegin(), a.cend(), std::back_inserter(results), s); + + return results == hat::find_all_pattern(a.cbegin(), a.cend(), s); + }()); + + static_assert([] { + constexpr std::array a{std::byte{1}, std::byte{2}, std::byte{3}, std::byte{4}, std::byte{1}}; + constexpr hat::fixed_signature<1> s{std::byte{1}}; + + std::array results{}; + const auto [scan_end, results_end] = hat::find_all_pattern(a.cbegin(), a.cend(), results.begin(), results.end(), s); + return scan_end == a.cend() && results_end == std::next(results.begin(), 2); + }()); +} diff --git a/src/Scanner.cpp b/src/utils.cpp similarity index 55% rename from src/Scanner.cpp rename to src/utils.cpp index dd7ecc0..4d21a31 100644 --- a/src/Scanner.cpp +++ b/src/utils.cpp @@ -1,11 +1,7 @@ #include -#include -#include - #include #include -#include #include #include "utils.hpp" @@ -112,76 +108,4 @@ namespace hat::detail { return min; } - - template<> - scan_context create_context(const scan_parameters& params) { -// const auto& ext = get_system().extensions; -// #if defined(LIBHAT_X86) || defined(LIBHAT_X86_64) -// if (compiled_extensions.bmi || ext.bmi) { -// #if defined(LIBHAT_X86_64) && defined(LIBHAT_FEATURE_AVX512) -// if ((compiled_extensions.avx512f || ext.avx512f) -// && (compiled_extensions.avx512bw || ext.avx512bw)) { -// return create_context(params); -// } -// #endif -// if (compiled_extensions.avx2 || ext.avx2) { -// return create_context(params); -// } -// } -// #if defined(LIBHAT_FEATURE_SSE) -// if (compiled_extensions.sse41 || ext.sse41) { -// return create_context(params); -// } -// #endif -// #endif -#if defined(LIBHAT_ARM) || defined(LIBHAT_AARCH64) - if (compiled_extensions.neon || ext.neon) { - return create_context(params); - } -#endif - // If none of the vectorized implementations are available/supported, then fallback to scanning per-byte - return create_context(params); - } -} - -// Validate return value const-ness for the root find_pattern impl -namespace hat { - static_assert(std::is_same_v(), - std::declval(), - std::declval()))>); - - static_assert(std::is_same_v(), - std::declval(), - std::declval()))>); - - consteval auto count_matches() { - constexpr std::array a{std::byte{1}, std::byte{2}, std::byte{3}, std::byte{4}, std::byte{1}}; - constexpr hat::fixed_signature<1> s{std::byte{1}}; - - std::vector results{}; - hat::find_all_pattern(a.cbegin(), a.cend(), std::back_inserter(results), s); - return results.size(); - } - static_assert(count_matches() == 2); - - static_assert([] { - constexpr std::array a{std::byte{1}, std::byte{2}, std::byte{3}, std::byte{4}, std::byte{1}}; - constexpr hat::fixed_signature<1> s{std::byte{1}}; - - std::vector results{}; - hat::find_all_pattern(a.cbegin(), a.cend(), std::back_inserter(results), s); - - return results == hat::find_all_pattern(a.cbegin(), a.cend(), s); - }()); - - static_assert([] { - constexpr std::array a{std::byte{1}, std::byte{2}, std::byte{3}, std::byte{4}, std::byte{1}}; - constexpr hat::fixed_signature<1> s{std::byte{1}}; - - std::array results{}; - const auto [scan_end, results_end] = hat::find_all_pattern(a.cbegin(), a.cend(), results.begin(), results.end(), s); - return scan_end == a.cend() && results_end == std::next(results.begin(), 2); - }()); } From 1892a2272045d3fceaab9f8eb83e17f4d3297f1b Mon Sep 17 00:00:00 2001 From: Brady Date: Sat, 1 Aug 2026 13:09:31 -0500 Subject: [PATCH 08/20] Increase impl buffer size in `scan_context` --- include/libhat/scanner.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/libhat/scanner.hpp b/include/libhat/scanner.hpp index 462c456..a43ec3f 100644 --- a/include/libhat/scanner.hpp +++ b/include/libhat/scanner.hpp @@ -152,7 +152,7 @@ namespace hat::detail { }; class scan_context { - static constexpr size_t impl_buffer_size = 32; + static constexpr size_t impl_buffer_size = 48; static constexpr size_t impl_buffer_align = alignof(std::max_align_t); public: scan_context(const scan_context&) = delete; From 9c6de59cf546f4a5983333fe6433769fa3d89a6a Mon Sep 17 00:00:00 2001 From: Brady Date: Sat, 1 Aug 2026 13:40:56 -0500 Subject: [PATCH 09/20] Fix `get_optimal_byte` implementation --- src/utils.cpp | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/src/utils.cpp b/src/utils.cpp index 4d21a31..36f34a9 100644 --- a/src/utils.cpp +++ b/src/utils.cpp @@ -34,6 +34,18 @@ namespace hat::detail { return std::nullopt; } + static auto count_bytes(const signature_view signature) { + std::array counts{}; + for (const auto element : signature) { + const auto value = std::to_integer(element.value()); + auto& count = counts[value]; + if (element.all() && count < 0xFF) { + count++; + } + } + return counts; + } + std::optional get_optimal_pair(const scan_parameters& params) { const bool pair0 = static_cast(params.hints & scan_hint::pair0); @@ -85,27 +97,15 @@ namespace hat::detail { std::optional get_optimal_byte(const scan_parameters& params) { const auto signature = params.signature; + const auto counts = count_bytes(signature); - std::array counts{}; - for (const auto element : signature) { - const auto value = std::to_integer(element.value()); - auto& count = counts[value]; - if (element.all() && count < 0xFF) { - count++; - } - } + auto bytes = signature | std::views::filter(&signature_element::all); + const auto min = std::ranges::min_element(bytes, std::less{}, + [&](const auto element) { return counts[std::to_integer(*element)]; }).base(); - std::optional min{}; - uint8_t minCount = 0xFF; - for (std::size_t i = 0; i < signature.size(); i++) { - auto& element = signature[i]; - const auto count = std::to_integer(element.value()); - if (!min || count < minCount) { - min = i; - minCount = count; - } + if (min != signature.end()) { + return std::distance(signature.begin(), min); } - - return min; + return {}; } } From c015612de3c22d97631830105d98ce20d036a62c Mon Sep 17 00:00:00 2001 From: Brady Date: Sat, 1 Aug 2026 14:07:38 -0500 Subject: [PATCH 10/20] Select optimal pair based on signature-local byte occurrences --- src/utils.cpp | 49 ++++++++++++++++++++++++++++++++----------------- 1 file changed, 32 insertions(+), 17 deletions(-) diff --git a/src/utils.cpp b/src/utils.cpp index 36f34a9..a992862 100644 --- a/src/utils.cpp +++ b/src/utils.cpp @@ -35,9 +35,9 @@ namespace hat::detail { } static auto count_bytes(const signature_view signature) { - std::array counts{}; + std::array counts{}; for (const auto element : signature) { - const auto value = std::to_integer(element.value()); + const auto value = std::to_integer(element.value()); auto& count = counts[value]; if (element.all() && count < 0xFF) { count++; @@ -47,10 +47,17 @@ namespace hat::detail { } std::optional get_optimal_pair(const scan_parameters& params) { - const bool pair0 = static_cast(params.hints & scan_hint::pair0); + const auto sig = params.signature; - const auto pair_hint = get_pair_hint(params.hints); - if (pair_hint && !pair0) { + // Only utilize byte pair based scanning if the signature starts with a byte pair + if (static_cast(params.hints & scan_hint::pair0)) { + if (sig.size() >= 2 && sig[0].all() && sig[1].all()) { + return 0; + } + return {}; + } + + if (const auto pair_hint = get_pair_hint(params.hints)) { const auto getScore = [&](const std::byte a, const std::byte b) -> std::uint16_t { const auto& [pairs, scores] = *pair_hint; const std::pair pair{a, b}; @@ -60,8 +67,8 @@ namespace hat::detail { }; std::optional> best{}; - for (auto it = params.signature.begin(); it != std::prev(params.signature.end()); it++) { - const auto i = static_cast(it - params.signature.begin()); + for (auto it = sig.begin(); it != std::prev(sig.end()); it++) { + const auto i = static_cast(it - sig.begin()); auto& a = *it; auto& b = *std::next(it); @@ -78,21 +85,29 @@ namespace hat::detail { } } - // If no "optimal" pair was found, find the first byte pair in the signature - for (auto it = params.signature.begin(); it != std::prev(params.signature.end()); it++) { - const auto i = static_cast(it - params.signature.begin()); + // If no "optimal" pair was found based on hints, find the best one based on individual byte occurrences + const auto counts = count_bytes(sig); + std::uint16_t minScore{0xFFFF}; + std::optional minIndex{}; + for (auto it = sig.begin(); it != std::prev(sig.end()); it++) { auto& a = *it; auto& b = *std::next(it); - - if (a.all() && b.all()) { - return i; + if (!a.all() || !b.all()) { + continue; } - if (i == 0 && pair0) { - break; + + const auto score = static_cast(counts[std::to_integer(*a)] + + counts[std::to_integer(*b)]); + if (!minIndex || (score < minScore)) { + minScore = score; + minIndex = std::distance(sig.begin(), it); + if (score == 2) { + break; // minimum value + } } } - return {}; + return minIndex; } std::optional get_optimal_byte(const scan_parameters& params) { @@ -101,7 +116,7 @@ namespace hat::detail { auto bytes = signature | std::views::filter(&signature_element::all); const auto min = std::ranges::min_element(bytes, std::less{}, - [&](const auto element) { return counts[std::to_integer(*element)]; }).base(); + [&](const auto element) { return counts[std::to_integer(*element)]; }).base(); if (min != signature.end()) { return std::distance(signature.begin(), min); From d49d8957449267916c36b6dc709f1ca88eb8b34b Mon Sep 17 00:00:00 2001 From: Brady Date: Sat, 1 Aug 2026 18:13:00 -0500 Subject: [PATCH 11/20] Consistency --- src/utils.cpp | 35 ++++++++++++++++++----------------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/src/utils.cpp b/src/utils.cpp index a992862..9935f2c 100644 --- a/src/utils.cpp +++ b/src/utils.cpp @@ -68,15 +68,15 @@ namespace hat::detail { std::optional> best{}; for (auto it = sig.begin(); it != std::prev(sig.end()); it++) { - const auto i = static_cast(it - sig.begin()); - auto& a = *it; - auto& b = *std::next(it); - - if (a.all() && b.all()) { - const auto score = getScore(a.value(), b.value()); - if (!best || score > best->second) { - best.emplace(i, score); - } + auto a = *it; + auto b = *std::next(it); + if (!a.all() || !b.all()) { + continue; + } + + const auto score = getScore(a.value(), b.value()); + if (!best || score > best->second) { + best.emplace(std::distance(sig.begin(), it), score); } } @@ -87,27 +87,28 @@ namespace hat::detail { // If no "optimal" pair was found based on hints, find the best one based on individual byte occurrences const auto counts = count_bytes(sig); - std::uint16_t minScore{0xFFFF}; - std::optional minIndex{}; + std::optional> best{}; for (auto it = sig.begin(); it != std::prev(sig.end()); it++) { - auto& a = *it; - auto& b = *std::next(it); + auto a = *it; + auto b = *std::next(it); if (!a.all() || !b.all()) { continue; } const auto score = static_cast(counts[std::to_integer(*a)] + counts[std::to_integer(*b)]); - if (!minIndex || (score < minScore)) { - minScore = score; - minIndex = std::distance(sig.begin(), it); + if (!best || score < best->second) { + best.emplace(std::distance(sig.begin(), it), score); if (score == 2) { break; // minimum value } } } - return minIndex; + if (best) { + return best->first; + } + return {}; } std::optional get_optimal_byte(const scan_parameters& params) { From 2c0e8f7d8315cad67b1f8322723911f20a838cfa Mon Sep 17 00:00:00 2001 From: Brady Date: Sat, 1 Aug 2026 18:34:19 -0500 Subject: [PATCH 12/20] Fix ARM compile --- src/scanner/neon.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/scanner/neon.cpp b/src/scanner/neon.cpp index 219448a..fe2db2f 100644 --- a/src/scanner/neon.cpp +++ b/src/scanner/neon.cpp @@ -52,7 +52,7 @@ namespace hat::detail { template static const_scan_result find_pattern_neon(const std::byte* begin, const std::byte* end, const scan_context& context) { - const auto signature = context.signature; + const auto signature = context.signature(); const auto cmpIndex = context.get().cmpIndex; // 128 bit vector containing first signature byte repeated From a7f44e1b9870301f3bf27e0d430630b72f648ee8 Mon Sep 17 00:00:00 2001 From: Brady Date: Sat, 1 Aug 2026 22:31:27 -0500 Subject: [PATCH 13/20] Fallback to `Single` when no fully masked anchor is available --- src/scanner/simd.hpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/scanner/simd.hpp b/src/scanner/simd.hpp index 8f4c078..19243c9 100644 --- a/src/scanner/simd.hpp +++ b/src/scanner/simd.hpp @@ -16,6 +16,11 @@ namespace hat::detail { const bool cmpeq2 = cmpIndex.has_value(); if (!cmpIndex) cmpIndex = get_optimal_byte(params); + if (!cmpIndex) LIBHAT_UNLIKELY { + // Highly unlikely case (no elements with a 0xFF mask), but it is supported by the 'Single' implementation. + return create_context(params); + } + const auto resolve = [&](std::integral_constant) { if (cmpeq2 && veccmp) return impl.template operator()(); if (cmpeq2) return impl.template operator()(); From b00002faa244add178a5552e6e4e6d65e29f6088 Mon Sep 17 00:00:00 2001 From: Brady Date: Sat, 1 Aug 2026 22:45:20 -0500 Subject: [PATCH 14/20] Remove error for signature missing masked byte --- bindings/c/include/libhat/c/libhat.h | 1 - bindings/c/src/libhat.cpp | 12 +++--------- include/libhat/signature.hpp | 6 ------ 3 files changed, 3 insertions(+), 16 deletions(-) diff --git a/bindings/c/include/libhat/c/libhat.h b/bindings/c/include/libhat/c/libhat.h index 9ccb663..c3541ce 100644 --- a/bindings/c/include/libhat/c/libhat.h +++ b/bindings/c/include/libhat/c/libhat.h @@ -30,7 +30,6 @@ extern "C" { typedef enum libhat_status { libhat_success, // The operation was successful libhat_err_unknown, - libhat_err_sig_missing_masked_byte, libhat_err_sig_element_parse_error, libhat_err_sig_empty_signature, libhat_err_sig_expected_wildcard, diff --git a/bindings/c/src/libhat.cpp b/bindings/c/src/libhat.cpp index f38d853..585cd0d 100644 --- a/bindings/c/src/libhat.cpp +++ b/bindings/c/src/libhat.cpp @@ -95,7 +95,6 @@ LIBHAT_API const char* libhat_status_to_string(const libhat_status status) { switch (status) { STATUS_CASE(libhat_success); STATUS_CASE(libhat_err_unknown); - STATUS_CASE(libhat_err_sig_missing_masked_byte); STATUS_CASE(libhat_err_sig_element_parse_error); STATUS_CASE(libhat_err_sig_empty_signature); STATUS_CASE(libhat_err_sig_expected_wildcard); @@ -113,7 +112,6 @@ LIBHAT_API libhat_status libhat_parse_signature(const char* signatureStr, const *signatureOut = nullptr; switch (result.error()) { using enum hat::signature_error; - case missing_masked_byte: return libhat_err_sig_missing_masked_byte; case element_parse_error: return libhat_err_sig_element_parse_error; case empty_signature: return libhat_err_sig_empty_signature; case expected_wildcard: return libhat_err_sig_expected_wildcard; @@ -142,16 +140,12 @@ LIBHAT_API libhat_status libhat_create_signature( } hat::signature signature{}; - bool containsByte = false; - signature.reserve(size); + signature.resize(size); for (size_t i{}; i < size; i++) { - containsByte |= signature.emplace_back( + signature[i] = { static_cast(bytes[i]), static_cast(mask[i]) - ).all(); - } - if (!containsByte) { - return libhat_err_sig_missing_masked_byte; + }; } *signatureOut = new libhat_signature{std::move(signature)}; return libhat_success; diff --git a/include/libhat/signature.hpp b/include/libhat/signature.hpp index 4479f1f..13b3d8d 100644 --- a/include/libhat/signature.hpp +++ b/include/libhat/signature.hpp @@ -89,7 +89,6 @@ LIBHAT_EXPORT namespace hat { using fixed_signature = std::array; enum class signature_error { - missing_masked_byte, element_parse_error, empty_signature, expected_wildcard, @@ -171,7 +170,6 @@ LIBHAT_EXPORT namespace hat { [[nodiscard]] LIBHAT_CONSTEXPR_RESULT result parse_signature_to(std::output_iterator auto out, const std::string_view str) { std::size_t written = 0; - bool containsByte = false; for (auto&& sub : str | std::views::split(' ')) { const std::string_view word{sub.begin(), sub.end()}; @@ -194,7 +192,6 @@ LIBHAT_EXPORT namespace hat { if (element) { *out++ = *element; written++; - containsByte |= element->all(); } else { return result_error{signature_error::element_parse_error}; } @@ -208,9 +205,6 @@ LIBHAT_EXPORT namespace hat { if (written == 0) { return result_error{signature_error::empty_signature}; } - if (!containsByte) { - return result_error{signature_error::missing_masked_byte}; - } return written; } From 39bb6353acfbe5063add1f015be39c031e0335a8 Mon Sep 17 00:00:00 2001 From: Brady Date: Sat, 1 Aug 2026 22:53:54 -0500 Subject: [PATCH 15/20] Allow empty signatures to be valid --- bindings/c/include/libhat/c/libhat.h | 1 - bindings/c/src/libhat.cpp | 5 ----- include/libhat/scanner.hpp | 3 +++ include/libhat/signature.hpp | 19 ++++--------------- src/scanner/experimental/win32.cpp | 4 ++-- 5 files changed, 9 insertions(+), 23 deletions(-) diff --git a/bindings/c/include/libhat/c/libhat.h b/bindings/c/include/libhat/c/libhat.h index c3541ce..b6313d6 100644 --- a/bindings/c/include/libhat/c/libhat.h +++ b/bindings/c/include/libhat/c/libhat.h @@ -31,7 +31,6 @@ typedef enum libhat_status { libhat_success, // The operation was successful libhat_err_unknown, libhat_err_sig_element_parse_error, - libhat_err_sig_empty_signature, libhat_err_sig_expected_wildcard, libhat_err_sig_invalid_token_length, libhat_err_invalid_argument_value, diff --git a/bindings/c/src/libhat.cpp b/bindings/c/src/libhat.cpp index 585cd0d..fe02cd7 100644 --- a/bindings/c/src/libhat.cpp +++ b/bindings/c/src/libhat.cpp @@ -96,7 +96,6 @@ LIBHAT_API const char* libhat_status_to_string(const libhat_status status) { STATUS_CASE(libhat_success); STATUS_CASE(libhat_err_unknown); STATUS_CASE(libhat_err_sig_element_parse_error); - STATUS_CASE(libhat_err_sig_empty_signature); STATUS_CASE(libhat_err_sig_expected_wildcard); STATUS_CASE(libhat_err_sig_invalid_token_length); STATUS_CASE(libhat_err_invalid_argument_value); @@ -113,7 +112,6 @@ LIBHAT_API libhat_status libhat_parse_signature(const char* signatureStr, const switch (result.error()) { using enum hat::signature_error; case element_parse_error: return libhat_err_sig_element_parse_error; - case empty_signature: return libhat_err_sig_empty_signature; case expected_wildcard: return libhat_err_sig_expected_wildcard; case invalid_token_length: return libhat_err_sig_invalid_token_length; } @@ -135,9 +133,6 @@ LIBHAT_API libhat_status libhat_create_signature( if (size && (!bytes || !mask)) { return libhat_err_invalid_argument_value; } - if (!size) { - return libhat_err_sig_empty_signature; - } hat::signature signature{}; signature.resize(size); diff --git a/include/libhat/scanner.hpp b/include/libhat/scanner.hpp index a43ec3f..f033c32 100644 --- a/include/libhat/scanner.hpp +++ b/include/libhat/scanner.hpp @@ -178,6 +178,9 @@ namespace hat::detail { [[nodiscard]] const T& get() const; [[nodiscard]] constexpr const_scan_result scan(const std::byte* begin, const std::byte* end) const { + if (signature_.empty()) LIBHAT_UNLIKELY { + return begin; + } if (signature_.size() > static_cast(std::distance(begin, end))) LIBHAT_UNLIKELY { return {}; } diff --git a/include/libhat/signature.hpp b/include/libhat/signature.hpp index 13b3d8d..f78e45a 100644 --- a/include/libhat/signature.hpp +++ b/include/libhat/signature.hpp @@ -90,7 +90,6 @@ LIBHAT_EXPORT namespace hat { enum class signature_error { element_parse_error, - empty_signature, expected_wildcard, invalid_token_length, }; @@ -104,11 +103,8 @@ LIBHAT_EXPORT namespace hat { } /// Convert raw byte storage into a signature - [[nodiscard]] LIBHAT_CONSTEXPR_RESULT result bytes_to_signature(std::span bytes) { - if (bytes.empty()) { - return result_error{signature_error::empty_signature}; - } - return signature{bytes.begin(), bytes.end()}; + [[nodiscard]] constexpr signature bytes_to_signature(std::span bytes) { + return {bytes.begin(), bytes.end()}; } template @@ -123,11 +119,7 @@ LIBHAT_EXPORT namespace hat { } template - [[nodiscard]] LIBHAT_CONSTEXPR_RESULT result string_to_signature(std::basic_string_view str) { - if (str.empty()) { - return result_error{signature_error::empty_signature}; - } - + [[nodiscard]] constexpr signature string_to_signature(std::basic_string_view str) { signature result; result.resize(str.size() * sizeof(Char)); @@ -142,7 +134,7 @@ LIBHAT_EXPORT namespace hat { } template - [[nodiscard]] LIBHAT_CONSTEXPR_RESULT result string_to_signature(std::basic_string str) { + [[nodiscard]] constexpr signature string_to_signature(std::basic_string str) { return string_to_signature(std::basic_string_view{str}); } @@ -202,9 +194,6 @@ LIBHAT_EXPORT namespace hat { } } } - if (written == 0) { - return result_error{signature_error::empty_signature}; - } return written; } diff --git a/src/scanner/experimental/win32.cpp b/src/scanner/experimental/win32.cpp index 980208a..6d564a3 100644 --- a/src/scanner/experimental/win32.cpp +++ b/src/scanner/experimental/win32.cpp @@ -9,7 +9,7 @@ namespace hat::experimental { scan_result find_vtable(const std::string& className, const hat::process::module& mod) { // Tracing cross-references // Type Descriptor => Object Locator => VTable - auto sig = string_to_signature(".?AV" + className + "@@").value(); + auto sig = string_to_signature(".?AV" + className + "@@"); // TODO: Have a better solution for this // 3rd character may be 'V' for classes and 'U' for structs @@ -53,7 +53,7 @@ namespace hat::experimental { scan_result find_vtable(const std::string& className, const hat::process::module& mod) { // Tracing cross-references // Type Descriptor Name => Type Info => VTable - const auto sig = string_to_signature(std::to_string(className.size()) + className + "\0").value(); + const auto sig = string_to_signature(std::to_string(className.size()) + className + "\0"); const auto typeName = *find_pattern(sig, ".rdata", mod); if (!typeName) { return nullptr; From 287ea903999206a1679567832210f96a7a6409f7 Mon Sep 17 00:00:00 2001 From: Brady Date: Sat, 1 Aug 2026 22:59:02 -0500 Subject: [PATCH 16/20] Include `Search` mode in Scanner test --- test/tests/Scanner.cpp | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/test/tests/Scanner.cpp b/test/tests/Scanner.cpp index e226564..89d382b 100644 --- a/test/tests/Scanner.cpp +++ b/test/tests/Scanner.cpp @@ -97,7 +97,14 @@ using FindPatternTestTypes = ::testing::Types< FindPatternParameters, FindPatternParameters, FindPatternParameters, - FindPatternParameters + FindPatternParameters, + + FindPatternParameters, + FindPatternParameters, + FindPatternParameters, + FindPatternParameters, + FindPatternParameters, + FindPatternParameters >; class FindPatternTestNameGenerator { @@ -111,6 +118,7 @@ class FindPatternTestNameGenerator { template static consteval std::string_view getModeName() { if constexpr (Mode == hat::detail::scan_mode::Single) return "Single"; + else if constexpr (Mode == hat::detail::scan_mode::Search) return "Search"; else if constexpr (Mode == hat::detail::scan_mode::SSE) return "SSE"; else if constexpr (Mode == hat::detail::scan_mode::AVX2) return "AVX2"; else if constexpr (Mode == hat::detail::scan_mode::AVX512) return "AVX512"; From 442bb1fdb6ca017a1706d02977ca2161b93e2bf0 Mon Sep 17 00:00:00 2001 From: Brady Date: Sun, 2 Aug 2026 00:11:42 -0500 Subject: [PATCH 17/20] Clean up single impl helpers --- src/scanner/single.cpp | 47 +++++++++++++++++++++++------------------- 1 file changed, 26 insertions(+), 21 deletions(-) diff --git a/src/scanner/single.cpp b/src/scanner/single.cpp index e97f954..08406bf 100644 --- a/src/scanner/single.cpp +++ b/src/scanner/single.cpp @@ -16,7 +16,7 @@ namespace { } [[nodiscard]] bool matches(const std::byte* buffer) const { - for (size_t i = 1; i < literals_; i++) { + for (std::size_t i = 1; i < literals_; i++) { const auto [element, offset] = anchor(i); if (buffer[offset] != element) { std::swap(anchors_[i], anchors_[i - 1]); @@ -49,23 +49,20 @@ namespace { std::size_t literals_; }; - struct std_context { - mutable std::vector anchors{}; - std::size_t literals{}; - - explicit std_context(const hat::detail::scan_parameters& params) { - this->anchors.resize(static_cast( - std::ranges::count_if(params.signature, &hat::signature_element::any))); - auto it = this->anchors.begin(); + struct single_context { + explicit single_context(const hat::detail::scan_parameters& params) : + anchors_(static_cast(std::ranges::count_if(params.signature, &hat::signature_element::any))) + { + auto it = anchors_.begin(); // Add fully masked bytes - for (size_t i{}; auto e : params.signature) { + for (std::size_t i{}; auto e : params.signature) { if (e.all()) { *it++ = i; } i++; } - this->literals = static_cast(std::distance(this->anchors.begin(), it)); + literals_ = static_cast(std::distance(anchors_.begin(), it)); // Add partially masked bytes for (size_t i{}; auto e : params.signature) { @@ -75,12 +72,16 @@ namespace { i++; } } - }; - anchor_matcher create_anchor_matcher(const hat::detail::scan_context& context) { - const auto& std = context.get(); - return {context.signature(), std.anchors, std.literals}; - } + static anchor_matcher create_matcher(const hat::detail::scan_context& context) { + const auto& std = context.get(); + return {context.signature(), std.anchors_, std.literals_}; + } + + private: + mutable std::vector anchors_{}; + std::size_t literals_{}; + }; } namespace hat::detail { @@ -95,7 +96,7 @@ namespace hat::detail { return nullptr; } - const auto matcher = create_anchor_matcher(context); + const auto matcher = single_context::create_matcher(context); for (auto i = scanBegin; i != scanEnd; i += stride) { if (matcher.literals() > 0) { const auto [anchor, offset] = matcher.top(); @@ -120,7 +121,7 @@ namespace hat::detail { const auto signature = context.signature(); const auto scanEnd = end - signature.size() + 1; - const auto matcher = create_anchor_matcher(context); + const auto matcher = single_context::create_matcher(context); for (auto i = begin; i != scanEnd; i++) { // This check should get hoisted out by the optimizer if (matcher.literals() > 0) { @@ -149,10 +150,14 @@ namespace hat::detail { template<> scan_context create_context(const scan_parameters& params) { + const auto create = [&](std::integral_constant) { + return scan_context{params.signature, &find_pattern_single, std::type_identity{}, params}; + }; switch (params.alignment) { - case scan_alignment::X1: return {params.signature, &find_pattern_single, std::type_identity{}, params}; - case scan_alignment::X4: return {params.signature, &find_pattern_single, std::type_identity{}, params}; - case scan_alignment::X16: return {params.signature, &find_pattern_single, std::type_identity{}, params}; + using enum scan_alignment; + case X1: return create(std::integral_constant{}); + case X4: return create(std::integral_constant{}); + case X16: return create(std::integral_constant{}); } LIBHAT_UNREACHABLE(); } From b77279a26f1caee3aaa569e18951e12cd35f1de6 Mon Sep 17 00:00:00 2001 From: Brady Date: Sun, 2 Aug 2026 00:26:57 -0500 Subject: [PATCH 18/20] Save a massive 8 bytes --- src/scanner/single.cpp | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/src/scanner/single.cpp b/src/scanner/single.cpp index 08406bf..dfe01e6 100644 --- a/src/scanner/single.cpp +++ b/src/scanner/single.cpp @@ -50,10 +50,11 @@ namespace { }; struct single_context { - explicit single_context(const hat::detail::scan_parameters& params) : - anchors_(static_cast(std::ranges::count_if(params.signature, &hat::signature_element::any))) - { - auto it = anchors_.begin(); + + explicit single_context(const hat::detail::scan_parameters& params) { + total_ = static_cast(std::ranges::count_if(params.signature, &hat::signature_element::any)); + anchors_ = std::make_unique(total_); + auto it = anchors_.get(); // Add fully masked bytes for (std::size_t i{}; auto e : params.signature) { @@ -62,7 +63,7 @@ namespace { } i++; } - literals_ = static_cast(std::distance(anchors_.begin(), it)); + literals_ = static_cast(std::distance(anchors_.get(), it)); // Add partially masked bytes for (size_t i{}; auto e : params.signature) { @@ -75,11 +76,12 @@ namespace { static anchor_matcher create_matcher(const hat::detail::scan_context& context) { const auto& std = context.get(); - return {context.signature(), std.anchors_, std.literals_}; + return {context.signature(), {std.anchors_.get(), std.total_}, std.literals_}; } private: - mutable std::vector anchors_{}; + std::unique_ptr anchors_; + std::size_t total_{}; std::size_t literals_{}; }; } From b1a7c58b95029b0fe38b34755b172e207f1f5d8d Mon Sep 17 00:00:00 2001 From: Brady Date: Sun, 9 Aug 2026 13:45:44 -0500 Subject: [PATCH 19/20] Add `hat::detail::is_supported` and avoid undefined externals --- include/libhat/scanner.hpp | 2 + src/scanner/avx2.cpp | 11 +++- src/scanner/avx512.cpp | 11 +++- src/scanner/neon.cpp | 11 +++- src/scanner/scanner.cpp | 100 +++++++++++++++++++++++++++++-------- src/scanner/sse.cpp | 11 +++- test/tests/Scanner.cpp | 15 +++--- 7 files changed, 126 insertions(+), 35 deletions(-) diff --git a/include/libhat/scanner.hpp b/include/libhat/scanner.hpp index f033c32..fbc61e5 100644 --- a/include/libhat/scanner.hpp +++ b/include/libhat/scanner.hpp @@ -151,6 +151,8 @@ namespace hat::detail { Neon, // ARMv7+ Neon }; + bool is_supported(scan_mode mode); + class scan_context { static constexpr size_t impl_buffer_size = 48; static constexpr size_t impl_buffer_align = alignof(std::max_align_t); diff --git a/src/scanner/avx2.cpp b/src/scanner/avx2.cpp index c3c8796..a7b4e56 100644 --- a/src/scanner/avx2.cpp +++ b/src/scanner/avx2.cpp @@ -1,9 +1,8 @@ #include +#include #if defined(LIBHAT_X86) || defined(LIBHAT_X86_64) -#include - #include "simd.hpp" #include @@ -102,4 +101,12 @@ namespace hat::detail { }>(params, 32); } } +#else +namespace hat::detail { + + template<> + scan_context create_context(const scan_parameters&) { + LIBHAT_UNREACHABLE(); + } +} #endif diff --git a/src/scanner/avx512.cpp b/src/scanner/avx512.cpp index 47c4b75..c9fa2f9 100644 --- a/src/scanner/avx512.cpp +++ b/src/scanner/avx512.cpp @@ -1,9 +1,8 @@ #include +#include #if defined(LIBHAT_X86_64) && defined(LIBHAT_FEATURE_AVX512) -#include - #include "simd.hpp" #include @@ -99,4 +98,12 @@ namespace hat::detail { }>(params, 64); } } +#else +namespace hat::detail { + + template<> + scan_context create_context(const scan_parameters&) { + LIBHAT_UNREACHABLE(); + } +} #endif diff --git a/src/scanner/neon.cpp b/src/scanner/neon.cpp index fe2db2f..83ae6d6 100644 --- a/src/scanner/neon.cpp +++ b/src/scanner/neon.cpp @@ -1,9 +1,8 @@ #include +#include #if defined(LIBHAT_ARM) || defined(LIBHAT_AARCH64) -#include - #include "simd.hpp" #include @@ -127,4 +126,12 @@ namespace hat::detail { }>(params, 16); } } +#else +namespace hat::detail { + + template<> + scan_context create_context(const scan_parameters&) { + LIBHAT_UNREACHABLE(); + } +} #endif diff --git a/src/scanner/scanner.cpp b/src/scanner/scanner.cpp index d12ba2c..49b8144 100644 --- a/src/scanner/scanner.cpp +++ b/src/scanner/scanner.cpp @@ -3,34 +3,92 @@ #include #include -namespace hat::detail { +#define TEST_EXT(f) \ + [](const auto& ext) -> bool { \ + if constexpr (requires { ext.f; }) { \ + return ext.f; \ + } \ + return false; \ + } + +#define TRY_SCAN_MODE(mode) \ + do { \ + if constexpr (::is_defined::value) \ + if (is_supported(scan_mode::mode)) \ + return create_context(params); \ + } while (false) + +namespace { + + template + struct is_defined : std::false_type {}; template<> - scan_context create_context(const scan_parameters& params) { - const auto& ext = get_system().extensions; -#if defined(LIBHAT_X86) || defined(LIBHAT_X86_64) - if (compiled_extensions.bmi || ext.bmi) { -#if defined(LIBHAT_X86_64) && defined(LIBHAT_FEATURE_AVX512) - if ((compiled_extensions.avx512f || ext.avx512f) - && (compiled_extensions.avx512bw || ext.avx512bw)) { - return create_context(params); - } + struct is_defined : std::true_type {}; + + template<> + struct is_defined : std::true_type {}; + + template<> + struct is_defined : std::true_type {}; + +#if (defined(LIBHAT_X86) || defined(LIBHAT_X86_64)) && defined(LIBHAT_FEATURE_SSE) + template<> + struct is_defined : std::true_type {}; #endif - if (compiled_extensions.avx2 || ext.avx2) { - return create_context(params); - } - } -#if defined(LIBHAT_FEATURE_SSE) - if (compiled_extensions.sse41 || ext.sse41) { - return create_context(params); - } + +#if defined(LIBHAT_X86) || defined(LIBHAT_X86_64) + template<> + struct is_defined : std::true_type {}; #endif + +#if defined(LIBHAT_X86_64) && defined(LIBHAT_FEATURE_AVX512) + template<> + struct is_defined : std::true_type {}; #endif + #if defined(LIBHAT_ARM) || defined(LIBHAT_AARCH64) - if (compiled_extensions.neon || ext.neon) { - return create_context(params); - } + template<> + struct is_defined : std::true_type {}; #endif + + template + bool all_of(std::predicate auto&&... tests) { + if constexpr (is_defined::value) { + const auto& ext = hat::get_system().extensions; + return ((tests(hat::compiled_extensions) || tests(ext)) && ...); + } + return false; + } +} + +namespace hat::detail { + + bool is_supported(const scan_mode mode) { + switch (mode) { + case scan_mode::Auto: + case scan_mode::Search: + case scan_mode::Single: + return true; + case scan_mode::SSE: + return all_of(TEST_EXT(sse41)); + case scan_mode::AVX2: + return all_of(TEST_EXT(bmi), TEST_EXT(avx2)); + case scan_mode::AVX512: + return all_of(TEST_EXT(bmi), TEST_EXT(avx512f), TEST_EXT(avx512bw)); + case scan_mode::Neon: + return all_of(TEST_EXT(neon)); + } + LIBHAT_UNREACHABLE(); + } + + template<> + scan_context create_context(const scan_parameters& params) { + TRY_SCAN_MODE(AVX512); + TRY_SCAN_MODE(AVX2); + TRY_SCAN_MODE(SSE); + TRY_SCAN_MODE(Neon); + // If none of the vectorized implementations are available/supported, then fallback to scanning per-byte return create_context(params); } diff --git a/src/scanner/sse.cpp b/src/scanner/sse.cpp index 88843e6..0c525c1 100644 --- a/src/scanner/sse.cpp +++ b/src/scanner/sse.cpp @@ -1,9 +1,8 @@ #include +#include #if (defined(LIBHAT_X86) || defined(LIBHAT_X86_64)) && defined(LIBHAT_FEATURE_SSE) -#include - #include "simd.hpp" #include @@ -112,4 +111,12 @@ namespace hat::detail { }>(params, 16); } } +#else +namespace hat::detail { + + template<> + scan_context create_context(const scan_parameters&) { + LIBHAT_UNREACHABLE(); + } +} #endif diff --git a/test/tests/Scanner.cpp b/test/tests/Scanner.cpp index 89d382b..0cecd3b 100644 --- a/test/tests/Scanner.cpp +++ b/test/tests/Scanner.cpp @@ -24,6 +24,12 @@ concept FindPatternTestCallback = std::invocable class FindPatternTest> : public ::testing::Test { protected: + void SetUp() override { + if (!hat::detail::is_supported(Mode)) { + GTEST_SKIP() << "Mode is not natively supported by hardware or OS"; + } + } + void run_cases( const hat::scan_alignment alignment, FindPatternTestCallback auto&& callback @@ -61,7 +67,6 @@ class FindPatternTest> }; using FindPatternTestTypes = ::testing::Types< -#if defined(LIBHAT_X86_64) || defined(LIBHAT_X86) FindPatternParameters, FindPatternParameters, FindPatternParameters, @@ -75,23 +80,21 @@ using FindPatternTestTypes = ::testing::Types< FindPatternParameters, FindPatternParameters, FindPatternParameters, -#endif -#ifdef LIBHAT_X86_64 + FindPatternParameters, FindPatternParameters, FindPatternParameters, FindPatternParameters, FindPatternParameters, FindPatternParameters, -#endif -#if defined(LIBHAT_ARM) || defined(LIBHAT_AARCH64) + FindPatternParameters, FindPatternParameters, FindPatternParameters, FindPatternParameters, FindPatternParameters, FindPatternParameters, -#endif + FindPatternParameters, FindPatternParameters, FindPatternParameters, From 634e32e41fe58c5e61eced4b91bed911755a37cb Mon Sep 17 00:00:00 2001 From: Brady Date: Sun, 9 Aug 2026 13:46:24 -0500 Subject: [PATCH 20/20] Add const/pure attribute macros --- include/libhat/defines.hpp | 8 ++++++++ include/libhat/system.hpp | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/include/libhat/defines.hpp b/include/libhat/defines.hpp index 9e3fc6f..05ea738 100644 --- a/include/libhat/defines.hpp +++ b/include/libhat/defines.hpp @@ -82,6 +82,14 @@ #define LIBHAT_TARGET(arch) #endif +#if defined(__GNUC__) || defined(__clang__) + #define LIBHAT_PURE __attribute__((pure)) + #define LIBHAT_CONST __attribute__((const)) +#else + #define LIBHAT_PURE + #define LIBHAT_CONST +#endif + #if __has_cpp_attribute(no_unique_address) #define LIBHAT_NO_UNIQUE_ADDRESS [[no_unique_address]] #elif __has_cpp_attribute(msvc::no_unique_address) diff --git a/include/libhat/system.hpp b/include/libhat/system.hpp index b8e64b9..2d6664e 100644 --- a/include/libhat/system.hpp +++ b/include/libhat/system.hpp @@ -142,5 +142,5 @@ LIBHAT_EXPORT namespace hat { LIBHAT_EXPORT namespace hat { - const system_info_impl& get_system(); + LIBHAT_PURE const system_info_impl& get_system(); }