From 224d3e8262ed6156ea3374fe91dfb5b66d6f0d3a Mon Sep 17 00:00:00 2001 From: nsfisis Date: Sun, 16 Aug 2026 16:24:38 +0900 Subject: perf(php-shim): scan the phar stub token with memchr The stub token search compared every 18-byte window of the file case insensitively. The token starts with a byte that ASCII case folding leaves alone, so memchr can pick out the candidate offsets and leave only those to the case-insensitive compare. Over the 35 MB executable, finding the token in the embedded bundle stub drops from 2.3 ms to 0.1 ms, and a scan that finds nothing drops from 24 ms to 3.6 ms. Co-Authored-By: Claude Opus 5 (1M context) --- crates/shirabe-php-shim/src/phar.rs | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) (limited to 'crates/shirabe-php-shim/src') diff --git a/crates/shirabe-php-shim/src/phar.rs b/crates/shirabe-php-shim/src/phar.rs index 574cf69a..40e19131 100644 --- a/crates/shirabe-php-shim/src/phar.rs +++ b/crates/shirabe-php-shim/src/phar.rs @@ -315,20 +315,27 @@ fn halt_compiler_token() -> [u8; 18] { token } +/// Scans the stub for `token`, which phar matches in any letter case. +fn find_halt_compiler_token(blob: &[u8], token: &[u8]) -> Option { + // The token starts with a byte that ASCII case folding leaves alone, so scanning for it first + // narrows the windows that need the full case-insensitive compare. + memchr::memchr_iter(token[0], blob).find(|&start| { + blob.get(start..start + token.len()) + .is_some_and(|slice| slice.eq_ignore_ascii_case(token)) + }) +} + fn parse_native_phar(path: &std::path::Path) -> anyhow::Result> { let bytes = std::fs::read(path) .map_err(|e| corruption_error(path, &format!("unable to open phar: {}", e)))?; let halt = halt_compiler_token(); - let halt_pos = bytes - .windows(halt.len()) - .position(|window| window.eq_ignore_ascii_case(&halt)) - .ok_or_else(|| { - corruption_error( - path, - &format!("{} not found in stub", String::from_utf8_lossy(&halt)), - ) - })?; + let halt_pos = find_halt_compiler_token(&bytes, &halt).ok_or_else(|| { + corruption_error( + path, + &format!("{} not found in stub", String::from_utf8_lossy(&halt)), + ) + })?; let mut offset = halt_pos + halt.len(); for close_tag in [&b" ?>"[..], &b"\n?>"[..]] { if bytes[offset..].starts_with(close_tag) { -- cgit v1.3.1-4-g156e