diff --git a/CHANGELOG.md b/CHANGELOG.md index 6a14a7b5..22027d12 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - **A proc macro for file creation**: - With the new `lofty_attr` crate, file creation has been simplified significantly. It is available for both internal and external usage. +- **ID3v2**: Exposed internal functions `id3::v2::util::{synch_u32, unsynch_u32}` ### Changed - **TaggedFile**: `tag{_mut}` no longer takes a reference to `TagType` diff --git a/src/file.rs b/src/file.rs index bcc4e712..4dd6ddac 100644 --- a/src/file.rs +++ b/src/file.rs @@ -699,7 +699,7 @@ impl FileType { // TODO: APE tags in the beginning of the file pub(crate) fn from_buffer_inner(buf: &[u8]) -> (Option, Option) { - use crate::id3::v2::unsynch_u32; + use crate::id3::v2::util::unsynch_u32; // Start out with an empty return: (File type, id3 size) // Only one can be set diff --git a/src/id3/v2/frame/header.rs b/src/id3/v2/frame/header.rs index a1937d53..85a3cae1 100644 --- a/src/id3/v2/frame/header.rs +++ b/src/id3/v2/frame/header.rs @@ -80,7 +80,7 @@ where // unsynch the frame size if necessary if synchsafe { - size = crate::id3::v2::unsynch_u32(size); + size = crate::id3::v2::util::unsynch_u32(size); } let frame_id = FrameID::new(id_str)?; diff --git a/src/id3/v2/mod.rs b/src/id3/v2/mod.rs index a64d76b3..3ada7371 100644 --- a/src/id3/v2/mod.rs +++ b/src/id3/v2/mod.rs @@ -8,10 +8,11 @@ //! * [Frame] mod flags; -pub(crate) mod util; +pub mod util; use crate::error::{ID3v2Error, ID3v2ErrorKind, Result}; use crate::macros::err; +use util::unsynch_u32; use std::io::Read; @@ -66,24 +67,6 @@ pub enum ID3v2Version { V4, } -// https://github.com/polyfloyd/rust-id3/blob/e142ec656bf70a8153f6e5b34a37f26df144c3c1/src/stream/unsynch.rs#L18-L20 -pub(crate) fn unsynch_u32(n: u32) -> u32 { - n & 0xFF | (n & 0xFF00) >> 1 | (n & 0xFF_0000) >> 2 | (n & 0xFF00_0000) >> 3 -} - -#[cfg(feature = "id3v2")] -// https://github.com/polyfloyd/rust-id3/blob/e142ec656bf70a8153f6e5b34a37f26df144c3c1/src/stream/unsynch.rs#L9-L15 -pub(crate) fn synch_u32(n: u32) -> Result { - if n > 0x1000_0000 { - err!(TooMuchData); - } - - let mut x: u32 = n & 0x7F | (n & 0xFFFF_FF80) << 1; - x = x & 0x7FFF | (x & 0xFFFF_8000) << 1; - x = x & 0x7F_FFFF | (x & 0xFF80_0000) << 1; - Ok(x) -} - #[derive(Copy, Clone)] pub(crate) struct ID3v2Header { #[cfg(feature = "id3v2")] diff --git a/src/id3/v2/util/mod.rs b/src/id3/v2/util/mod.rs index 7427c712..d446afed 100644 --- a/src/id3/v2/util/mod.rs +++ b/src/id3/v2/util/mod.rs @@ -1,53 +1,84 @@ +//! Utilities for working with ID3v2 tags + pub(crate) mod text_utils; -#[cfg(feature = "id3v2")] -pub(crate) mod upgrade; -#[cfg(feature = "id3v2")] -use crate::error::{ID3v2Error, ID3v2ErrorKind, Result}; +cfg_if::cfg_if! { + if #[cfg(feature = "id3v2")] { + pub(crate) mod upgrade; -#[cfg(feature = "id3v2")] -pub(in crate::id3::v2) fn unsynch_content(content: &[u8]) -> Result> { - let mut unsynch_content = Vec::new(); + use crate::error::{ID3v2Error, ID3v2ErrorKind, Result}; - let mut discard = false; + pub(in crate::id3::v2) fn unsynch_content(content: &[u8]) -> Result> { + let mut unsynch_content = Vec::new(); - let mut i = 0; - let mut next = 0; - let content_len = content.len(); + let mut discard = false; - // Check for (0xFF, 0x00, 0x00), replace with (0xFF, 0x00) - while i < content_len && next < content_len { - // Verify the next byte is less than 0xE0 (0b111xxxxx) - // Then remove the next byte if it is a zero - if discard { - if content[next] >= 0xE0 { - return Err(ID3v2Error::new(ID3v2ErrorKind::Other( - "Encountered an invalid unsynchronisation", - )) - .into()); - } + let mut i = 0; + let mut next = 0; + let content_len = content.len(); + + // Check for (0xFF, 0x00, 0x00), replace with (0xFF, 0x00) + while i < content_len && next < content_len { + // Verify the next byte is less than 0xE0 (0b111xxxxx) + // Then remove the next byte if it is a zero + if discard { + if content[next] >= 0xE0 { + return Err(ID3v2Error::new(ID3v2ErrorKind::Other( + "Encountered an invalid unsynchronisation", + )) + .into()); + } + + if content[next] == 0 { + discard = false; + next += 1; + + continue; + } + } - if content[next] == 0 { discard = false; + + unsynch_content.push(content[next]); + + if content[next] == 0xFF { + discard = true + } + + i += 1; next += 1; - - continue; } + + Ok(unsynch_content) } - discard = false; + /// Create a synchsafe integer + /// + /// See [`FrameFlags::unsynchronisation`](crate::id3::v2::FrameFlags::unsynchronisation) for an explanation. + /// + /// # Errors + /// + /// `n` doesn't fit in 28 bits + // https://github.com/polyfloyd/rust-id3/blob/e142ec656bf70a8153f6e5b34a37f26df144c3c1/src/stream/unsynch.rs#L9-L15 + pub fn synch_u32(n: u32) -> Result { + if n > 0x1000_0000 { + crate::macros::err!(TooMuchData); + } - unsynch_content.push(content[next]); - - if content[next] == 0xFF { - discard = true + let mut x: u32 = n & 0x7F | (n & 0xFFFF_FF80) << 1; + x = x & 0x7FFF | (x & 0xFFFF_8000) << 1; + x = x & 0x7F_FFFF | (x & 0xFF80_0000) << 1; + Ok(x) } - - i += 1; - next += 1; } +} - Ok(unsynch_content) +/// Unsynchronise a synchsafe integer +/// +/// See [`FrameFlags::unsynchronisation`](crate::id3::v2::FrameFlags::unsynchronisation) for an explanation. +// https://github.com/polyfloyd/rust-id3/blob/e142ec656bf70a8153f6e5b34a37f26df144c3c1/src/stream/unsynch.rs#L18-L20 +pub fn unsynch_u32(n: u32) -> u32 { + n & 0xFF | (n & 0xFF00) >> 1 | (n & 0xFF_0000) >> 2 | (n & 0xFF00_0000) >> 3 } #[cfg(test)] diff --git a/src/id3/v2/write/frame.rs b/src/id3/v2/write/frame.rs index d0c18e39..cebe08cb 100644 --- a/src/id3/v2/write/frame.rs +++ b/src/id3/v2/write/frame.rs @@ -1,6 +1,6 @@ use crate::error::{ID3v2Error, ID3v2ErrorKind, Result}; use crate::id3::v2::frame::{FrameFlags, FrameRef, FrameValue}; -use crate::id3::v2::synch_u32; +use crate::id3::v2::util::synch_u32; use std::io::Write; diff --git a/src/id3/v2/write/mod.rs b/src/id3/v2/write/mod.rs index 541e0c7c..60da1000 100644 --- a/src/id3/v2/write/mod.rs +++ b/src/id3/v2/write/mod.rs @@ -6,8 +6,8 @@ use crate::error::Result; use crate::file::FileType; use crate::id3::find_id3v2; use crate::id3::v2::frame::FrameRef; -use crate::id3::v2::synch_u32; use crate::id3::v2::tag::Id3v2TagRef; +use crate::id3::v2::util::synch_u32; use crate::macros::err; use crate::probe::Probe;