aboutsummaryrefslogtreecommitdiffhomepage
path: root/crates/shirabe/src/autoload
diff options
context:
space:
mode:
authornsfisis <nsfisis@gmail.com>2026-07-18 15:03:55 +0900
committernsfisis <nsfisis@gmail.com>2026-07-18 15:54:27 +0900
commit91692846909ed191addb7ec1c34aad11392ab88b (patch)
tree7c477055e432fd43a98e5dddc016e07dcfc67f60 /crates/shirabe/src/autoload
parent4ae58baf8618f5fe916ba2a69faaca93514134ce (diff)
downloadphp-shirabe-91692846909ed191addb7ec1c34aad11392ab88b.tar.gz
php-shirabe-91692846909ed191addb7ec1c34aad11392ab88b.tar.zst
php-shirabe-91692846909ed191addb7ec1c34aad11392ab88b.zip
perf(regex): eliminate per-call clone overhead in preg_* dispatch
regex::Regex::clone() does not share the underlying meta engine's search-cache pool, so every fresh clone pays a ~10us warmup cost on its first use. Two changes together eliminate this across nearly all preg_* call sites: - A php_regex! macro resolves PHP-style patterns to a per-call-site &'static regex::Regex (via regex-macro's LazyLock), applied at the majority of call sites throughout the codebase. - Call sites still passing dynamic pattern strings go through PATTERN_CACHE, which now stores Arc<(Regex, bool)> and hands out Arc::clone()s instead of cloning the Regex itself. PregPattern::resolve() returns a ResolvedPattern enum (Arc or 'static reference) rather than an owned Regex, so neither path ever clones the Regex proper. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Diffstat (limited to 'crates/shirabe/src/autoload')
-rw-r--r--crates/shirabe/src/autoload/autoload_generator.rs36
1 files changed, 24 insertions, 12 deletions
diff --git a/crates/shirabe/src/autoload/autoload_generator.rs b/crates/shirabe/src/autoload/autoload_generator.rs
index be56ff8a..49eade41 100644
--- a/crates/shirabe/src/autoload/autoload_generator.rs
+++ b/crates/shirabe/src/autoload/autoload_generator.rs
@@ -27,7 +27,7 @@ use shirabe_external_packages::symfony::console::formatter::OutputFormatter;
use shirabe_php_shim::{
InvalidArgumentException, PhpMixed, array_keys, array_map, array_merge_map,
array_merge_recursive, array_shift, array_slice_strs, array_unique, bin2hex, explode,
- file_exists, file_get_contents, hash, implode, is_array, ksort, ltrim, preg_quote,
+ file_exists, file_get_contents, hash, implode, is_array, ksort, ltrim, php_regex, preg_quote,
random_bytes, realpath, str_contains, str_replace, str_starts_with, strlen, strpos, strtr,
substr, substr_count, trim, unlink, var_export,
};
@@ -514,7 +514,7 @@ impl AutoloadGenerator {
file_get_contents(format!("{}/autoload.php", vendor_path)).unwrap_or_default();
let mut matches: IndexMap<CaptureKey, String> = IndexMap::new();
if Preg::match3(
- "{ComposerAutoloaderInit([^:\\s]+)::}",
+ php_regex!("{ComposerAutoloaderInit([^:\\s]+)::}"),
&content,
Some(&mut matches),
) {
@@ -704,7 +704,9 @@ impl AutoloadGenerator {
for pattern in &excluded {
// extract the constant string prefix of the pattern here, until we reach a non-escaped regex special character
let pattern_processed = Preg::replace(
- "{^(([^.+*?\\[^\\]$(){}=!<>|:\\\\#-]+|\\\\[.+*?\\[^\\]$(){}=!<>|:#-])*).*}",
+ php_regex!(
+ "{^(([^.+*?\\[^\\]$(){}=!<>|:\\\\#-]+|\\\\[.+*?\\[^\\]$(){}=!<>|:#-])*).*}"
+ ),
"$1",
pattern,
);
@@ -1072,7 +1074,7 @@ impl AutoloadGenerator {
}
}
- if Preg::is_match("{\\.phar([\\\\/]|$)}", &path) {
+ if Preg::is_match(php_regex!("{\\.phar([\\\\/]|$)}"), &path) {
base_dir = format!("'phar://' . {}", base_dir);
}
@@ -1098,8 +1100,11 @@ impl AutoloadGenerator {
let links = array_merge_map(package.get_replaces(), package.get_provides());
for (_k, link) in &links {
let mut matches: IndexMap<CaptureKey, String> = IndexMap::new();
- if Preg::match3("{^ext-(.+)$}iD", link.get_target(), Some(&mut matches))
- && let Some(ext) = matches.get(&CaptureKey::ByIndex(1)).cloned()
+ if Preg::match3(
+ php_regex!("{^ext-(.+)$}iD"),
+ link.get_target(),
+ Some(&mut matches),
+ ) && let Some(ext) = matches.get(&CaptureKey::ByIndex(1)).cloned()
{
extension_providers
.entry(ext)
@@ -1141,7 +1146,11 @@ impl AutoloadGenerator {
let mut matches: IndexMap<CaptureKey, String> = IndexMap::new();
if check_platform.as_bool() == Some(true)
- && Preg::match3("{^ext-(.+)$}iD", link.get_target(), Some(&mut matches))
+ && Preg::match3(
+ php_regex!("{^ext-(.+)$}iD"),
+ link.get_target(),
+ Some(&mut matches),
+ )
{
let ext_key = matches
.get(&CaptureKey::ByIndex(1))
@@ -1662,8 +1671,11 @@ class ComposerStaticInit{}
);
m
});
- let value = shirabe_php_shim::ltrim(&Preg::replace("/^ */m", " $0$0", &value), None);
- let value = Preg::replace("/ +$/m", "", &value);
+ let value = shirabe_php_shim::ltrim(
+ &Preg::replace(php_regex!("/^ */m"), " $0$0", &value),
+ None,
+ );
+ let value = Preg::replace(php_regex!("/ +$/m"), "", &value);
file.push_str(&format!(
" public static ${} = {};\n\n",
@@ -1785,7 +1797,7 @@ class ComposerStaticInit{}
);
path_str = ltrim(
&Preg::replace(
- &format!("{{^{}}}", target_dir),
+ format!("{{^{}}}", target_dir),
"",
&ltrim(&path_str, Some("\\/")),
),
@@ -1804,7 +1816,7 @@ class ComposerStaticInit{}
if r#type == "exclude-from-classmap" {
// first escape user input
let p = Preg::replace(
- "{/+}",
+ php_regex!("{/+}"),
"/",
&preg_quote(&trim(&strtr(&path_str, "\\", "/"), Some("/")), None),
);
@@ -1821,7 +1833,7 @@ class ComposerStaticInit{}
let updir_cell: std::cell::RefCell<Option<String>> =
std::cell::RefCell::new(None);
let p = Preg::replace_callback(
- "{^((?:(?:\\\\\\.){1,2}+/)+)}",
+ php_regex!("{^((?:(?:\\\\\\.){1,2}+/)+)}"),
|matches: &IndexMap<CaptureKey, String>| -> String {
// undo preg_quote for the matched string
*updir_cell.borrow_mut() = Some(str_replace(