From 3785cf7633e824f07ce963fb811c397e7646ce4d Mon Sep 17 00:00:00 2001 From: Roderick van Domburg Date: Sat, 11 Jan 2025 23:51:55 +0100 Subject: [PATCH] perf: inline performance-critical operations * Standardize inlining of simple, frequently-used operations * Focus on performance-critical audio processing paths * Inline UUID operations (deref, display, conversion, generation) * Inline numeric type conversions in util module --- CHANGELOG.md | 1 + src/arl.rs | 3 ++ src/gateway.rs | 7 +++ src/http.rs | 3 ++ src/player.rs | 16 +++++++ src/protocol/codec.rs | 1 + src/protocol/connect/channel.rs | 2 + src/protocol/connect/contents.rs | 8 ++++ src/protocol/connect/messages.rs | 1 + src/protocol/connect/stream.rs | 3 ++ src/protocol/gateway/list_data/episodes.rs | 1 + src/protocol/gateway/list_data/livestream.rs | 1 + src/protocol/gateway/list_data/mod.rs | 9 ++++ src/protocol/gateway/list_data/songs.rs | 1 + src/protocol/gateway/mod.rs | 2 + src/protocol/gateway/user_data.rs | 5 +++ src/protocol/gateway/user_radio.rs | 1 + src/protocol/media.rs | 5 +++ src/proxy.rs | 2 + src/remote.rs | 47 ++++++++++++++++---- src/signal.rs | 1 + src/tokens.rs | 3 ++ src/track.rs | 23 ++++++++++ src/util.rs | 4 ++ src/uuid.rs | 4 ++ 25 files changed, 146 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7353b63..df04eb1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ and [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/). ### Changed - [decrypt] Add explicit `BufRead` implementation to standardize buffering behavior - [decrypt] Inline performance-critical buffer operations +- [docs] Remove incorrect mention of "Hi-Res" audio quality ## [0.8.1] - 2025-01-11 diff --git a/src/arl.rs b/src/arl.rs index 98b7a69..39f9f8e 100644 --- a/src/arl.rs +++ b/src/arl.rs @@ -87,6 +87,7 @@ impl Arl { /// assert!(Arl::new("spaces not allowed".to_string()).is_err()); /// assert!(Arl::new("控制字符".to_string()).is_err()); /// ``` + #[inline] pub fn new(arl: String) -> Result { Ok(Self(arl)) } @@ -110,6 +111,7 @@ impl Deref for Arl { /// validation invariants. type Target = String; + #[inline] fn deref(&self) -> &Self::Target { &self.0 } @@ -135,6 +137,7 @@ impl Deref for Arl { /// println!("{:?}", arl); // Prints: Arl("REDACTED") /// ``` impl fmt::Display for Arl { + #[inline] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}", self.0) } diff --git a/src/gateway.rs b/src/gateway.rs index f72785b..ff917e5 100644 --- a/src/gateway.rs +++ b/src/gateway.rs @@ -362,6 +362,7 @@ impl Gateway { /// /// The license token is required for media access. #[must_use] + #[inline] pub fn license_token(&self) -> Option<&str> { self.user_data .as_ref() @@ -374,6 +375,7 @@ impl Gateway { /// * No user data is available /// * Current time is past expiration time #[must_use] + #[inline] pub fn is_expired(&self) -> bool { self.expires_at() <= SystemTime::now() } @@ -382,6 +384,7 @@ impl Gateway { /// /// Returns UNIX epoch if no session is active. #[must_use] + #[inline] pub fn expires_at(&self) -> SystemTime { if let Some(data) = &self.user_data { return data.user.options.expiration_timestamp; @@ -391,12 +394,14 @@ impl Gateway { } /// Updates the cached user data. + #[inline] pub fn set_user_data(&mut self, data: UserData) { self.user_data = Some(data); } /// Returns a reference to the current user data if available. #[must_use] + #[inline] pub fn user_data(&self) -> Option<&UserData> { self.user_data.as_ref() } @@ -430,6 +435,7 @@ impl Gateway { /// Returns the user's display name if available. #[must_use] + #[inline] pub fn user_name(&self) -> Option<&str> { self.user_data.as_ref().map(|data| data.user.name.as_str()) } @@ -601,6 +607,7 @@ impl Gateway { /// /// Forces a refresh on next token request while preserving /// other API functionality. + #[inline] pub fn flush_user_token(&mut self) { // Force refreshing user data, but do not set `user_data` to `None` so // so we can continue using the `api_token` it contains. diff --git a/src/http.rs b/src/http.rs index 31ed8d9..f392c17 100644 --- a/src/http.rs +++ b/src/http.rs @@ -205,6 +205,7 @@ impl Client { /// ); /// let response = client.execute(request).await?; /// ``` + #[inline] pub fn request(&self, method: Method, url: U, body: T) -> reqwest::Request where U: Into, @@ -225,6 +226,7 @@ impl Client { /// /// * `url` - Request URL /// * `body` - Request body content + #[inline] pub fn post(&self, url: U, body: T) -> reqwest::Request where U: Into, @@ -241,6 +243,7 @@ impl Client { /// /// * `url` - Request URL /// * `body` - Request body content (usually empty) + #[inline] pub fn get(&self, url: U, body: T) -> reqwest::Request where U: Into, diff --git a/src/player.rs b/src/player.rs index d642e70..7625ddc 100644 --- a/src/player.rs +++ b/src/player.rs @@ -990,12 +990,14 @@ impl Player { /// Returns the currently playing track, if any. #[must_use] + #[inline] pub fn track(&self) -> Option<&Track> { self.queue.get(self.position) } /// Returns a mutable reference to the currently playing track, if any. #[must_use] + #[inline] pub fn track_mut(&mut self) -> Option<&mut Track> { self.queue.get_mut(self.position) } @@ -1015,6 +1017,7 @@ impl Player { /// Returns a reference to the next track in the queue, if any. #[must_use] + #[inline] pub fn next_track(&self) -> Option<&Track> { let next = self.position.saturating_add(1); self.queue.get(next) @@ -1022,6 +1025,7 @@ impl Player { /// Returns a mutable reference to the next track in the queue, if any. #[must_use] + #[inline] pub fn next_track_mut(&mut self) -> Option<&mut Track> { let next = self.position.saturating_add(1); self.queue.get_mut(next) @@ -1154,6 +1158,7 @@ impl Player { /// Returns the current repeat mode. #[must_use] + #[inline] pub fn repeat_mode(&self) -> RepeatMode { self.repeat_mode } @@ -1186,6 +1191,7 @@ impl Player { /// /// Note: This returns the stored volume setting even if the audio device is closed. #[must_use] + #[inline] pub fn volume(&self) -> Percentage { self.volume } @@ -1425,16 +1431,19 @@ impl Player { /// Returns current position in the queue. #[must_use] + #[inline] pub fn position(&self) -> usize { self.position } /// Sets the license token for media access. + #[inline] pub fn set_license_token(&mut self, license_token: impl Into) { self.license_token = license_token.into(); } /// Enables or disables volume normalization. + #[inline] pub fn set_normalization(&mut self, normalization: bool) { self.normalization = normalization; } @@ -1457,35 +1466,41 @@ impl Player { /// /// Note: Actual quality may be lower if track is not /// available in requested quality. + #[inline] pub fn set_audio_quality(&mut self, quality: AudioQuality) { self.audio_quality = quality; } /// Returns whether volume normalization is enabled. #[must_use] + #[inline] pub fn normalization(&self) -> bool { self.normalization } /// Returns current license token. #[must_use] + #[inline] pub fn license_token(&self) -> &str { &self.license_token } /// Returns current preferred audio quality setting. #[must_use] + #[inline] pub fn audio_quality(&self) -> AudioQuality { self.audio_quality } /// Returns current normalization target gain. #[must_use] + #[inline] pub fn gain_target_db(&self) -> i8 { self.gain_target_db } /// Sets the media content URL. + #[inline] pub fn set_media_url(&mut self, url: Url) { self.media_url = url; } @@ -1507,6 +1522,7 @@ impl Player { /// assert!(!player.is_started()); /// ``` #[must_use] + #[inline] pub fn is_started(&self) -> bool { self.sink.is_some() } diff --git a/src/protocol/codec.rs b/src/protocol/codec.rs index 4d58b9b..29d6edc 100644 --- a/src/protocol/codec.rs +++ b/src/protocol/codec.rs @@ -83,6 +83,7 @@ impl Codec { /// * "flac" /// * "mp3" impl fmt::Display for Codec { + #[inline] fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { Codec::AAC => write!(f, "aac"), diff --git a/src/protocol/connect/channel.rs b/src/protocol/connect/channel.rs index 7d4e29e..0cfa9f6 100644 --- a/src/protocol/connect/channel.rs +++ b/src/protocol/connect/channel.rs @@ -648,6 +648,7 @@ impl FromStr for Channel { /// assert_eq!(unspec.to_string(), "-1"); /// ``` impl fmt::Display for UserId { + #[inline] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Self::Id(id) => write!(f, "{id}"), @@ -714,6 +715,7 @@ impl FromStr for UserId { /// assert!(matches!(user, UserId::Id(_))); /// ``` impl From for UserId { + #[inline] fn from(id: NonZeroU64) -> Self { Self::Id(id) } diff --git a/src/protocol/connect/contents.rs b/src/protocol/connect/contents.rs index 0700cbf..9113d36 100644 --- a/src/protocol/connect/contents.rs +++ b/src/protocol/connect/contents.rs @@ -943,6 +943,7 @@ pub enum Status { /// println!("Command completed with status: {}", Status::OK); /// ``` impl fmt::Display for Status { + #[inline] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Status::OK => write!(f, "Ok"), @@ -1386,6 +1387,7 @@ impl Percentage { /// assert_eq!(p.as_percent_f32(), 75.0); /// ``` #[must_use] + #[inline] pub const fn from_ratio_f32(ratio: f32) -> Self { Self(ratio as f64) } @@ -1406,6 +1408,7 @@ impl Percentage { /// assert_eq!(p.as_percent_f64(), 50.0); /// ``` #[must_use] + #[inline] pub const fn from_ratio_f64(ratio: f64) -> Self { Self(ratio) } @@ -1426,6 +1429,7 @@ impl Percentage { /// assert_eq!(p.as_ratio_f32(), 0.75); /// ``` #[must_use] + #[inline] pub const fn from_percent_f32(percent: f32) -> Self { Self(percent as f64 / 100.0) } @@ -1446,6 +1450,7 @@ impl Percentage { /// assert_eq!(p.as_ratio_f64(), 0.75); /// ``` #[must_use] + #[inline] pub const fn from_percent_f64(percent: f64) -> Self { Self(percent / 100.0) } @@ -1478,6 +1483,7 @@ impl Percentage { /// assert_eq!(RATIO, 0.333); /// ``` #[must_use] + #[inline] pub const fn as_ratio_f64(&self) -> f64 { self.0 } @@ -1510,6 +1516,7 @@ impl Percentage { /// assert_eq!(PERCENT, 33.3); /// ``` #[must_use] + #[inline] pub const fn as_percent_f64(&self) -> f64 { self.0 * 100.0 } @@ -2420,6 +2427,7 @@ pub enum DeviceType { /// assert_eq!(DeviceType::Unknown.to_string(), "unknown"); /// ``` impl fmt::Display for DeviceType { + #[inline] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { DeviceType::Desktop => write!(f, "desktop"), diff --git a/src/protocol/connect/messages.rs b/src/protocol/connect/messages.rs index 5e8a35c..66f9465 100644 --- a/src/protocol/connect/messages.rs +++ b/src/protocol/connect/messages.rs @@ -418,6 +418,7 @@ enum Stanza { /// assert_eq!(Stanza::Send.to_string(), "Send"); /// ``` impl fmt::Display for Stanza { + #[inline] fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{self:?}") } diff --git a/src/protocol/connect/stream.rs b/src/protocol/connect/stream.rs index 9beafba..72d6206 100644 --- a/src/protocol/connect/stream.rs +++ b/src/protocol/connect/stream.rs @@ -383,6 +383,7 @@ impl Ident { /// println!("{contents}"); /// ``` impl fmt::Display for Contents { + #[inline] fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{} {}", self.action, self.value.track_id) } @@ -399,6 +400,7 @@ impl fmt::Display for Contents { /// assert_eq!(Action::Play.to_string(), "PLAY"); /// ``` impl fmt::Display for Action { + #[inline] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Self::Play => write!(f, "{}", Self::PLAY), @@ -445,6 +447,7 @@ impl FromStr for Action { /// assert_eq!(Ident::Limitation.to_string(), "LIMITATION"); /// ``` impl fmt::Display for Ident { + #[inline] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Self::Limitation => write!(f, "{}", Self::LIMITATION), diff --git a/src/protocol/gateway/list_data/episodes.rs b/src/protocol/gateway/list_data/episodes.rs index 2ab6a22..242f9ad 100644 --- a/src/protocol/gateway/list_data/episodes.rs +++ b/src/protocol/gateway/list_data/episodes.rs @@ -65,6 +65,7 @@ pub struct EpisodeData(pub ListData); impl Deref for EpisodeData { type Target = ListData; + #[inline] fn deref(&self) -> &Self::Target { &self.0 } diff --git a/src/protocol/gateway/list_data/livestream.rs b/src/protocol/gateway/list_data/livestream.rs index 705a96d..a4feea9 100644 --- a/src/protocol/gateway/list_data/livestream.rs +++ b/src/protocol/gateway/list_data/livestream.rs @@ -84,6 +84,7 @@ pub struct LivestreamData(pub ListData); impl Deref for LivestreamData { type Target = ListData; + #[inline] fn deref(&self) -> &Self::Target { &self.0 } diff --git a/src/protocol/gateway/list_data/mod.rs b/src/protocol/gateway/list_data/mod.rs index 22ba4a7..75e5e78 100644 --- a/src/protocol/gateway/list_data/mod.rs +++ b/src/protocol/gateway/list_data/mod.rs @@ -414,6 +414,7 @@ impl ListData { /// * "episode" - Podcast episode /// * "livestream" - Radio station #[must_use] + #[inline] pub const fn typ(&self) -> &'static str { match self { ListData::Song { .. } => "song", @@ -428,6 +429,7 @@ impl ListData { /// * Positive - Regular Deezer content /// * Negative - User uploaded songs #[must_use] + #[inline] pub fn id(&self) -> TrackId { match self { ListData::Song { id, .. } @@ -440,6 +442,7 @@ impl ListData { /// /// Returns None for livestreams which only have a station name. #[must_use] + #[inline] pub fn title(&self) -> Option<&str> { match self { ListData::Song { title, .. } | ListData::Episode { title, .. } => Some(title.as_str()), @@ -454,6 +457,7 @@ impl ListData { /// * Podcast name for episodes /// * Station name for livestreams #[must_use] + #[inline] pub fn artist(&self) -> &str { match self { ListData::Song { artist, .. } => artist.as_str(), @@ -469,6 +473,7 @@ impl ListData { /// * Podcast artwork ID for episodes /// * Station logo ID for livestreams #[must_use] + #[inline] pub fn cover_id(&self) -> &str { match self { ListData::Song { album_cover, .. } => album_cover, @@ -486,6 +491,7 @@ impl ListData { /// * Episode duration for podcasts /// * None for livestreams #[must_use] + #[inline] pub fn duration(&self) -> Option { match self { ListData::Song { duration, .. } | ListData::Episode { duration, .. } => Some(*duration), @@ -500,6 +506,7 @@ impl ListData { /// * Episodes - Track token for Deezer CDN /// * Livestreams - None (uses direct URLs) #[must_use] + #[inline] pub fn track_token(&self) -> Option<&str> { match self { ListData::Song { track_token, .. } | ListData::Episode { track_token, .. } => { @@ -516,6 +523,7 @@ impl ListData { /// * Episodes - Track token expiry /// * Livestreams - None (no token needed) #[must_use] + #[inline] pub fn expiry(&self) -> Option { match self { ListData::Song { expiry, .. } | ListData::Episode { expiry, .. } => Some(*expiry), @@ -552,6 +560,7 @@ pub struct LivestreamUrls { impl Deref for LivestreamUrls { type Target = LivestreamUrl; + #[inline] fn deref(&self) -> &Self::Target { &self.data } diff --git a/src/protocol/gateway/list_data/songs.rs b/src/protocol/gateway/list_data/songs.rs index ee4b692..2e5825c 100644 --- a/src/protocol/gateway/list_data/songs.rs +++ b/src/protocol/gateway/list_data/songs.rs @@ -99,6 +99,7 @@ pub struct SongData(pub ListData); impl Deref for SongData { type Target = ListData; + #[inline] fn deref(&self) -> &Self::Target { &self.0 } diff --git a/src/protocol/gateway/mod.rs b/src/protocol/gateway/mod.rs index 5bdf802..424224f 100644 --- a/src/protocol/gateway/mod.rs +++ b/src/protocol/gateway/mod.rs @@ -152,6 +152,7 @@ impl Response { /// } /// ``` #[must_use] + #[inline] pub fn first(&self) -> Option<&T> { self.all().first() } @@ -168,6 +169,7 @@ impl Response { /// } /// ``` #[must_use] + #[inline] pub fn all(&self) -> &Vec { match self { Self::Paginated { results, .. } => &results.data, diff --git a/src/protocol/gateway/user_data.rs b/src/protocol/gateway/user_data.rs index 387d495..dd476f9 100644 --- a/src/protocol/gateway/user_data.rs +++ b/src/protocol/gateway/user_data.rs @@ -116,6 +116,7 @@ pub struct MediaUrl(pub Url); impl Deref for MediaUrl { type Target = Url; + #[inline] fn deref(&self) -> &Self::Target { &self.0 } @@ -136,6 +137,7 @@ impl Deref for MediaUrl { /// let url: Url = media_url.into(); /// ``` impl From for Url { + #[inline] fn from(url: MediaUrl) -> Self { url.0 } @@ -159,6 +161,7 @@ impl From for Url { /// assert_eq!(url.as_str(), "https://media.deezer.com"); /// ``` impl Default for MediaUrl { + #[inline] fn default() -> Self { let media_url = Url::from_str("https://media.deezer.com").expect("invalid media url"); Self(media_url) @@ -248,6 +251,7 @@ pub struct Gatekeeps { /// assert!(flags.remote_control); /// ``` impl Default for Gatekeeps { + #[inline] fn default() -> Self { Self { remote_control: true, @@ -282,6 +286,7 @@ pub struct Gain { /// assert_eq!(gain.target, -15); /// ``` impl Default for Gain { + #[inline] fn default() -> Self { Self { target: -15 } } diff --git a/src/protocol/gateway/user_radio.rs b/src/protocol/gateway/user_radio.rs index 1a1d8d1..1881c27 100644 --- a/src/protocol/gateway/user_radio.rs +++ b/src/protocol/gateway/user_radio.rs @@ -72,6 +72,7 @@ pub struct UserRadio(pub ListData); impl Deref for UserRadio { type Target = ListData; + #[inline] fn deref(&self) -> &Self::Target { &self.0 } diff --git a/src/protocol/media.rs b/src/protocol/media.rs index 87343f3..27edf57 100644 --- a/src/protocol/media.rs +++ b/src/protocol/media.rs @@ -144,6 +144,7 @@ pub enum Type { /// assert_eq!(Type::PREVIEW.to_string(), "PREVIEW"); /// ``` impl fmt::Display for Type { + #[inline] fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{self:?}") } @@ -215,6 +216,7 @@ pub enum Cipher { /// assert_eq!(Cipher::NONE.to_string(), "NONE"); /// ``` impl fmt::Display for Cipher { + #[inline] fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{self:?}") } @@ -274,6 +276,7 @@ pub enum Format { /// assert_eq!(Format::FLAC.to_string(), "FLAC"); /// ``` impl fmt::Display for Format { + #[inline] fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{self:?}") } @@ -298,6 +301,7 @@ impl fmt::Display for Format { /// assert_eq!(AudioQuality::from(Format::EXTERNAL), AudioQuality::Unknown); /// ``` impl From for AudioQuality { + #[inline] fn from(format: Format) -> Self { match format { Format::MP3_64 => AudioQuality::Basic, @@ -396,6 +400,7 @@ pub struct Error { /// assert_eq!(error.to_string(), "Not found (404)"); /// ``` impl fmt::Display for Error { + #[inline] fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{} ({})", self.message, self.code) } diff --git a/src/proxy.rs b/src/proxy.rs index 3192a81..dcab4b7 100644 --- a/src/proxy.rs +++ b/src/proxy.rs @@ -80,6 +80,7 @@ impl Http { /// let proxy = Http::from_env(); /// ``` #[must_use] + #[inline] pub fn from_env() -> Option { let proxy = env::var("HTTPS_PROXY") .or_else(|_| env::var("https_proxy")) @@ -242,6 +243,7 @@ impl FromStr for Http { /// Note: Authentication credentials are not included /// in the output for security. impl Display for Http { + #[inline] fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "{}", self.url) } diff --git a/src/remote.rs b/src/remote.rs index c839873..07058c3 100644 --- a/src/remote.rs +++ b/src/remote.rs @@ -258,15 +258,19 @@ enum InitialVolume { /// Calculates a future time instant by adding seconds to now. /// +/// Used for scheduling timers and watchdogs. Handles overflow +/// by returning None if addition would overflow. +/// /// # Arguments /// /// * `seconds` - Duration to add to current time /// /// # Returns /// -/// * Some(Instant) - Future time if addition succeeds -/// * None - If addition would overflow +/// * `Some(Instant)` - Future time if addition succeeds +/// * `None` - If addition would overflow #[must_use] +#[inline] fn from_now(seconds: Duration) -> Option { tokio::time::Instant::now().checked_add(seconds) } @@ -791,14 +795,17 @@ impl Client { } } - /// Checks whether the current queue is a Flow (personalized radio) queue. + /// Returns whether current queue is a Flow (personalized radio). /// - /// Examines the queue context to determine if it represents a personalized radio stream. + /// Examines queue context to identify Flow queues by checking: + /// * Queue has contexts + /// * First context is a user mix /// /// # Returns /// - /// * `true` - Current queue is a Flow queue - /// * `false` - Current queue is not Flow or no queue exists + /// * `true` - Queue is a Flow queue + /// * `false` - Queue is not Flow or no queue exists + #[inline] fn is_flow(&self) -> bool { self.queue.as_ref().is_some_and(|queue| { queue @@ -816,6 +823,7 @@ impl Client { /// Resets the receive watchdog timer. /// /// Called when messages are received from the controller to prevent connection timeout. + #[inline] fn reset_watchdog_rx(&mut self) { if let Some(deadline) = from_now(Self::WATCHDOG_RX_TIMEOUT) { self.watchdog_rx.as_mut().reset(deadline); @@ -825,6 +833,7 @@ impl Client { /// Resets the transmit watchdog timer. /// /// Called when messages are sent to the controller to maintain heartbeat timing. + #[inline] fn reset_watchdog_tx(&mut self) { if let Some(deadline) = from_now(Self::WATCHDOG_TX_TIMEOUT) { self.watchdog_tx.as_mut().reset(deadline); @@ -834,6 +843,7 @@ impl Client { /// Resets the playback reporting timer. /// /// Schedules the next progress report according to the reporting interval. + #[inline] fn reset_reporting_timer(&mut self) { if let Some(deadline) = from_now(Self::REPORTING_INTERVAL) { self.reporting_timer.as_mut().reset(deadline); @@ -1084,6 +1094,7 @@ impl Client { /// * true - Connected to controller /// * false - Not connected #[must_use] + #[inline] fn is_connected(&self) -> bool { if let ConnectionState::Connected { .. } = &self.connection_state { return true; @@ -1094,10 +1105,16 @@ impl Client { /// Returns ID of currently connected controller if any. /// + /// Checks both active connections and pending connections: + /// * Returns controller ID from active connection if present + /// * Returns controller ID from pending connection if no active connection + /// * Returns None if no controller connection exists + /// /// # Returns /// - /// * Some(DeviceId) - ID of connected controller - /// * None - No controller connected + /// * `Some(DeviceId)` - ID of connected or connecting controller + /// * `None` - No controller connection exists + #[inline] fn controller(&self) -> Option { if let ConnectionState::Connected { controller, .. } = &self.connection_state { return Some(controller.clone()); @@ -1628,6 +1645,18 @@ impl Client { } } + /// Sets the current playback position in the queue. + /// + /// Handles position conversion for shuffled queues: + /// * For unshuffled queues - Uses position directly + /// * For shuffled queues - Maps position through shuffle order + /// + /// # Arguments + /// + /// * `position` - Target position in the queue (in display order) + /// + /// After position calculation, updates the player's actual queue position. + #[inline] fn set_position(&mut self, position: usize) { let mut position = position; if let Some(queue) = self.queue.as_ref() { @@ -2221,6 +2250,7 @@ impl Client { /// /// Returns unspecified ID if no user token available. #[must_use] + #[inline] fn user_id(&self) -> UserId { self.user_token .as_ref() @@ -2241,6 +2271,7 @@ impl Client { /// /// Channel descriptor for protocol messages #[must_use] + #[inline] fn channel(&self, ident: Ident) -> Channel { let user_id = self.user_id(); let from = if let Ident::UserFeed(_) = ident { diff --git a/src/signal.rs b/src/signal.rs index 5429583..670d382 100644 --- a/src/signal.rs +++ b/src/signal.rs @@ -117,6 +117,7 @@ impl Handler { /// * "SIGTERM" for [`ShutdownSignal::Terminate`] /// * "SIGHUP" for [`ShutdownSignal::Reload`] impl fmt::Display for ShutdownSignal { + #[inline] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { ShutdownSignal::Interrupt => write!(f, "Ctrl+C"), diff --git a/src/tokens.rs b/src/tokens.rs index 998a064..d615a28 100644 --- a/src/tokens.rs +++ b/src/tokens.rs @@ -129,6 +129,7 @@ impl UserToken { /// println!("Token valid for: {:?}", token.time_to_live()); /// ``` #[must_use] + #[inline] pub fn time_to_live(&self) -> Duration { self.expires_at .duration_since(SystemTime::now()) @@ -162,6 +163,7 @@ impl UserToken { /// } /// ``` #[must_use] + #[inline] pub fn is_expired(&self) -> bool { self.expires_at <= SystemTime::now() } @@ -187,6 +189,7 @@ impl UserToken { /// assert_eq!(token.to_string(), "secret_token"); /// ``` impl fmt::Display for UserToken { + #[inline] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}", self.token) } diff --git a/src/track.rs b/src/track.rs index dcf6fbb..7c8745b 100644 --- a/src/track.rs +++ b/src/track.rs @@ -124,6 +124,7 @@ pub enum ExternalUrl { /// Display implementation for track type. impl fmt::Display for TrackType { + #[inline] fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { Self::Song => write!(f, "song"), @@ -310,6 +311,7 @@ pub enum MediumType { impl Deref for MediumType { type Target = Medium; + #[inline] fn deref(&self) -> &Self::Target { match self { Self::Primary(medium) | Self::Fallback(medium) => medium, @@ -339,6 +341,7 @@ impl Track { /// Returns the track's unique identifier. #[must_use] + #[inline] pub fn id(&self) -> TrackId { self.id } @@ -348,6 +351,7 @@ impl Track { /// The duration represents the total playback time of the track. /// No duration is available for livestreams. #[must_use] + #[inline] pub fn duration(&self) -> Option { self.duration } @@ -357,12 +361,14 @@ impl Track { /// Always true for songs. Episodes and livestreams may be /// region-restricted or temporarily unavailable. #[must_use] + #[inline] pub fn available(&self) -> bool { self.available } /// Returns the track type. #[must_use] + #[inline] pub fn typ(&self) -> TrackType { self.typ } @@ -374,24 +380,28 @@ impl Track { /// * Negative values indicate track is louder than reference /// * None indicates no gain information available #[must_use] + #[inline] pub fn gain(&self) -> Option { self.gain } /// Returns the track title. #[must_use] + #[inline] pub fn title(&self) -> Option<&str> { self.title.as_deref() } /// Returns the track artist name. #[must_use] + #[inline] pub fn artist(&self) -> &str { &self.artist } /// Returns the album title for this track. #[must_use] + #[inline] pub fn album_title(&self) -> Option<&str> { self.album_title.as_deref() } @@ -420,6 +430,7 @@ impl Track { /// https://e-cdns-images.dzcdn.net/images/cover/f286f9e7dc818e181c37b944e2461101/500x500.jpg /// ``` #[must_use] + #[inline] pub fn cover_id(&self) -> &str { &self.cover_id } @@ -429,6 +440,7 @@ impl Track { /// After this time, the track becomes unavailable for download /// and may need token refresh. #[must_use] + #[inline] pub fn expiry(&self) -> Option { self.expiry } @@ -440,6 +452,7 @@ impl Track { /// * Progress always reports 100% /// * Multiple quality/codec options #[must_use] + #[inline] pub fn is_livestream(&self) -> bool { self.typ == TrackType::Livestream } @@ -469,12 +482,14 @@ impl Track { /// Returns the track's audio quality. #[must_use] + #[inline] pub fn quality(&self) -> AudioQuality { self.quality } /// Returns the encryption cipher used for this track. #[must_use] + #[inline] pub fn cipher(&self) -> Cipher { self.cipher } @@ -483,6 +498,7 @@ impl Track { /// /// True if the track uses any cipher other than NONE. #[must_use] + #[inline] pub fn is_encrypted(&self) -> bool { self.cipher != Cipher::NONE } @@ -492,6 +508,7 @@ impl Track { /// True only for FLAC encoded songs. Episodes and livestreams /// are never lossless. #[must_use] + #[inline] pub fn is_lossless(&self) -> bool { self.codec().is_some_and(|codec| codec == Codec::FLAC) } @@ -1045,6 +1062,7 @@ impl Track { /// * Download hasn't started /// * Download was reset #[must_use] + #[inline] pub fn handle(&self) -> Option { self.handle.clone() } @@ -1057,6 +1075,7 @@ impl Track { /// For livestreams, always returns false since they are continuous /// streams that can't be fully buffered. #[must_use] + #[inline] pub fn is_complete(&self) -> bool { self.duration == self.buffered() } @@ -1087,6 +1106,7 @@ impl Track { /// Size becomes available after download starts and server /// provides Content-Length. #[must_use] + #[inline] pub fn file_size(&self) -> Option { self.file_size } @@ -1098,6 +1118,7 @@ impl Track { /// * Are never encrypted /// * Include episodes and livestreams #[must_use] + #[inline] pub fn is_external(&self) -> bool { self.external } @@ -1110,6 +1131,7 @@ impl Track { /// * Stream-specific (livestreams) /// * Unknown (some external content) #[must_use] + #[inline] pub fn bitrate(&self) -> Option { self.bitrate } @@ -1121,6 +1143,7 @@ impl Track { /// * FLAC - High quality songs only /// * AAC - Some livestreams and episodes #[must_use] + #[inline] pub fn codec(&self) -> Option { self.codec } diff --git a/src/util.rs b/src/util.rs index dbeb5f2..0609790 100644 --- a/src/util.rs +++ b/src/util.rs @@ -49,6 +49,7 @@ pub trait ToF32 { /// assert!(clamped == f32::MAX); /// ``` impl ToF32 for f64 { + #[inline] #[expect(clippy::cast_possible_truncation)] fn to_f32_lossy(self) -> f32 { self.clamp(f64::from(f32::MIN), f64::from(f32::MAX)) as f32 @@ -71,6 +72,7 @@ impl ToF32 for f64 { /// assert!(clamped == f32::MAX); /// ``` impl ToF32 for u64 { + #[inline] #[expect(clippy::cast_possible_truncation)] #[expect(clippy::cast_precision_loss)] #[expect(clippy::cast_sign_loss)] @@ -99,6 +101,7 @@ impl ToF32 for u64 { /// assert!(clamped == f32::MAX); /// ``` impl ToF32 for u128 { + #[inline] #[expect(clippy::cast_possible_truncation)] #[expect(clippy::cast_precision_loss)] #[expect(clippy::cast_sign_loss)] @@ -127,6 +130,7 @@ impl ToF32 for u128 { /// assert!(clamped == f32::MAX); /// ``` impl ToF32 for usize { + #[inline] #[expect(clippy::cast_possible_truncation)] #[expect(clippy::cast_precision_loss)] #[expect(clippy::cast_sign_loss)] diff --git a/src/uuid.rs b/src/uuid.rs index 62dd3fa..dd1a166 100644 --- a/src/uuid.rs +++ b/src/uuid.rs @@ -45,6 +45,7 @@ pub struct Uuid(pub uuid::Uuid); impl Deref for Uuid { type Target = uuid::Uuid; + #[inline] fn deref(&self) -> &Self::Target { &self.0 } @@ -67,6 +68,7 @@ impl Uuid { /// println!("{}", uuid); // Prints a UUID like "550e8400-e29b-41d4-a716-446655440000" /// ``` #[must_use] + #[inline] pub fn fast_v4() -> Self { let random_bytes = fastrand::u128(..).to_ne_bytes(); let uuid = uuid::Builder::from_random_bytes(random_bytes).into_uuid(); @@ -85,6 +87,7 @@ impl Uuid { /// println!("{}", uuid); // e.g., "550e8400-e29b-41d4-a716-446655440000" /// ``` impl fmt::Display for Uuid { + #[inline] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { self.0.fmt(f) } @@ -126,6 +129,7 @@ impl FromStr for Uuid { /// let std_uuid: uuid::Uuid = our_uuid.into(); /// ``` impl From for uuid::Uuid { + #[inline] fn from(value: Uuid) -> Self { *value }