From fa6c06e3753a6a5df9f78699f939fdac10e9a467 Mon Sep 17 00:00:00 2001 From: loxoron218 Date: Tue, 21 Apr 2026 22:06:53 +0200 Subject: [PATCH 1/2] refactor(lofty): replace OnceLock with LazyLock Replace `std::sync::OnceLock` with `std::sync::LazyLock` for static initializations in `upgrade.rs`, `write/mod.rs`, `resolve.rs`, and `tag/item.rs` to simplify lazy initialization logic. --- lofty/src/id3/v2/util/upgrade.rs | 40 ++++++++++++++------------------ lofty/src/id3/v2/write/mod.rs | 8 +++---- lofty/src/resolve.rs | 6 ++--- lofty/src/tag/item.rs | 6 ++--- 4 files changed, 27 insertions(+), 33 deletions(-) diff --git a/lofty/src/id3/v2/util/upgrade.rs b/lofty/src/id3/v2/util/upgrade.rs index 6bc10d7f0..f3b7e0bfb 100644 --- a/lofty/src/id3/v2/util/upgrade.rs +++ b/lofty/src/id3/v2/util/upgrade.rs @@ -15,7 +15,7 @@ use std::collections::HashMap; /// assert_eq!(new_title, Some("TIT2")); /// ``` pub fn upgrade_v2(key: &str) -> Option<&'static str> { - v2keys().get(key).copied() + V2_KEYS.get(key).copied() } /// Upgrade an ID3v2.3 key to an ID3v2.4 key @@ -31,38 +31,32 @@ pub fn upgrade_v2(key: &str) -> Option<&'static str> { /// assert_eq!(new_involved_people_list, Some("TIPL")); /// ``` pub fn upgrade_v3(key: &str) -> Option<&'static str> { - v3keys().get(key).copied() + V3_KEYS.get(key).copied() } macro_rules! gen_upgrades { (V2 => [$($($v2_key:literal)|* => $id3v24_from_v2:literal),+]; V3 => [$($($v3_key:literal)|* => $id3v24_from_v3:literal),+]) => { - use std::sync::OnceLock; + use std::sync::LazyLock; - fn v2keys() -> &'static HashMap<&'static str, &'static str> { - static INSTANCE: OnceLock> = OnceLock::new(); - INSTANCE.get_or_init(|| { - let mut map = HashMap::new(); + static V2_KEYS: LazyLock> = LazyLock::new(|| { + let mut map = HashMap::new(); + $( $( - $( - map.insert($v2_key, $id3v24_from_v2); - )+ + map.insert($v2_key, $id3v24_from_v2); )+ - map - }) - } + )+ + map + }); - fn v3keys() -> &'static HashMap<&'static str, &'static str> { - static INSTANCE: OnceLock> = OnceLock::new(); - INSTANCE.get_or_init(|| { - let mut map = HashMap::new(); + static V3_KEYS: LazyLock> = LazyLock::new(|| { + let mut map = HashMap::new(); + $( $( - $( - map.insert($v3_key, $id3v24_from_v3); - )+ + map.insert($v3_key, $id3v24_from_v3); )+ - map - }) - } + )+ + map + }); }; } diff --git a/lofty/src/id3/v2/write/mod.rs b/lofty/src/id3/v2/write/mod.rs index f38708dc3..27e19cc6d 100644 --- a/lofty/src/id3/v2/write/mod.rs +++ b/lofty/src/id3/v2/write/mod.rs @@ -15,14 +15,13 @@ use crate::util::io::{FileLike, Length, Truncate}; use std::io::{Cursor, Read, Seek, SeekFrom, Write}; use std::ops::Not; -use std::sync::OnceLock; +use std::sync::LazyLock; use byteorder::{BigEndian, LittleEndian, WriteBytesExt}; // In the very rare chance someone wants to write a CRC in their extended header fn crc_32_table() -> &'static [u32; 256] { - static INSTANCE: OnceLock<[u32; 256]> = OnceLock::new(); - INSTANCE.get_or_init(|| { + static INSTANCE: LazyLock<[u32; 256]> = LazyLock::new(|| { let mut crc32_table = [0; 256]; for n in 0..256 { @@ -33,7 +32,8 @@ fn crc_32_table() -> &'static [u32; 256] { } crc32_table - }) + }); + &INSTANCE } #[allow(clippy::shadow_unrelated)] diff --git a/lofty/src/resolve.rs b/lofty/src/resolve.rs index 9dd9f2d58..2de189572 100644 --- a/lofty/src/resolve.rs +++ b/lofty/src/resolve.rs @@ -9,7 +9,7 @@ use crate::tag::{TagSupport, TagType}; use std::collections::HashMap; use std::io::{Read, Seek}; use std::marker::PhantomData; -use std::sync::{Arc, Mutex, OnceLock}; +use std::sync::{Arc, LazyLock, Mutex}; /// A custom file resolver /// @@ -36,8 +36,8 @@ pub trait FileResolver: Send + Sync + AudioFile { type ResolverMap = HashMap<&'static str, &'static dyn ObjectSafeFileResolver>; pub(crate) fn custom_resolvers() -> &'static Arc> { - static INSTANCE: OnceLock>> = OnceLock::new(); - INSTANCE.get_or_init(Default::default) + static INSTANCE: LazyLock>> = LazyLock::new(Default::default); + &INSTANCE } pub(crate) fn lookup_resolver(name: &'static str) -> &'static dyn ObjectSafeFileResolver { diff --git a/lofty/src/tag/item.rs b/lofty/src/tag/item.rs index d1982f037..a46d83127 100644 --- a/lofty/src/tag/item.rs +++ b/lofty/src/tag/item.rs @@ -52,8 +52,7 @@ macro_rules! gen_map { $(#[$meta])? impl $NAME { pub(crate) fn get_item_key(&self, key: &str) -> Option { - static INSTANCE: std::sync::OnceLock> = std::sync::OnceLock::new(); - INSTANCE.get_or_init(|| { + static INSTANCE: std::sync::LazyLock> = std::sync::LazyLock::new(|| { let mut map = HashMap::new(); $( let values: &'static [ItemKey] = &[$(ItemKey::$variant,)+]; @@ -62,7 +61,8 @@ macro_rules! gen_map { )+ )+ map - }).iter().find(|(k, _)| k.eq_ignore_ascii_case(key)).map(|(_, v)| v[0]) + }); + INSTANCE.iter().find(|(k, _)| k.eq_ignore_ascii_case(key)).map(|(_, v)| v[0]) } pub(crate) fn get_key(&self, item_key: ItemKey) -> Option<&'static str> { From c6409a925b36ec43aa5553045b7737b5178db8bc Mon Sep 17 00:00:00 2001 From: loxoron218 Date: Thu, 23 Apr 2026 20:33:38 +0200 Subject: [PATCH 2/2] refactor(lofty): replace custom_resolvers function with CUSTOM_RESOLVERS static Replace the `custom_resolvers()` function with a global `CUSTOM_RESOLVERS` `LazyLock` static to simplify access and align with the transition from `OnceLock` to `LazyLock`. This also includes refactoring the CRC32 table to use a static `LazyLock` instead of a function. --- lofty/src/file/file_type.rs | 4 ++-- lofty/src/id3/v2/write/mod.rs | 27 ++++++++++++--------------- lofty/src/probe.rs | 4 ++-- lofty/src/resolve.rs | 10 ++++------ 4 files changed, 20 insertions(+), 25 deletions(-) diff --git a/lofty/src/file/file_type.rs b/lofty/src/file/file_type.rs index 67280fc9f..a9d1dce76 100644 --- a/lofty/src/file/file_type.rs +++ b/lofty/src/file/file_type.rs @@ -1,5 +1,5 @@ use crate::config::global_options; -use crate::resolve::custom_resolvers; +use crate::resolve::CUSTOM_RESOLVERS; use crate::tag::{TagSupport, TagType}; use std::ffi::OsStr; @@ -189,7 +189,7 @@ impl FileType { // Give custom resolvers priority if unsafe { global_options().use_custom_resolvers } { - if let Some((ty, _)) = custom_resolvers() + if let Some((ty, _)) = CUSTOM_RESOLVERS .lock() .ok()? .iter() diff --git a/lofty/src/id3/v2/write/mod.rs b/lofty/src/id3/v2/write/mod.rs index 27e19cc6d..e6e581df9 100644 --- a/lofty/src/id3/v2/write/mod.rs +++ b/lofty/src/id3/v2/write/mod.rs @@ -20,21 +20,18 @@ use std::sync::LazyLock; use byteorder::{BigEndian, LittleEndian, WriteBytesExt}; // In the very rare chance someone wants to write a CRC in their extended header -fn crc_32_table() -> &'static [u32; 256] { - static INSTANCE: LazyLock<[u32; 256]> = LazyLock::new(|| { - let mut crc32_table = [0; 256]; - - for n in 0..256 { - crc32_table[n as usize] = (0..8).fold(n as u32, |acc, _| match acc & 1 { - 1 => 0xEDB8_8320 ^ (acc >> 1), - _ => acc >> 1, - }); - } +static CRC32_TABLE: LazyLock<[u32; 256]> = LazyLock::new(|| { + let mut crc32_table = [0; 256]; + + for n in 0..256 { + crc32_table[n as usize] = (0..8).fold(n as u32, |acc, _| match acc & 1 { + 1 => 0xEDB8_8320 ^ (acc >> 1), + _ => acc >> 1, + }); + } - crc32_table - }); - &INSTANCE -} + crc32_table +}); #[allow(clippy::shadow_unrelated)] pub(crate) fn write_id3v2<'a, F, I>( @@ -270,7 +267,7 @@ fn calculate_crc(content: &[u8]) -> [u8; 5] { let crc: u32 = content .iter() .fold(!0, |crc, octet| { - (crc >> 8) ^ crc_32_table()[(((crc & 0xFF) ^ u32::from(*octet)) & 0xFF) as usize] + (crc >> 8) ^ CRC32_TABLE[(((crc & 0xFF) ^ u32::from(*octet)) & 0xFF) as usize] }) .not(); diff --git a/lofty/src/probe.rs b/lofty/src/probe.rs index 5f41d3489..48adb3045 100644 --- a/lofty/src/probe.rs +++ b/lofty/src/probe.rs @@ -16,7 +16,7 @@ use crate::musepack::MpcFile; use crate::ogg::opus::OpusFile; use crate::ogg::speex::SpeexFile; use crate::ogg::vorbis::VorbisFile; -use crate::resolve::custom_resolvers; +use crate::resolve::CUSTOM_RESOLVERS; use crate::wavpack::WavPackFile; use crate::io::FileLike; @@ -336,7 +336,7 @@ impl Probe { // Give custom resolvers priority if unsafe { global_options().use_custom_resolvers } { - if let Ok(lock) = custom_resolvers().lock() { + if let Ok(lock) = CUSTOM_RESOLVERS.lock() { #[allow(clippy::significant_drop_in_scrutinee)] for (_, resolve) in lock.iter() { if let ret @ Some(_) = resolve.guess(&buf[..buf_len]) { diff --git a/lofty/src/resolve.rs b/lofty/src/resolve.rs index 2de189572..9e3ac1f7f 100644 --- a/lofty/src/resolve.rs +++ b/lofty/src/resolve.rs @@ -35,13 +35,11 @@ pub trait FileResolver: Send + Sync + AudioFile { // Just broken out to its own type to make `CUSTOM_RESOLVER`'s type shorter :) type ResolverMap = HashMap<&'static str, &'static dyn ObjectSafeFileResolver>; -pub(crate) fn custom_resolvers() -> &'static Arc> { - static INSTANCE: LazyLock>> = LazyLock::new(Default::default); - &INSTANCE -} +pub(crate) static CUSTOM_RESOLVERS: LazyLock>> = + LazyLock::new(Default::default); pub(crate) fn lookup_resolver(name: &'static str) -> &'static dyn ObjectSafeFileResolver { - let res = custom_resolvers().lock().unwrap(); + let res = CUSTOM_RESOLVERS.lock().unwrap(); if let Some(resolver) = res.get(name).copied() { return resolver; @@ -114,7 +112,7 @@ impl ObjectSafeFileResolver for GhostlyResolver { /// * Attempting to register an existing name or type /// * See [`Mutex::lock`] pub fn register_custom_resolver(name: &'static str) { - let mut res = custom_resolvers().lock().unwrap(); + let mut res = CUSTOM_RESOLVERS.lock().unwrap(); assert!( res.iter().all(|(n, _)| *n != name), "Resolver `{}` already exists!",