From 82bed4910ebc4f85be11266d3def197739f7f7db Mon Sep 17 00:00:00 2001 From: Tunglies <77394545+Tunglies@users.noreply.github.com> Date: Sat, 22 Nov 2025 16:25:23 +0800 Subject: [PATCH] perf: refactor IRuntime to use HashSet for exists_keys and improve related functions performance --- crates/clash-verge-types/src/runtime.rs | 109 +++++++++++++----------- src-tauri/src/cmd/runtime.rs | 4 +- src-tauri/src/core/manager/config.rs | 4 +- src-tauri/src/enhance/mod.rs | 9 +- 4 files changed, 68 insertions(+), 58 deletions(-) diff --git a/crates/clash-verge-types/src/runtime.rs b/crates/clash-verge-types/src/runtime.rs index 91603f2ed..ad013e5d8 100644 --- a/crates/clash-verge-types/src/runtime.rs +++ b/crates/clash-verge-types/src/runtime.rs @@ -1,79 +1,90 @@ use serde_yaml_ng::{Mapping, Value}; use smartstring::alias::String; -use std::collections::HashMap; +use std::collections::{HashMap, HashSet}; + +const PATCH_CONFIG_INNER: [&str; 4] = ["allow-lan", "ipv6", "log-level", "unified-delay"]; #[derive(Default, Clone)] pub struct IRuntime { pub config: Option, // 记录在订阅中(包括merge和script生成的)出现过的keys // 这些keys不一定都生效 - // TODO 或许我们可以用 hashset 来存储以提升查询效率 - pub exists_keys: Vec, + pub exists_keys: HashSet, // TODO 或许可以用 FixMap 来存储以提升效率 pub chain_logs: HashMap>, } impl IRuntime { + #[inline] pub fn new() -> Self { Self::default() } // 这里只更改 allow-lan | ipv6 | log-level | tun + #[inline] pub fn patch_config(&mut self, patch: Mapping) { - if let Some(config) = self.config.as_mut() { - ["allow-lan", "ipv6", "log-level", "unified-delay"] - .into_iter() - .for_each(|key| { - if let Some(value) = patch.get(key).to_owned() { - config.insert(key.into(), value.clone()); - } - }); + let config = if let Some(config) = self.config.as_mut() { + config + } else { + return; + }; - let patch_tun = patch.get("tun"); - if patch_tun.is_some() { - let tun = config.get("tun"); - let mut tun: Mapping = tun.map_or_else(Mapping::new, |val| { - val.as_mapping().cloned().unwrap_or_else(Mapping::new) - }); - let patch_tun = patch_tun.map_or_else(Mapping::new, |val| { - val.as_mapping().cloned().unwrap_or_else(Mapping::new) - }); - use_keys(&patch_tun).into_iter().for_each(|key| { - if let Some(value) = patch_tun.get(key.as_str()) { + for key in PATCH_CONFIG_INNER.iter() { + if let Some(value) = patch.get(key) { + config.insert((*key).into(), value.clone()); + } + } + + let patch_tun = patch.get("tun"); + if let Some(patch_tun_value) = patch_tun { + let mut tun = config + .get("tun") + .and_then(|val| val.as_mapping()) + .cloned() + .unwrap_or_else(Mapping::new); + + if let Some(patch_tun_mapping) = patch_tun_value.as_mapping() { + for key in use_keys(patch_tun_mapping) { + if let Some(value) = patch_tun_mapping.get(key.as_str()) { tun.insert(Value::from(key.as_str()), value.clone()); } - }); - - config.insert("tun".into(), Value::from(tun)); + } } + + config.insert("tun".into(), Value::from(tun)); } } // 传入none 为删除 + #[inline] pub fn update_proxy_chain_config(&mut self, proxy_chain_config: Option) { - if let Some(config) = self.config.as_mut() { - if let Some(Value::Sequence(proxies)) = config.get_mut("proxies") { - proxies.iter_mut().for_each(|proxy| { - if let Some(proxy) = proxy.as_mapping_mut() - && proxy.get("dialer-proxy").is_some() - { - proxy.remove("dialer-proxy"); - } - }); - } + let config = if let Some(config) = self.config.as_mut() { + config + } else { + return; + }; - if let Some(Value::Sequence(dialer_proxies)) = proxy_chain_config - && let Some(Value::Sequence(proxies)) = config.get_mut("proxies") - { - for (i, dialer_proxy) in dialer_proxies.iter().enumerate() { - if let Some(Value::Mapping(proxy)) = proxies - .iter_mut() - .find(|proxy| proxy.get("name") == Some(dialer_proxy)) - && i != 0 - && let Some(dialer_proxy) = dialer_proxies.get(i - 1) - { - proxy.insert("dialer-proxy".into(), dialer_proxy.to_owned()); - } + if let Some(Value::Sequence(proxies)) = config.get_mut("proxies") { + proxies.iter_mut().for_each(|proxy| { + if let Some(proxy) = proxy.as_mapping_mut() + && proxy.get("dialer-proxy").is_some() + { + proxy.remove("dialer-proxy"); + } + }); + } + + if let Some(Value::Sequence(dialer_proxies)) = proxy_chain_config + && let Some(Value::Sequence(proxies)) = config.get_mut("proxies") + { + for (i, dialer_proxy) in dialer_proxies.iter().enumerate() { + if let Some(Value::Mapping(proxy)) = proxies + .iter_mut() + .find(|proxy| proxy.get("name") == Some(dialer_proxy)) + && i != 0 + && let Some(dialer_proxy) = dialer_proxies.get(i - 1) + { + proxy.insert("dialer-proxy".into(), dialer_proxy.to_owned()); } } } @@ -81,7 +92,8 @@ impl IRuntime { } // TODO 完整迁移 enhance 行为后移除 -fn use_keys(config: &Mapping) -> Vec { +#[inline] +fn use_keys<'a>(config: &'a Mapping) -> impl Iterator + 'a { config .iter() .filter_map(|(key, _)| key.as_str()) @@ -90,5 +102,4 @@ fn use_keys(config: &Mapping) -> Vec { s.make_ascii_lowercase(); s }) - .collect() } diff --git a/src-tauri/src/cmd/runtime.rs b/src-tauri/src/cmd/runtime.rs index 9ad8c3acb..24f0cbec5 100644 --- a/src-tauri/src/cmd/runtime.rs +++ b/src-tauri/src/cmd/runtime.rs @@ -4,7 +4,7 @@ use anyhow::{Context as _, anyhow}; use clash_verge_logging::{Type, logging_error}; use serde_yaml_ng::Mapping; use smartstring::alias::String; -use std::collections::HashMap; +use std::collections::{HashMap, HashSet}; /// 获取运行时配置 #[tauri::command] @@ -31,7 +31,7 @@ pub async fn get_runtime_yaml() -> CmdResult { /// 获取运行时存在的键 #[tauri::command] -pub async fn get_runtime_exists() -> CmdResult> { +pub async fn get_runtime_exists() -> CmdResult> { Ok(Config::runtime().await.latest_arc().exists_keys.clone()) } diff --git a/src-tauri/src/core/manager/config.rs b/src-tauri/src/core/manager/config.rs index 9e81bdd08..b0a499287 100644 --- a/src-tauri/src/core/manager/config.rs +++ b/src-tauri/src/core/manager/config.rs @@ -9,7 +9,7 @@ use anyhow::{Result, anyhow}; use clash_verge_logging::{Type, logging}; use clash_verge_types::runtime::IRuntime; use smartstring::alias::String; -use std::{path::PathBuf, time::Instant}; +use std::{collections::HashSet, path::PathBuf, time::Instant}; use tauri_plugin_mihomo::Error as MihomoError; impl CoreManager { @@ -22,7 +22,7 @@ impl CoreManager { Config::runtime().await.edit_draft(|d| { *d = IRuntime { config: Some(clash_config.to_owned()), - exists_keys: vec![], + exists_keys: HashSet::new(), chain_logs: Default::default(), } }); diff --git a/src-tauri/src/enhance/mod.rs b/src-tauri/src/enhance/mod.rs index 484e16f2a..f8e8ba05c 100644 --- a/src-tauri/src/enhance/mod.rs +++ b/src-tauri/src/enhance/mod.rs @@ -597,7 +597,7 @@ async fn apply_dns_settings(mut config: Mapping, enable_dns_settings: bool) -> M /// Enhance mode /// 返回最终订阅、该订阅包含的键、和script执行的结果 -pub async fn enhance() -> (Mapping, Vec, HashMap) { +pub async fn enhance() -> (Mapping, HashSet, HashMap) { // gather config values let cfg_vals = get_config_values().await; let ConfigValues { @@ -667,11 +667,10 @@ pub async fn enhance() -> (Mapping, Vec, HashMap) { // dns settings config = apply_dns_settings(config, enable_dns_settings).await; - let mut exists_set = HashSet::new(); - exists_set.extend(exists_keys); - let exists_keys: Vec = exists_set.into_iter().collect(); + let mut exists_keys_set = HashSet::new(); + exists_keys_set.extend(exists_keys); - (config, exists_keys, result_map) + (config, exists_keys_set, result_map) } #[allow(clippy::expect_used)]