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}