pdb/
source.rs

1// Copyright 2017 pdb Developers
2//
3// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
4// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
5// http://opensource.org/licenses/MIT>, at your option. This file may not be
6// copied, modified, or distributed except according to those terms.
7
8use std::fmt;
9use std::io;
10
11/// Represents an offset + size of the source file.
12///
13/// The multi-stream file implementation (used by `pdb::PDB`) determines which byte ranges it needs
14/// to satisfy its requests, and it describes those requests as a `&[SourceSlice]`.
15#[derive(Debug, Clone, Copy, Eq, PartialEq)]
16pub struct SourceSlice {
17    /// Offset into the source file.
18    pub offset: u64,
19    /// Size of the slice.
20    pub size: usize,
21}
22
23/// The `pdb` crate accesses PDB files via the `pdb::Source` trait.
24///
25/// This library is written with zero-copy in mind. `Source`s provide [`SourceView`]s which need not
26/// outlive their parent, supporting implementations of e.g. memory mapped files.
27///
28/// PDB files are "multi-stream files" (MSF) under the hood. MSFs have various layers of
29/// indirection, but ultimately the MSF code asks a `Source` to view a series of
30/// [`{ offset, size }` records](SourceSlice), which the `Source` provides as a
31/// contiguous `&[u8]`.
32///
33/// # Default
34///
35/// There is a default `Source` implementation for `std::io::Read` + `std::io::Seek` +
36/// `std::fmt::Debug`, allowing a `std::fs::File` to be treated as `pdb::Source`. This
37/// implementation provides views by allocating a buffer, seeking, and reading the contents into
38/// that buffer.
39///
40/// # Alignment
41///
42/// The requested offsets will always be aligned to the MSF's page size, which is always a power of
43/// two and is usually (but not always) 4096 bytes. The requested sizes will also be multiples of
44/// the page size, except for the size of the final `SourceSlice`, which may be smaller.
45///
46/// PDB files are specified as always being a multiple of the page size, so `Source` implementations
47/// are free to e.g. map whole pages and return a sub-slice of the requested length.
48///
49pub trait Source<'s>: fmt::Debug {
50    /// Provides a contiguous view of the source file composed of the requested position(s).
51    ///
52    /// Note that the SourceView's as_slice() method cannot fail, so `view()` is the time to raise
53    /// IO errors.
54    fn view(&mut self, slices: &[SourceSlice]) -> Result<Box<dyn SourceView<'s>>, io::Error>;
55}
56
57/// An owned, droppable, read-only view of the source file which can be referenced as a byte slice.
58pub trait SourceView<'s>: fmt::Debug {
59    /// Returns a view to the raw data.
60    fn as_slice(&self) -> &[u8];
61}
62
63#[derive(Clone)]
64struct ReadView {
65    bytes: Vec<u8>,
66}
67
68impl fmt::Debug for ReadView {
69    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
70        write!(f, "ReadView({} bytes)", self.bytes.len())
71    }
72}
73
74impl SourceView<'_> for ReadView {
75    fn as_slice(&self) -> &[u8] {
76        self.bytes.as_slice()
77    }
78}
79
80impl<'s, T> Source<'s> for T
81where
82    T: io::Read + io::Seek + fmt::Debug + 's,
83{
84    fn view(&mut self, slices: &[SourceSlice]) -> Result<Box<dyn SourceView<'s>>, io::Error> {
85        let len = slices.iter().fold(0, |acc, s| acc + s.size);
86
87        let mut v = ReadView {
88            bytes: Vec::with_capacity(len),
89        };
90        v.bytes.resize(len, 0);
91
92        {
93            let bytes = v.bytes.as_mut_slice();
94            let mut output_offset: usize = 0;
95            for slice in slices {
96                self.seek(io::SeekFrom::Start(slice.offset))?;
97                self.read_exact(&mut bytes[output_offset..(output_offset + slice.size)])?;
98                output_offset += slice.size;
99            }
100        }
101
102        Ok(Box::new(v))
103    }
104}
105
106#[cfg(test)]
107mod tests {
108    mod read_view {
109        use crate::source::*;
110        use std::io::Cursor;
111        use std::io::ErrorKind;
112
113        #[test]
114        fn test_basic_reading() {
115            let mut data = vec![0; 4096];
116            data[42] = 42;
117
118            let mut source: Box<dyn Source<'_>> = Box::new(Cursor::new(data.as_slice()));
119
120            let source_slices = vec![SourceSlice {
121                offset: 40,
122                size: 4,
123            }];
124            let view = source
125                .view(source_slices.as_slice())
126                .expect("viewing must succeed");
127            assert_eq!(&[0u8, 0, 42, 0], view.as_slice());
128        }
129
130        #[test]
131        fn test_discontinuous_reading() {
132            let mut data = vec![0; 4096];
133            data[42] = 42;
134            data[88] = 88;
135
136            let mut source: Box<dyn Source<'_>> = Box::new(Cursor::new(data.as_slice()));
137
138            let source_slices = vec![
139                SourceSlice {
140                    offset: 88,
141                    size: 1,
142                },
143                SourceSlice {
144                    offset: 40,
145                    size: 4,
146                },
147            ];
148            let view = source
149                .view(source_slices.as_slice())
150                .expect("viewing must succeed");
151            assert_eq!(&[88u8, 0, 0, 42, 0], view.as_slice());
152        }
153
154        #[test]
155        fn test_duplicate_reading() {
156            let mut data = vec![0; 4096];
157            data[42] = 42;
158            data[88] = 88;
159
160            let mut source: Box<dyn Source<'_>> = Box::new(Cursor::new(data.as_slice()));
161
162            let source_slices = vec![
163                SourceSlice {
164                    offset: 88,
165                    size: 1,
166                },
167                SourceSlice {
168                    offset: 40,
169                    size: 4,
170                },
171                SourceSlice {
172                    offset: 88,
173                    size: 1,
174                },
175            ];
176            let view = source
177                .view(source_slices.as_slice())
178                .expect("viewing must succeed");
179            assert_eq!(&[88u8, 0, 0, 42, 0, 88], view.as_slice());
180        }
181
182        #[test]
183        fn test_eof_reading() {
184            let data = vec![0; 4096];
185
186            let mut source: Box<dyn Source<'_>> = Box::new(Cursor::new(data.as_slice()));
187
188            // one byte is readable, but we asked for two
189            let source_slices = vec![SourceSlice {
190                offset: 4095,
191                size: 2,
192            }];
193            let r = source.view(source_slices.as_slice());
194            match r {
195                Ok(_) => panic!("should have failed"),
196                Err(e) => {
197                    assert_eq!(ErrorKind::UnexpectedEof, e.kind());
198                }
199            }
200        }
201    }
202}