ogg_pager/
packets.rs

1use crate::error::{PageError, Result};
2use crate::header::PageHeader;
3use crate::paginate::paginate;
4use crate::Page;
5
6use std::fmt::{Debug, Formatter};
7use std::io::{Read, Seek, Write};
8
9/// A container for packets in an OGG file
10pub struct Packets {
11	content: Vec<u8>,
12	packet_sizes: Vec<u64>,
13}
14
15impl Packets {
16	/// Read as many packets as possible from a reader
17	///
18	/// # Errors
19	///
20	/// A page has a bad length
21	///
22	/// # Examples
23	///
24	/// ```rust
25	/// use ogg_pager::Packets;
26	///
27	/// # fn main() -> Result<(), ogg_pager::PageError> {
28	/// # let path = "../lofty/tests/files/assets/minimal/full_test.ogg";
29	/// let mut file = std::fs::File::open(path)?;
30	///
31	/// let packets = Packets::read(&mut file)?;
32	/// # Ok(()) }
33	/// ```
34	pub fn read<R>(data: &mut R) -> Result<Self>
35	where
36		R: Read + Seek,
37	{
38		Self::read_count(data, -1)
39	}
40
41	/// Read a specific number of packets from a reader
42	///
43	/// A special value of `-1` will read as many packets as possible,
44	/// in which case [`Packets::read`] should be used.
45	///
46	/// NOTE: Any value 0 or below will return an empty [`Packets`]
47	///
48	/// # Errors
49	///
50	/// * Unable to read the specified number of packets
51	/// * A page has a bad length
52	///
53	/// # Examples
54	///
55	/// ```rust
56	/// use ogg_pager::Packets;
57	///
58	/// # fn main() -> Result<(), ogg_pager::PageError> {
59	/// # let path = "../lofty/tests/files/assets/minimal/full_test.ogg";
60	/// let mut file = std::fs::File::open(path)?;
61	///
62	/// // We know that the file has at least 2 packets in it
63	/// let packets = Packets::read_count(&mut file, 2)?;
64	/// # Ok(()) }
65	/// ```
66	#[allow(clippy::read_zero_byte_vec)]
67	pub fn read_count<R>(data: &mut R, count: isize) -> Result<Self>
68	where
69		R: Read + Seek,
70	{
71		let mut content = Vec::new();
72		let mut packet_sizes = Vec::new();
73
74		if count == 0 || count < -1 {
75			return Ok(Self {
76				content,
77				packet_sizes,
78			});
79		}
80
81		let mut read = 0;
82
83		let mut packet_size = 0_u64;
84		let mut packet_bytes_already_read = None;
85		let mut current_packet_content;
86		'outer: loop {
87			if let Ok(header) = PageHeader::read(data) {
88				for i in header.segments {
89					packet_size += u64::from(i);
90
91					if i < 255 {
92						if count != -1 {
93							read += 1;
94						}
95
96						let byte_count_to_read = Self::get_byte_count_to_read(
97							packet_size,
98							&mut packet_bytes_already_read,
99						);
100
101						current_packet_content = vec![0; byte_count_to_read as usize];
102						data.read_exact(&mut current_packet_content)?;
103
104						packet_sizes.push(packet_size);
105						packet_size = 0;
106						packet_bytes_already_read = None;
107
108						content.append(&mut current_packet_content);
109
110						if read == count {
111							break 'outer;
112						}
113					}
114				}
115
116				// The packet continues on the next page, write what we can so far
117				if packet_size != 0 {
118					let byte_count_to_read =
119						Self::get_byte_count_to_read(packet_size, &mut packet_bytes_already_read);
120
121					current_packet_content = vec![0; byte_count_to_read as usize];
122					data.read_exact(&mut current_packet_content)?;
123					content.append(&mut current_packet_content);
124				}
125
126				continue;
127			}
128
129			break;
130		}
131
132		if count != -1 && packet_sizes.len() != count as usize {
133			return Err(PageError::NotEnoughData);
134		}
135
136		Ok(Self {
137			content,
138			packet_sizes,
139		})
140	}
141
142	fn get_byte_count_to_read(
143		packet_size: u64,
144		packet_bytes_already_read: &mut Option<u64>,
145	) -> u64 {
146		let byte_count_to_read;
147		match packet_bytes_already_read {
148			Some(already_read_bytes_count) => {
149				byte_count_to_read = packet_size - *already_read_bytes_count;
150				*packet_bytes_already_read = Some(*already_read_bytes_count + byte_count_to_read);
151			},
152			None => {
153				byte_count_to_read = packet_size;
154				*packet_bytes_already_read = Some(packet_size);
155			},
156		};
157
158		byte_count_to_read
159	}
160
161	/// Returns the number of packets
162	///
163	/// # Examples
164	///
165	/// ```rust
166	/// use ogg_pager::Packets;
167	///
168	/// # fn main() -> Result<(), ogg_pager::PageError> {
169	/// # let path = "../lofty/tests/files/assets/minimal/full_test.ogg";
170	/// let mut file = std::fs::File::open(path)?;
171	///
172	/// // I want to read 2 packets
173	/// let packets = Packets::read_count(&mut file, 2)?;
174	///
175	/// // And that's what I received!
176	/// assert_eq!(packets.len(), 2);
177	/// # Ok(()) }
178	/// ```
179	pub fn len(&self) -> usize {
180		self.packet_sizes.len()
181	}
182
183	/// Returns true if there are no packets
184	///
185	/// # Examples
186	///
187	/// ```rust
188	/// use ogg_pager::Packets;
189	///
190	/// # fn main() -> Result<(), ogg_pager::PageError> {
191	/// # let path = "../lofty/tests/files/assets/minimal/full_test.ogg";
192	/// let mut file = std::fs::File::open(path)?;
193	///
194	/// let packets = Packets::read(&mut file)?;
195	///
196	/// // My file contains packets!
197	/// assert!(!packets.is_empty());
198	/// # Ok(()) }
199	/// ```
200	pub fn is_empty(&self) -> bool {
201		self.packet_sizes.is_empty()
202	}
203
204	/// Gets the packet at a specified index, returning its contents
205	///
206	/// NOTES:
207	///
208	/// * This is zero-indexed
209	/// * If the index is out of bounds, it will return [`None`]
210	///
211	/// # Examples
212	///
213	/// ```rust
214	/// use ogg_pager::Packets;
215	///
216	/// # fn main() -> Result<(), ogg_pager::PageError> {
217	/// # let path = "../lofty/tests/files/assets/minimal/full_test.ogg";
218	/// let mut file = std::fs::File::open(path)?;
219	///
220	/// let packets = Packets::read(&mut file)?;
221	///
222	/// let first_packet = packets.get(0);
223	/// assert!(first_packet.is_some());
224	///
225	/// let out_of_bounds = packets.get(1000000);
226	/// assert!(out_of_bounds.is_none());
227	/// # Ok(()) }
228	/// ```
229	pub fn get(&self, idx: usize) -> Option<&[u8]> {
230		if idx >= self.content.len() {
231			return None;
232		}
233
234		let start_pos = match idx {
235			// Packet 0 starts at pos 0
236			0 => 0,
237			// Anything else we have to get the size of the previous packet
238			other => self.packet_sizes[other - 1] as usize,
239		};
240
241		if let Some(packet_size) = self.packet_sizes.get(idx) {
242			return Some(&self.content[start_pos..start_pos + *packet_size as usize]);
243		}
244
245		None
246	}
247
248	/// Sets the packet content, if it exists
249	///
250	/// NOTES:
251	///
252	/// * This is zero-indexed
253	/// * If the index is out of bounds, it will return `false`
254	///
255	/// # Examples
256	///
257	/// ```rust
258	/// use ogg_pager::Packets;
259	///
260	/// # fn main() -> Result<(), ogg_pager::PageError> {
261	/// # let path = "../lofty/tests/files/assets/minimal/full_test.ogg";
262	/// let mut file = std::fs::File::open(path)?;
263	///
264	/// let mut packets = Packets::read(&mut file)?;
265	///
266	/// let new_content = [0; 100];
267	///
268	/// assert_ne!(packets.get(0), Some(new_content.as_slice()));
269	///
270	/// // Set our new content
271	/// assert!(packets.set(0, new_content));
272	///
273	/// // Now our packet contains the new content
274	/// assert_eq!(packets.get(0), Some(new_content.as_slice()));
275	///
276	/// // We cannot index out of bounds
277	/// assert!(!packets.set(1000000, new_content));
278	/// # Ok(()) }
279	/// ```
280	pub fn set(&mut self, idx: usize, content: impl Into<Vec<u8>>) -> bool {
281		if idx >= self.packet_sizes.len() {
282			return false;
283		}
284
285		let start_pos = match idx {
286			// Packet 0 starts at pos 0
287			0 => 0,
288			// Anything else we have to get the size of the previous packet
289			other => self.packet_sizes[other - 1] as usize,
290		};
291
292		let content = content.into();
293		let content_size = content.len();
294
295		let end_pos = start_pos + self.packet_sizes[idx] as usize;
296		self.content.splice(start_pos..end_pos, content);
297
298		self.packet_sizes[idx] = content_size as u64;
299
300		true
301	}
302
303	/// Returns an iterator over the packets
304	///
305	/// # Examples
306	///
307	/// ```rust
308	/// use ogg_pager::Packets;
309	///
310	/// # fn main() -> Result<(), ogg_pager::PageError> {
311	/// # let path = "../lofty/tests/files/assets/minimal/full_test.ogg";
312	/// let mut file = std::fs::File::open(path)?;
313	///
314	/// let packets = Packets::read(&mut file)?;
315	///
316	/// for packet in packets.iter() {
317	/// 	println!("Packet size: {}", packet.len());
318	/// }
319	/// # Ok(()) }
320	pub fn iter(&self) -> PacketsIter<'_> {
321		<&Self as IntoIterator>::into_iter(self)
322	}
323
324	/// Convert the packets into a stream of pages
325	///
326	/// See [paginate()] for more information.
327	///
328	/// # Errors
329	///
330	/// See [`paginate()`]
331	///
332	/// # Examples
333	///
334	/// ```rust
335	/// use ogg_pager::{Packets, CONTAINS_FIRST_PAGE_OF_BITSTREAM, CONTAINS_LAST_PAGE_OF_BITSTREAM};
336	///
337	/// # fn main() -> Result<(), ogg_pager::PageError> {
338	/// # let path = "../lofty/tests/files/assets/minimal/full_test.ogg";
339	/// let mut file = std::fs::File::open(path)?;
340	///
341	/// let mut packets = Packets::read(&mut file)?;
342	///
343	/// let stream_serial_number = 1234;
344	/// let absolute_granule_position = 0;
345	/// let flags = CONTAINS_FIRST_PAGE_OF_BITSTREAM | CONTAINS_LAST_PAGE_OF_BITSTREAM;
346	///
347	/// let pages = packets.paginate(stream_serial_number, absolute_granule_position, flags)?;
348	///
349	/// println!("We created {} pages!", pages.len());
350	/// # Ok(()) }
351	/// ```
352	pub fn paginate(&self, stream_serial: u32, abgp: u64, flags: u8) -> Result<Vec<Page>> {
353		let mut packets = Vec::new();
354
355		let mut pos = 0;
356		for packet_size in self.packet_sizes.iter().copied() {
357			packets.push(&self.content[pos..pos + packet_size as usize]);
358			pos += packet_size as usize;
359		}
360
361		paginate(packets, stream_serial, abgp, flags)
362	}
363
364	/// Write packets to a writer
365	///
366	/// This will paginate and write all of the packets to a writer.
367	///
368	/// # Errors
369	///
370	/// * Unable to write, see [`std::io::Error`]
371	///
372	/// # Examples
373	///
374	/// ```rust,no_run
375	/// use ogg_pager::{Packets, CONTAINS_FIRST_PAGE_OF_BITSTREAM, CONTAINS_LAST_PAGE_OF_BITSTREAM};
376	/// use std::fs::OpenOptions;
377	///
378	/// # fn main() -> Result<(), ogg_pager::PageError> {
379	/// let mut file = std::fs::File::open("foo.ogg")?;
380	///
381	/// let mut packets = Packets::read(&mut file)?;
382	///
383	/// let stream_serial_number = 1234;
384	/// let absolute_granule_position = 0;
385	/// let flags = CONTAINS_FIRST_PAGE_OF_BITSTREAM | CONTAINS_LAST_PAGE_OF_BITSTREAM;
386	///
387	/// let mut new_file = OpenOptions::new().write(true).open("bar.ogg")?;
388	/// let pages_written = packets.write_to(
389	/// 	&mut new_file,
390	/// 	stream_serial_number,
391	/// 	absolute_granule_position,
392	/// 	flags,
393	/// )?;
394	///
395	/// println!("We wrote {} pages!", pages_written);
396	/// # Ok(()) }
397	/// ```
398	pub fn write_to<W>(
399		&self,
400		writer: &mut W,
401		stream_serial: u32,
402		abgp: u64,
403		flags: u8,
404	) -> Result<usize>
405	where
406		W: Write,
407	{
408		let paginated = self.paginate(stream_serial, abgp, flags)?;
409		let num_pages = paginated.len();
410
411		for mut page in paginated {
412			page.gen_crc();
413			writer.write_all(&page.as_bytes())?;
414		}
415
416		Ok(num_pages)
417	}
418}
419
420/// An iterator over packets
421///
422/// This is created by calling `into_iter` on [`Packets`]
423#[derive(Clone, PartialEq, Eq, Debug)]
424pub struct PacketsIter<'a> {
425	content: &'a [u8],
426	packet_sizes: &'a [u64],
427	cap: usize,
428}
429
430impl<'a> Iterator for PacketsIter<'a> {
431	type Item = &'a [u8];
432
433	fn next(&mut self) -> Option<Self::Item> {
434		if self.cap == 0 {
435			return None;
436		}
437
438		let packet_size = self.packet_sizes[0];
439
440		self.cap -= 1;
441		self.packet_sizes = &self.packet_sizes[1..];
442
443		let (ret, remaining) = self.content.split_at(packet_size as usize);
444		self.content = remaining;
445
446		Some(ret)
447	}
448}
449
450impl<'a> IntoIterator for &'a Packets {
451	type Item = &'a [u8];
452	type IntoIter = PacketsIter<'a>;
453
454	fn into_iter(self) -> Self::IntoIter {
455		PacketsIter {
456			content: &self.content,
457			packet_sizes: &self.packet_sizes,
458			cap: self.packet_sizes.len(),
459		}
460	}
461}
462
463impl Debug for Packets {
464	fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
465		f.debug_struct("Packets")
466			.field("total_bytes", &self.content.len())
467			.field("count", &self.packet_sizes.len())
468			.finish()
469	}
470}