aboutsummaryrefslogtreecommitdiffhomepage
path: root/crates
diff options
context:
space:
mode:
authornsfisis <nsfisis@gmail.com>2026-08-16 16:24:38 +0900
committernsfisis <nsfisis@gmail.com>2026-08-16 17:34:46 +0900
commit224d3e8262ed6156ea3374fe91dfb5b66d6f0d3a (patch)
tree5e21148a1192f9f24ebbd2b007f2b9a8a8f8cc37 /crates
parent494f79d0caf614325408d82d5c7a58f9e4396590 (diff)
downloadphp-shirabe-224d3e8262ed6156ea3374fe91dfb5b66d6f0d3a.tar.gz
php-shirabe-224d3e8262ed6156ea3374fe91dfb5b66d6f0d3a.tar.zst
php-shirabe-224d3e8262ed6156ea3374fe91dfb5b66d6f0d3a.zip
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) <noreply@anthropic.com>
Diffstat (limited to 'crates')
-rw-r--r--crates/shirabe-php-shim/Cargo.toml1
-rw-r--r--crates/shirabe-php-shim/src/phar.rs25
2 files changed, 17 insertions, 9 deletions
diff --git a/crates/shirabe-php-shim/Cargo.toml b/crates/shirabe-php-shim/Cargo.toml
index cad9ad47..48784716 100644
--- a/crates/shirabe-php-shim/Cargo.toml
+++ b/crates/shirabe-php-shim/Cargo.toml
@@ -16,6 +16,7 @@ fastrand.workspace = true
flate2.workspace = true
indexmap.workspace = true
md5.workspace = true
+memchr.workspace = true
nix.workspace = true
regex.workspace = true
regex-macro.workspace = true
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<usize> {
+ // 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<Vec<PharEntry>> {
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) {