Skip to main content

ms_pdb_msf/
stream_reader.rs

1use super::*;
2
3/// Allows reading a stream using the [`Read`], [`Seek`], and [`ReadAt`] traits.
4pub struct StreamReader<'a, F> {
5    /// The stream index. This is used only for diagnostics.
6    stream: u32,
7    /// Size in bytes of the stream. This value _is never_ equal to [`NIL_STREAM_SIZE`].
8    stream_size: u32,
9    is_nil: bool,
10    /// Page size of the MSF file.
11    page_size: PageSize,
12    /// Maps page indices within the stream to page indices within the MSF file.
13    page_map: &'a [Page],
14    /// Provides access to the MSF file contents.
15    file: &'a F,
16    /// The seek position of the stream reader.
17    pos: u64,
18}
19
20impl<'a, F: ReadAt> StreamReader<'a, F> {
21    pub(crate) fn new(
22        pdb: &'a Msf<F>,
23        stream: u32,
24        stream_size: u32,
25        page_map: &'a [Page],
26        pos: u64,
27    ) -> Self {
28        Self {
29            stream,
30            stream_size: if stream_size == NIL_STREAM_SIZE {
31                0
32            } else {
33                stream_size
34            },
35            is_nil: stream_size == NIL_STREAM_SIZE,
36            page_size: pdb.pages.page_size,
37            page_map,
38            file: &pdb.file,
39            pos,
40        }
41    }
42
43    /// Size in bytes of the stream.
44    ///
45    /// This will be zero for nil streams.
46    pub fn len(&self) -> u32 {
47        self.stream_size
48    }
49
50    /// Tests whether this stream is empty (zero-length)
51    pub fn is_empty(&self) -> bool {
52        self.len() == 0
53    }
54
55    /// Returns `true` if this is a nil stream.
56    pub fn is_nil(&self) -> bool {
57        self.is_nil
58    }
59}
60
61impl<'a, F: ReadAt> Seek for StreamReader<'a, F> {
62    fn seek(&mut self, from: SeekFrom) -> std::io::Result<u64> {
63        let new_pos: i64 = match from {
64            SeekFrom::Start(offset) => offset as i64,
65            SeekFrom::End(signed_offset) => signed_offset + self.stream_size as i64,
66            SeekFrom::Current(signed_offset) => self.pos as i64 + signed_offset,
67        };
68
69        if new_pos < 0 {
70            return Err(std::io::ErrorKind::InvalidInput.into());
71        }
72        self.pos = new_pos as u64;
73        Ok(self.pos)
74    }
75}
76
77impl<'a, F: ReadAt> Read for StreamReader<'a, F> {
78    fn read(&mut self, dst: &mut [u8]) -> std::io::Result<usize> {
79        let (n, new_pos) = super::read::read_stream_core(
80            self.stream,
81            self.file,
82            self.page_size,
83            self.stream_size,
84            self.page_map,
85            self.pos,
86            dst,
87        )?;
88
89        self.pos = new_pos;
90        Ok(n)
91    }
92}
93
94impl<'a, F: ReadAt> ReadAt for StreamReader<'a, F> {
95    fn read_exact_at(&self, buf: &mut [u8], offset: u64) -> std::io::Result<()> {
96        let (n, _new_pos) = super::read::read_stream_core(
97            self.stream,
98            self.file,
99            self.page_size,
100            self.stream_size,
101            self.page_map,
102            offset,
103            buf,
104        )?;
105        if n != buf.len() {
106            return Err(std::io::Error::from(std::io::ErrorKind::UnexpectedEof));
107        }
108        Ok(())
109    }
110
111    fn read_at(&self, buf: &mut [u8], offset: u64) -> std::io::Result<usize> {
112        let (n, _new_pos) = super::read::read_stream_core(
113            self.stream,
114            self.file,
115            self.page_size,
116            self.stream_size,
117            self.page_map,
118            offset,
119            buf,
120        )?;
121        Ok(n)
122    }
123}