aboutsummaryrefslogtreecommitdiffhomepage
path: root/crates/shirabe/src/package/version
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/package/version
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/package/version')
-rw-r--r--crates/shirabe/src/package/version/version_bumper.rs8
-rw-r--r--crates/shirabe/src/package/version/version_guesser.rs37
-rw-r--r--crates/shirabe/src/package/version/version_parser.rs12
-rw-r--r--crates/shirabe/src/package/version/version_selector.rs11
4 files changed, 48 insertions, 20 deletions
diff --git a/crates/shirabe/src/package/version/version_bumper.rs b/crates/shirabe/src/package/version/version_bumper.rs
index 4f2d3242..e1f58942 100644
--- a/crates/shirabe/src/package/version/version_bumper.rs
+++ b/crates/shirabe/src/package/version/version_bumper.rs
@@ -7,6 +7,7 @@ use crate::package::version::VersionParser;
use crate::util::Platform;
use indexmap::IndexMap;
use shirabe_external_packages::composer::pcre::{CaptureKey, Preg};
+use shirabe_php_shim::php_regex;
use shirabe_semver::Intervals;
use shirabe_semver::constraint::AnyConstraint;
@@ -45,11 +46,12 @@ impl VersionBumper {
return Ok(pretty_constraint);
}
- let major = Preg::replace(r"{^([1-9][0-9]*|0\.\d+).*}", "$1", &version);
- let version_without_suffix = Preg::replace(r"{(?:\.(?:0|9999999))+(-dev)?$}", "", &version);
+ let major = Preg::replace(php_regex!(r"{^([1-9][0-9]*|0\.\d+).*}"), "$1", &version);
+ let version_without_suffix =
+ Preg::replace(php_regex!(r"{(?:\.(?:0|9999999))+(-dev)?$}"), "", &version);
let new_pretty_constraint = format!("^{}", version_without_suffix);
- if !Preg::is_match(r"{^\^\d+(\.\d+)*$}", &new_pretty_constraint) {
+ if !Preg::is_match(php_regex!(r"{^\^\d+(\.\d+)*$}"), &new_pretty_constraint) {
return Ok(pretty_constraint);
}
diff --git a/crates/shirabe/src/package/version/version_guesser.rs b/crates/shirabe/src/package/version/version_guesser.rs
index 249646c4..add59ead 100644
--- a/crates/shirabe/src/package/version/version_guesser.rs
+++ b/crates/shirabe/src/package/version/version_guesser.rs
@@ -15,7 +15,7 @@ use indexmap::IndexMap;
use shirabe_external_packages::composer::pcre::{CaptureKey, Preg};
use shirabe_php_shim::{
PHP_INT_MAX, PhpMixed, RuntimeException, array_keys, array_map, array_merge, empty,
- function_exists, implode, is_string, json_encode, preg_quote, str_replace, strlen,
+ function_exists, implode, is_string, json_encode, php_regex, preg_quote, str_replace, strlen,
strnatcasecmp, strpos, substr, trim, usort,
};
@@ -163,10 +163,13 @@ impl VersionGuesser {
}
if "-dev" == substr(version_data.version.as_deref().unwrap_or(""), -4, None)
- && Preg::is_match(r"{\.9{7}}", version_data.version.as_deref().unwrap_or(""))
+ && Preg::is_match(
+ php_regex!(r"{\.9{7}}"),
+ version_data.version.as_deref().unwrap_or(""),
+ )
{
version_data.pretty_version = Some(Preg::replace(
- r"{(\.9{7})+}",
+ php_regex!(r"{(\.9{7})+}"),
".x",
version_data.version.as_deref().unwrap_or(""),
));
@@ -185,12 +188,12 @@ impl VersionGuesser {
None,
)
&& Preg::is_match(
- r"{\.9{7}}",
+ php_regex!(r"{\.9{7}}"),
version_data.feature_version.as_deref().unwrap_or(""),
)
{
version_data.feature_pretty_version = Some(Preg::replace(
- r"{(\.9{7})+}",
+ php_regex!(r"{(\.9{7})+}"),
".x",
version_data.feature_version.as_deref().unwrap_or(""),
));
@@ -237,7 +240,9 @@ impl VersionGuesser {
if !branch.is_empty() {
let mut m: IndexMap<CaptureKey, String> = IndexMap::new();
if Preg::is_match3(
- r"{^(?:\* ) *(\(no branch\)|\(detached from \S+\)|\(HEAD detached at \S+\)|\S+) *([a-f0-9]+) .*$}",
+ php_regex!(
+ r"{^(?:\* ) *(\(no branch\)|\(detached from \S+\)|\(HEAD detached at \S+\)|\S+) *([a-f0-9]+) .*$}"
+ ),
&branch,
Some(&mut m),
) {
@@ -263,11 +268,13 @@ impl VersionGuesser {
if !branch.is_empty() && {
let mut tmp: IndexMap<CaptureKey, String> = IndexMap::new();
- !Preg::is_match3(r"{^ *.+/HEAD }", &branch, Some(&mut tmp))
+ !Preg::is_match3(php_regex!(r"{^ *.+/HEAD }"), &branch, Some(&mut tmp))
} {
let mut m: IndexMap<CaptureKey, String> = IndexMap::new();
if Preg::is_match3(
- r"{^(?:\* )? *((?:remotes/(?:origin|upstream)/)?[^\s/]+) *([a-f0-9]+) .*$}",
+ php_regex!(
+ r"{^(?:\* )? *((?:remotes/(?:origin|upstream)/)?[^\s/]+) *([a-f0-9]+) .*$}"
+ ),
&branch,
Some(&mut m),
) {
@@ -516,7 +523,8 @@ impl VersionGuesser {
)
.is_some();
if !has_branch_alias || has_self_version {
- let branch = Preg::replace(r"{^dev-}", "", version.as_deref().unwrap_or(""));
+ let branch =
+ Preg::replace(php_regex!(r"{^dev-}"), "", version.as_deref().unwrap_or(""));
let mut length: i64 = PHP_INT_MAX;
// return directly, if branch is configured to be non-feature branch
@@ -549,7 +557,8 @@ impl VersionGuesser {
let mut last_index: i64 = -1;
for (index, candidate) in branches.iter().enumerate() {
let index = index as i64;
- let candidate_version = Preg::replace(r"{^remotes/\S+/}", "", candidate);
+ let candidate_version =
+ Preg::replace(php_regex!(r"{^remotes/\S+/}"), "", candidate);
// do not compare against itself or other feature branches
if candidate == &branch
@@ -625,7 +634,7 @@ impl VersionGuesser {
}
!Preg::is_match(
- &format!(
+ format!(
r"{{^({}|master|main|latest|next|current|support|tip|trunk|default|develop|\d+\..+)$}}",
non_feature_branches,
),
@@ -772,7 +781,11 @@ impl VersionGuesser {
}
};
let mut m: IndexMap<CaptureKey, String> = IndexMap::new();
- if Preg::is_match3(r"{^(\d+(?:\.\d+)*)-dev$}i", &version, Some(&mut m)) {
+ if Preg::is_match3(
+ php_regex!(r"{^(\d+(?:\.\d+)*)-dev$}i"),
+ &version,
+ Some(&mut m),
+ ) {
return Ok(format!(
"{}.x-dev",
m.get(&CaptureKey::ByIndex(1)).cloned().unwrap_or_default()
diff --git a/crates/shirabe/src/package/version/version_parser.rs b/crates/shirabe/src/package/version/version_parser.rs
index 3527722c..8cf3aa72 100644
--- a/crates/shirabe/src/package/version/version_parser.rs
+++ b/crates/shirabe/src/package/version/version_parser.rs
@@ -3,6 +3,7 @@
use crate::repository::PlatformRepository;
use indexmap::IndexMap;
use shirabe_external_packages::composer::pcre::Preg;
+use shirabe_php_shim::php_regex;
use shirabe_semver::Semver;
use shirabe_semver::VersionParser as SemverVersionParser;
use shirabe_semver::constraint::AnyConstraint;
@@ -49,11 +50,18 @@ impl VersionParser {
let count = pairs.len();
let mut i = 0_usize;
while i < count {
- let mut pair = Preg::replace(r"{^([^=: ]+)[=: ](.*)$}", "$1 $2", pairs[i].trim());
+ let mut pair = Preg::replace(
+ php_regex!(r"{^([^=: ]+)[=: ](.*)$}"),
+ "$1 $2",
+ pairs[i].trim(),
+ );
if !pair.contains(' ')
&& i + 1 < count
&& !pairs[i + 1].contains('/')
- && !Preg::is_match(r"{(?<=[a-z0-9_/-])\*|\*(?=[a-z0-9_/-])}i", &pairs[i + 1])
+ && !Preg::is_match(
+ php_regex!(r"{(?<=[a-z0-9_/-])\*|\*(?=[a-z0-9_/-])}i"),
+ &pairs[i + 1],
+ )
&& !PlatformRepository::is_platform_package(&pairs[i + 1])
{
pair += &format!(" {}", pairs[i + 1]);
diff --git a/crates/shirabe/src/package/version/version_selector.rs b/crates/shirabe/src/package/version/version_selector.rs
index 48b045eb..ff5e7586 100644
--- a/crates/shirabe/src/package/version/version_selector.rs
+++ b/crates/shirabe/src/package/version/version_selector.rs
@@ -18,7 +18,8 @@ use crate::repository::RepositorySetInterface;
use indexmap::IndexMap;
use shirabe_external_packages::composer::pcre::Preg;
use shirabe_php_shim::{
- PHP_MAJOR_VERSION, PHP_MINOR_VERSION, PHP_RELEASE_VERSION, strtolower, version_compare,
+ PHP_MAJOR_VERSION, PHP_MINOR_VERSION, PHP_RELEASE_VERSION, php_regex, strtolower,
+ version_compare,
};
use shirabe_semver::constraint::AnyConstraint;
use shirabe_semver::constraint::SimpleConstraint;
@@ -280,7 +281,11 @@ impl VersionSelector {
if let Some(extra) = extra
&& extra != VersionParser::DEFAULT_BRANCH_ALIAS
{
- let new_extra = Preg::replace(r"{^(\d+\.\d+\.\d+)(\.9999999)-dev$}", "$1.0", &extra);
+ let new_extra = Preg::replace(
+ php_regex!(r"{^(\d+\.\d+\.\d+)(\.9999999)-dev$}"),
+ "$1.0",
+ &extra,
+ );
if new_extra != extra {
let new_extra = new_extra.replace(".9999999", ".0");
return self.transform_version(&new_extra, &new_extra, "dev");
@@ -299,7 +304,7 @@ impl VersionSelector {
let semantic_version_parts: Vec<&str> = version.split('.').collect();
if semantic_version_parts.len() == 4
- && Preg::is_match(r"{^\d+\D?}", semantic_version_parts[3])
+ && Preg::is_match(php_regex!(r"{^\d+\D?}"), semantic_version_parts[3])
{
let mut parts: Vec<String> = semantic_version_parts
.iter()