mpeg_audio_header/
reader.rs

1// SPDX-FileCopyrightText: The mpeg-audio-header authors
2// SPDX-License-Identifier: MPL-2.0
3
4use std::{
5    io::{self, prelude::*},
6    time::Duration,
7};
8
9use crate::{
10    error::{Error, PositionalError},
11    PositionalResult,
12};
13
14/// Position within a readable source
15#[derive(Debug, Clone)]
16pub struct ReadPosition {
17    pub(crate) byte_offset: u64,
18    pub(crate) duration: Duration,
19}
20
21impl ReadPosition {
22    pub(crate) const fn new() -> Self {
23        Self {
24            byte_offset: 0,
25            duration: Duration::ZERO,
26        }
27    }
28
29    /// The number of bytes that have been consumed
30    #[must_use]
31    pub const fn byte_offset(&self) -> u64 {
32        self.byte_offset
33    }
34
35    /// The accumulated duration since the start of the stream
36    #[must_use]
37    pub const fn duration(&self) -> Duration {
38        self.duration
39    }
40}
41
42pub(crate) struct Reader<'r, T> {
43    reader: &'r mut T,
44    position: ReadPosition,
45}
46
47impl<'r, T: Read> Reader<'r, T> {
48    #[must_use]
49    pub(crate) fn new(reader: &'r mut T) -> Self {
50        Reader {
51            reader,
52            position: ReadPosition::new(),
53        }
54    }
55
56    fn read_exact(&mut self, buffer: &mut [u8]) -> PositionalResult<()> {
57        self.reader
58            .read_exact(buffer)
59            .map(|()| {
60                self.position.byte_offset += buffer.len() as u64;
61            })
62            .map_err(|e| self.positional_error(e.into()))
63    }
64
65    pub(crate) fn try_read_exact_until_eof(&mut self, buffer: &mut [u8]) -> PositionalResult<bool> {
66        self.read_exact(buffer).map(|()| true).or_else(|err| {
67            if err.is_unexpected_eof() {
68                Ok(false)
69            } else {
70                Err(err)
71            }
72        })
73    }
74
75    fn skip(&mut self, max_bytes: u64) -> PositionalResult<u64> {
76        match io::copy(&mut self.reader.take(max_bytes), &mut io::sink()) {
77            Err(e) => Err(self.positional_error(e.into())),
78            Ok(num_bytes_skipped) => {
79                debug_assert!(num_bytes_skipped <= max_bytes);
80                self.position.byte_offset += num_bytes_skipped;
81                Ok(num_bytes_skipped)
82            }
83        }
84    }
85
86    pub(crate) fn try_skip_exact_until_eof(&mut self, num_bytes: u64) -> PositionalResult<bool> {
87        match self.skip(num_bytes) {
88            Ok(skipped_bytes) => {
89                debug_assert!(skipped_bytes <= num_bytes);
90                Ok(skipped_bytes == num_bytes)
91            }
92            Err(err) => {
93                if err.is_unexpected_eof() {
94                    Ok(false)
95                } else {
96                    Err(err)
97                }
98            }
99        }
100    }
101
102    #[must_use]
103    pub(crate) fn position(&self) -> &ReadPosition {
104        &self.position
105    }
106
107    pub(crate) fn add_duration(&mut self, duration: Duration) {
108        self.position.duration += duration;
109    }
110
111    #[must_use]
112    pub(crate) fn positional_error(&self, source: Error) -> PositionalError {
113        let Self { position, .. } = self;
114        PositionalError {
115            source,
116            position: position.clone(),
117        }
118    }
119}