diff options
| author | nsfisis <nsfisis@gmail.com> | 2026-07-20 22:57:01 +0900 |
|---|---|---|
| committer | nsfisis <nsfisis@gmail.com> | 2026-07-20 22:57:01 +0900 |
| commit | 2862d2da275a672f22485f5d1db3550248b20251 (patch) | |
| tree | 5f0664c615f67c097b050ab59fc5a64f1f5b526e /crates/shirabe/src/repository | |
| parent | 77ff62b1e5333cd05b6c405feb2f42d0d8dcfab8 (diff) | |
| download | php-shirabe-2862d2da275a672f22485f5d1db3550248b20251.tar.gz php-shirabe-2862d2da275a672f22485f5d1db3550248b20251.tar.zst php-shirabe-2862d2da275a672f22485f5d1db3550248b20251.zip | |
perf(composer-repository): avoid redundant clones when loading async package metadata
load_async_packages cloned each response's version metadata up to
three times (spec_list/response, response_arr, versions_mixed, then
every per-version field) before building the versions Vec. Move
ownership through pattern matches and IndexMap::shift_remove instead.
Measured with laravel/laravel create-project: per-batch response
processing time in load_async_packages drops ~39% (1.30s -> 0.80s
cumulative), narrowing the E2E gap vs upstream Composer from 1.37s to
1.14s on average.
Diffstat (limited to 'crates/shirabe/src/repository')
| -rw-r--r-- | crates/shirabe/src/repository/composer_repository.rs | 68 |
1 files changed, 38 insertions, 30 deletions
diff --git a/crates/shirabe/src/repository/composer_repository.rs b/crates/shirabe/src/repository/composer_repository.rs index 580667b5..ab83ace2 100644 --- a/crates/shirabe/src/repository/composer_repository.rs +++ b/crates/shirabe/src/repository/composer_repository.rs @@ -1788,47 +1788,55 @@ impl ComposerRepository { let version_parser = self.version_parser.clone(); // [$response, $packagesSource] = $spec; - let spec_list = spec.as_list().cloned().unwrap_or_default(); - let response = spec_list.first().cloned().unwrap_or(PhpMixed::Null); - let packages_source_val = spec_list.get(1).cloned().unwrap_or(PhpMixed::Null); + let mut spec_list = match spec { + PhpMixed::List(l) => l, + _ => Vec::new(), + }; + let packages_source_val = if spec_list.len() > 1 { + spec_list.remove(1) + } else { + PhpMixed::Null + }; + let response = if !spec_list.is_empty() { + spec_list.remove(0) + } else { + PhpMixed::Null + }; let packages_source: Option<String> = packages_source_val.as_string().map(|s| s.to_string()); if response.is_null() { continue; } - let response_arr = match response.as_array() { - Some(a) => a.clone(), - None => continue, - }; - let inner_packages = response_arr.get("packages"); - let versions_mixed = match inner_packages - .and_then(|v| v.as_array()) - .and_then(|a| a.get(&real_name)) - .cloned() - { - Some(b) => b, - None => continue, + let mut response_arr = match response { + PhpMixed::Array(a) => a, + _ => continue, }; + // Takes ownership out of response_arr instead of cloning: response_arr is local to + // this iteration and "packages" is not read again afterwards (only "minified" is). + let versions_mixed = + match response_arr + .shift_remove("packages") + .and_then(|mut v| match &mut v { + PhpMixed::Array(m) => m.shift_remove(&real_name), + _ => None, + }) { + Some(b) => b, + None => continue, + }; - let mut versions: Vec<IndexMap<String, PhpMixed>> = match &versions_mixed { + let mut versions: Vec<IndexMap<String, PhpMixed>> = match versions_mixed { PhpMixed::List(l) => l - .iter() - .filter_map(|v| { - v.as_array().map(|a| { - a.iter() - .map(|(k, v)| (k.clone(), v.clone())) - .collect::<IndexMap<String, PhpMixed>>() - }) + .into_iter() + .filter_map(|v| match v { + PhpMixed::Array(a) => Some(a), + _ => None, }) .collect(), PhpMixed::Array(a) => a - .values() - .filter_map(|v| { - v.as_array().map(|a| { - a.iter() - .map(|(k, v)| (k.clone(), v.clone())) - .collect::<IndexMap<String, PhpMixed>>() - }) + .into_values() + .filter_map(|v| match v { + PhpMixed::Array(a) => Some(a), + _ => None, }) .collect(), _ => continue, |
