lofty/id3/v2/util/
synchsafe.rs

1//! Utilities for working with unsynchronized ID3v2 content
2//!
3//! See [`FrameFlags::unsynchronisation`](crate::id3::v2::FrameFlags::unsynchronisation) for an explanation.
4
5use crate::error::Result;
6
7use std::io::Read;
8
9/// A reader for unsynchronized content
10///
11/// See [`FrameFlags::unsynchronisation`](crate::id3::v2::FrameFlags::unsynchronisation) for an explanation.
12///
13/// # Examples
14///
15/// ```rust
16/// use std::io::{Cursor, Read};
17/// use lofty::id3::v2::util::synchsafe::UnsynchronizedStream;
18///
19/// fn main() -> lofty::error::Result<()> {
20/// // The content has two `0xFF 0x00` pairs, which will be removed
21/// let content = [0xFF, 0x00, 0x1A, 0xFF, 0x00, 0x15];
22///
23/// let mut unsynchronized_reader = UnsynchronizedStream::new(Cursor::new(content));
24///
25/// let mut unsynchronized_content = Vec::new();
26/// unsynchronized_reader.read_to_end(&mut unsynchronized_content)?;
27///
28/// // All null bytes following `0xFF` have been removed
29/// assert_eq!(unsynchronized_content, [0xFF, 0x1A, 0xFF, 0x15]);
30/// # Ok(()) }
31/// ```
32pub struct UnsynchronizedStream<R> {
33	reader: R,
34	// Same buffer size as `BufReader`
35	buf: [u8; 8 * 1024],
36	bytes_available: usize,
37	pos: usize,
38	encountered_ff: bool,
39}
40
41impl<R> UnsynchronizedStream<R> {
42	/// Create a new [`UnsynchronizedStream`]
43	///
44	/// # Examples
45	///
46	/// ```rust
47	/// use lofty::id3::v2::util::synchsafe::UnsynchronizedStream;
48	/// use std::io::Cursor;
49	///
50	/// let reader = Cursor::new([0xFF, 0x00, 0x1A]);
51	/// let unsynchronized_reader = UnsynchronizedStream::new(reader);
52	/// ```
53	pub fn new(reader: R) -> Self {
54		Self {
55			reader,
56			buf: [0; 8 * 1024],
57			bytes_available: 0,
58			pos: 0,
59			encountered_ff: false,
60		}
61	}
62
63	/// Extract the reader, discarding the [`UnsynchronizedStream`]
64	///
65	/// # Examples
66	///
67	/// ```rust
68	/// use lofty::id3::v2::util::synchsafe::UnsynchronizedStream;
69	/// use std::io::Cursor;
70	///
71	/// # fn main() -> lofty::error::Result<()> {
72	/// let reader = Cursor::new([0xFF, 0x00, 0x1A]);
73	/// let unsynchronized_reader = UnsynchronizedStream::new(reader);
74	///
75	/// let reader = unsynchronized_reader.into_inner();
76	/// # Ok(()) }
77	/// ```
78	pub fn into_inner(self) -> R {
79		self.reader
80	}
81
82	/// Get a reference to the inner reader
83	///
84	/// # Examples
85	///
86	/// ```rust
87	/// use lofty::id3::v2::util::synchsafe::UnsynchronizedStream;
88	/// use std::io::Cursor;
89	///
90	/// # fn main() -> lofty::error::Result<()> {
91	/// let reader = Cursor::new([0xFF, 0x00, 0x1A]);
92	/// let unsynchronized_reader = UnsynchronizedStream::new(reader);
93	///
94	/// let reader = unsynchronized_reader.get_ref();
95	/// assert_eq!(reader.position(), 0);
96	/// # Ok(()) }
97	pub fn get_ref(&self) -> &R {
98		&self.reader
99	}
100}
101
102impl<R: Read> Read for UnsynchronizedStream<R> {
103	fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
104		let dest_len = buf.len();
105		if dest_len == 0 {
106			return Ok(0);
107		}
108
109		let mut dest_pos = 0;
110		loop {
111			if dest_pos == dest_len {
112				break;
113			}
114
115			if self.pos >= self.bytes_available {
116				self.bytes_available = self.reader.read(&mut self.buf)?;
117				self.pos = 0;
118			}
119
120			// Exhausted the reader
121			if self.bytes_available == 0 {
122				break;
123			}
124
125			if self.encountered_ff {
126				self.encountered_ff = false;
127
128				// Only skip the next byte if this is valid unsynchronization
129				// Otherwise just continue as normal
130				if self.buf[self.pos] == 0 {
131					self.pos += 1;
132					continue;
133				}
134			}
135
136			let current_byte = self.buf[self.pos];
137			buf[dest_pos] = current_byte;
138			dest_pos += 1;
139			self.pos += 1;
140
141			if current_byte == 0xFF {
142				self.encountered_ff = true;
143			}
144		}
145
146		Ok(dest_pos)
147	}
148}
149
150/// An integer that can be converted to and from synchsafe variants
151pub trait SynchsafeInteger: Sized {
152	/// The integer type that this can be widened to for use in [`SynchsafeInteger::widening_synch`]
153	type WideningType;
154
155	/// Create a synchsafe integer
156	///
157	/// See [`FrameFlags::unsynchronisation`](crate::id3::v2::FrameFlags::unsynchronisation) for an explanation.
158	///
159	/// # Errors
160	///
161	/// `self` doesn't fit in <`INTEGER_TYPE::BITS - size_of::<INTEGER_TYPE>()`> bits
162	///
163	/// # Examples
164	///
165	/// ```rust
166	/// use lofty::id3::v2::util::synchsafe::SynchsafeInteger;
167	///
168	/// # fn main() -> lofty::error::Result<()> {
169	/// // Maximum value we can represent in a synchsafe u32
170	/// let unsynch_number = 0xFFF_FFFF_u32;
171	/// let synch_number = unsynch_number.synch()?;
172	///
173	/// // Our synchronized number should be something completely different
174	/// assert_ne!(synch_number, unsynch_number);
175	///
176	/// // Each byte should have 7 set bits and an MSB of 0
177	/// assert_eq!(synch_number, 0b01111111_01111111_01111111_01111111_u32);
178	/// # Ok(()) }
179	/// ```
180	fn synch(self) -> Result<Self>;
181
182	/// Create a synchsafe integer, widening to the next available integer type
183	///
184	/// See [`FrameFlags::unsynchronisation`](crate::id3::v2::FrameFlags::unsynchronisation) for an explanation.
185	///
186	/// # Examples
187	///
188	/// ```rust
189	/// use lofty::id3::v2::util::synchsafe::SynchsafeInteger;
190	///
191	/// // 0b11111111
192	/// let large_number = u8::MAX;
193	///
194	/// // Widened to a u16
195	/// // 0b00000001_01111111
196	/// let large_number_synchsafe = large_number.widening_synch();
197	///
198	/// // Unsynchronizing the number will get us back to 255
199	/// assert_eq!(large_number_synchsafe.unsynch(), large_number as u16);
200	/// ```
201	fn widening_synch(self) -> Self::WideningType;
202
203	/// Unsynchronise a synchsafe integer
204	///
205	/// See [`FrameFlags::unsynchronisation`](crate::id3::v2::FrameFlags::unsynchronisation) for an explanation.
206	///
207	/// # Examples
208	///
209	/// ```rust
210	/// use lofty::id3::v2::util::synchsafe::SynchsafeInteger;
211	///
212	/// # fn main() -> lofty::error::Result<()> {
213	/// let unsynch_number = 0xFFF_FFFF_u32;
214	/// let synch_number = unsynch_number.synch()?;
215	///
216	/// // Our synchronized number should be something completely different
217	/// assert_ne!(synch_number, unsynch_number);
218	///
219	/// // Now, our re-unsynchronized number should match our original
220	/// let re_unsynch_number = synch_number.unsynch();
221	/// assert_eq!(re_unsynch_number, unsynch_number);
222	/// # Ok(()) }
223	/// ```
224	fn unsynch(self) -> Self;
225}
226
227macro_rules! impl_synchsafe {
228	(
229		$ty:ty, $widening_ty:ty,
230		synch($n:ident) $body:block;
231		widening_synch($w:ident) $widening_body:block;
232		unsynch($u:ident) $unsynch_body:block
233	) => {
234		#[allow(unused_parens)]
235		impl SynchsafeInteger for $ty {
236			type WideningType = $widening_ty;
237
238			fn synch(self) -> Result<Self> {
239				const MAXIMUM_INTEGER: $ty = {
240					let num_bytes = core::mem::size_of::<$ty>();
241					// 7 bits are available per byte, shave off 1 bit per byte
242					<$ty>::MAX >> num_bytes
243				};
244
245				if self > MAXIMUM_INTEGER {
246					crate::macros::err!(TooMuchData);
247				}
248
249				let $n = self;
250				Ok($body)
251			}
252
253			fn widening_synch(self) -> Self::WideningType {
254				let mut $w = <$widening_ty>::MIN;
255				let $n = self;
256				$widening_body;
257				$w
258			}
259
260			fn unsynch(self) -> Self {
261				let $u = self;
262				$unsynch_body
263			}
264		}
265	};
266}
267
268impl_synchsafe! {
269	u8, u16,
270	synch(n) {
271		(n & 0x7F)
272	};
273	widening_synch(w) {
274		w |= u16::from(n & 0x7F);
275		w |= u16::from(n & 0x80) << 1;
276	};
277	unsynch(u) {
278		(u & 0x7F)
279	}
280}
281
282impl_synchsafe! {
283	u16, u32,
284	synch(n) {
285		(n & 0x7F) |
286		((n & (0x7F << 7)) << 1)
287	};
288	widening_synch(w) {
289		w |= u32::from(n & 0x7F);
290		w |= u32::from((n & (0x7F << 7)) << 1);
291		w |= u32::from(n & (0x03 << 14)) << 2;
292	};
293	unsynch(u) {
294		((u & 0x7F00) >> 1) | (u & 0x7F)
295	}
296}
297
298impl_synchsafe! {
299	u32, u64,
300	synch(n) {
301		(n & 0x7F) |
302		((n & (0x7F << 7)) << 1) |
303		((n & (0x7F << 14)) << 2) |
304		((n & (0x7F << 21)) << 3)
305	};
306	widening_synch(w) {
307		w |= u64::from(n & 0x7F);
308		w |= u64::from(n & (0x7F << 7)) << 1;
309		w |= u64::from(n & (0x7F << 14)) << 2;
310		w |= u64::from(n & (0x7F << 21)) << 3;
311		w |= u64::from(n & (0x0F << 28)) << 4;
312	};
313	unsynch(u) {
314		((u & 0x7F00_0000) >> 3) | ((u & 0x7F_0000) >> 2) | ((u & 0x7F00) >> 1) | (u & 0x7F)
315	}
316}
317
318#[cfg(test)]
319mod tests {
320	const UNSYNCHRONIZED_CONTENT: &[u8] =
321		&[0xFF, 0x00, 0x00, 0xFF, 0x12, 0xB0, 0x05, 0xFF, 0x00, 0x00];
322	const EXPECTED: &[u8] = &[0xFF, 0x00, 0xFF, 0x12, 0xB0, 0x05, 0xFF, 0x00];
323
324	#[test_log::test]
325	fn unsynchronized_stream() {
326		let reader = Cursor::new(UNSYNCHRONIZED_CONTENT);
327		let mut unsynchronized_reader = UnsynchronizedStream::new(reader);
328
329		let mut final_content = Vec::new();
330		unsynchronized_reader
331			.read_to_end(&mut final_content)
332			.unwrap();
333
334		assert_eq!(final_content, EXPECTED);
335	}
336
337	#[test_log::test]
338	fn unsynchronized_stream_large() {
339		// Create a buffer >10k to force a buffer reset
340		let reader = Cursor::new(UNSYNCHRONIZED_CONTENT.repeat(1000));
341		let mut unsynchronized_reader = UnsynchronizedStream::new(reader);
342
343		let mut final_content = Vec::new();
344		unsynchronized_reader
345			.read_to_end(&mut final_content)
346			.unwrap();
347
348		// UNSYNCHRONIZED_CONTENT * 1000 should equal EXPECTED * 1000
349		assert_eq!(final_content, EXPECTED.repeat(1000));
350	}
351
352	#[test_log::test]
353	fn unsynchronized_stream_should_not_replace_unrelated() {
354		const ORIGINAL_CONTENT: &[u8] = &[0xFF, 0x1A, 0xFF, 0xC0, 0x10, 0x01];
355
356		let reader = Cursor::new(ORIGINAL_CONTENT);
357		let mut unsynchronized_reader = UnsynchronizedStream::new(reader);
358
359		let mut final_content = Vec::new();
360		unsynchronized_reader
361			.read_to_end(&mut final_content)
362			.unwrap();
363
364		assert_eq!(final_content, ORIGINAL_CONTENT);
365	}
366
367	use crate::id3::v2::util::synchsafe::{SynchsafeInteger, UnsynchronizedStream};
368	use std::io::{Cursor, Read};
369	macro_rules! synchsafe_integer_tests {
370		(
371			$($int:ty => {
372				synch: $original:literal, $new:literal;
373				unsynch: $original_unsync:literal, $new_unsynch:literal;
374				widen: $original_widen:literal, $new_widen:literal;
375			});+
376		) => {
377			$(
378				paste::paste! {
379					#[test_log::test]
380					fn [<$int _synch>]() {
381						assert_eq!($original.synch().unwrap(), $new);
382					}
383
384					#[test_log::test]
385					fn [<$int _unsynch>]() {
386						assert_eq!($original_unsync.unsynch(), $new_unsynch);
387					}
388
389					#[test_log::test]
390					fn [<$int _widen>]() {
391						assert_eq!($original_widen.widening_synch(), $new_widen);
392					}
393				}
394			)+
395		};
396	}
397
398	synchsafe_integer_tests! {
399		u8 => {
400			synch:   0x7F_u8, 0x7F_u8;
401			unsynch: 0x7F_u8, 0x7F_u8;
402			widen:   0xFF_u8, 0x017F_u16;
403		};
404		u16 => {
405			synch:   0x3FFF_u16, 0x7F7F_u16;
406			unsynch: 0x7F7F_u16, 0x3FFF_u16;
407			widen:   0xFFFF_u16, 0x0003_7F7F_u32;
408		};
409		u32 => {
410			synch:   0xFFF_FFFF_u32, 0x7F7F_7F7F_u32;
411			unsynch: 0x7F7F_7F7F_u32, 0xFFF_FFFF_u32;
412			widen:   0xFFFF_FFFF_u32, 0x000F_7F7F_7F7F_u64;
413		}
414	}
415}