From ef5fa1cb4607a6ff30438ec2f0d163cf7e35fe50 Mon Sep 17 00:00:00 2001 From: zarzet <42882290+zarzet@users.noreply.github.com> Date: Sun, 18 Oct 2026 22:11:05 +0700 Subject: [PATCH] perf(lyrics): compile credit regexes once with_credits runs for every parse_lyrics data job and compiled up to seven regexes per call. Hoist them into LazyLock statics next to the other lyrics patterns, with the same pattern text, so each is compiled once per process. Every other regex in the workspace is already static. storage_failure also lowercased the error message once per probe on the download failure path; lowercase it once and reuse it. --- .../extensions/src/backend/downloads.rs | 23 +++++---- .../crates/mobile/src/native_lyrics.rs | 50 ++++++++----------- 2 files changed, 35 insertions(+), 38 deletions(-) diff --git a/rust_backend/crates/extensions/src/backend/downloads.rs b/rust_backend/crates/extensions/src/backend/downloads.rs index 5f96ea9b..10417765 100644 --- a/rust_backend/crates/extensions/src/backend/downloads.rs +++ b/rust_backend/crates/extensions/src/backend/downloads.rs @@ -1810,16 +1810,19 @@ fn stopped(service: &str, availability: &Value, message: &str) -> Value { } fn storage_failure(kind: &str, message: &str) -> bool { - kind.eq_ignore_ascii_case("permission") - || [ - "operation not permitted", - "permission denied", - "read-only file system", - "failed to create file", - "failed to create directory", - ] - .into_iter() - .any(|part| message.to_lowercase().contains(part)) + if kind.eq_ignore_ascii_case("permission") { + return true; + } + let message = message.to_lowercase(); + [ + "operation not permitted", + "permission denied", + "read-only file system", + "failed to create file", + "failed to create directory", + ] + .into_iter() + .any(|part| message.contains(part)) } #[cfg(test)] diff --git a/rust_backend/crates/mobile/src/native_lyrics.rs b/rust_backend/crates/mobile/src/native_lyrics.rs index abb8cd60..d05b6c88 100644 --- a/rust_backend/crates/mobile/src/native_lyrics.rs +++ b/rust_backend/crates/mobile/src/native_lyrics.rs @@ -31,6 +31,20 @@ static SUPPLEMENT: LazyLock = LazyLock::new(|| { static NEW_LINE: LazyLock = LazyLock::new(|| Regex::new(r"\r\n|\r|\n").unwrap()); static WRITER_CREDIT: LazyLock = LazyLock::new(|| Regex::new(&format!(r"(?i)^Written{WS}+by{WS}*:{WS}*(.+)$")).unwrap()); +static AU_TAG: LazyLock = + LazyLock::new(|| Regex::new(&format!(r"(?im)^\[au:([^\]\r\n]*)\]{WS}*$")).unwrap()); +static PROVIDER_TAG: LazyLock = + LazyLock::new(|| Regex::new(&format!(r"(?im)^\[x-provider:([^\]\r\n]*)\]{WS}*$")).unwrap()); +static BY_TAG: LazyLock = + LazyLock::new(|| Regex::new(&format!(r"(?im)^\[by:([^\]\r\n]*)\]{WS}*$")).unwrap()); +static VIA_CREDIT: LazyLock = + LazyLock::new(|| Regex::new(&format!(r"(?i)\bvia{WS}+([^\(]+)")).unwrap()); +static API_SUFFIX: LazyLock = + LazyLock::new(|| Regex::new(&format!(r"(?i){WS}+API$")).unwrap()); +static SOURCE_CREDIT: LazyLock = + LazyLock::new(|| Regex::new(&format!(r"(?i)\(source:{WS}*([^\)]+)\)")).unwrap()); +static EXTENSION_PREFIX: LazyLock = + LazyLock::new(|| Regex::new(r"(?i)^extension:").unwrap()); #[derive(Clone, Debug, Serialize, PartialEq)] #[serde(rename_all = "camelCase")] @@ -897,39 +911,19 @@ fn xml_source(input: &str, drop_cdata: bool) -> String { } fn with_credits(raw: &str, lyrics: &mut Parsed) -> Result<(), String> { - let tag = |name: &str| { - Regex::new(&format!(r"(?im)^\[{name}:([^\]\r\n]*)\]{WS}*$")) - .unwrap() - .captures(raw) - .map(|c| trim(&c[1]).to_string()) - }; - let mut writers = tag("au"); - let mut provider = tag("x-provider"); - let credit = tag("by").unwrap_or_default(); + let tag = |pattern: &Regex| pattern.captures(raw).map(|c| trim(&c[1]).to_string()); + let mut writers = tag(&AU_TAG); + let mut provider = tag(&PROVIDER_TAG); + let credit = tag(&BY_TAG).unwrap_or_default(); if provider.is_none() - && let Some(c) = Regex::new(&format!(r"(?i)\bvia{WS}+([^\(]+)")) - .unwrap() - .captures(&credit) + && let Some(c) = VIA_CREDIT.captures(&credit) { - provider = Some( - Regex::new(&format!(r"(?i){WS}+API$")) - .unwrap() - .replace(trim(&c[1]), "") - .into(), - ); + provider = Some(API_SUFFIX.replace(trim(&c[1]), "").into()); } if provider.is_none() { - provider = Regex::new(&format!(r"(?i)\(source:{WS}*([^\)]+)\)")) - .unwrap() - .captures(&credit) - .map(|c| trim(&c[1]).into()); + provider = SOURCE_CREDIT.captures(&credit).map(|c| trim(&c[1]).into()); } - provider = provider.map(|value| { - Regex::new(r"(?i)^extension:") - .unwrap() - .replace(&value, "") - .into() - }); + provider = provider.map(|value| EXTENSION_PREFIX.replace(&value, "").into()); if looks_ttml(raw) { spotiflac_core::xml::check_depth(raw)?; let normalized = xml_source(raw, false);