Skip to main content

ms_pdb/
stream_index.rs

1use std::fmt::Display;
2use zerocopy::{LE, U16};
3use zerocopy_derive::{FromBytes, Immutable, IntoBytes, KnownLayout, Unaligned};
4
5/// Identifies a stream in a PDB/MSF file.
6///
7/// This type guards against NIL stream values. The value stored in `Stream` should never be
8/// a NIL value (0xFFFF).
9#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Debug, Hash)]
10#[repr(transparent)]
11pub struct Stream(u16);
12
13impl Stream {
14    // Some streams have a fixed index.
15
16    /// Fixed stream index 0 is the Previous MSF Stream Directory
17    pub const OLD_STREAM_DIR: Stream = Stream(0);
18
19    /// Index of the PDB Information Stream. It contains version information and information to
20    /// connect this PDB to the executable.
21    pub const PDB: Stream = Stream(1);
22
23    /// Index of the Type Information Stream. It contains type records.
24    pub const TPI: Stream = Stream(2);
25
26    /// Debug Information Stream (DBI).
27    pub const DBI: Stream = Stream(3);
28
29    /// CodeView type records, index of IPI hash stream
30    pub const IPI: Stream = Stream(4);
31
32    /// Validates that `index` is non-NIL and converts it to a `Stream` value.
33    ///
34    /// If `index` is NIL (0xffff), then this returns `None`.
35    pub fn new(index: u16) -> Option<Stream> {
36        if index == NIL_STREAM_INDEX {
37            None
38        } else {
39            Some(Stream(index))
40        }
41    }
42
43    /// Returns the value of the stream index.
44    pub fn value(self) -> u16 {
45        self.0
46    }
47
48    /// Returns the value of the stream index, cast to `usize`. Use this when indexing slices.
49    pub fn index(self) -> usize {
50        debug_assert!(self.0 != NIL_STREAM_INDEX);
51        self.0 as usize
52    }
53}
54
55impl From<Stream> for u32 {
56    fn from(value: Stream) -> Self {
57        value.value() as u32
58    }
59}
60
61impl Display for Stream {
62    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
63        Display::fmt(&self.0, f)
64    }
65}
66
67/// A reserved stream index meaning "no stream at all", in `u16`.
68pub const NIL_STREAM_INDEX: u16 = 0xffff;
69
70/// Error type for `Stream::try_from` implementations.
71#[derive(Clone, Debug)]
72pub struct StreamIndexIsNilError;
73
74impl std::error::Error for StreamIndexIsNilError {}
75
76impl Display for StreamIndexIsNilError {
77    fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
78        fmt.write_str("The given stream index is NIL.")
79    }
80}
81
82#[derive(Clone, Debug)]
83pub struct StreamIndexOverflow;
84
85impl std::error::Error for StreamIndexOverflow {}
86
87impl Display for StreamIndexOverflow {
88    fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
89        fmt.write_str("The value is out of range for 16-bit stream indexes.")
90    }
91}
92
93impl TryFrom<u16> for Stream {
94    type Error = StreamIndexIsNilError;
95
96    fn try_from(i: u16) -> Result<Self, Self::Error> {
97        if i != NIL_STREAM_INDEX {
98            Ok(Self(i))
99        } else {
100            Err(StreamIndexIsNilError)
101        }
102    }
103}
104
105/// This structure can be embedded directly in structure definitions.
106#[derive(
107    Copy, Clone, Eq, PartialEq, Debug, IntoBytes, FromBytes, Immutable, KnownLayout, Unaligned,
108)]
109#[repr(transparent)]
110pub struct StreamIndexU16(pub U16<LE>);
111
112impl StreamIndexU16 {
113    /// The value of a nil stream index.
114    pub const NIL: Self = Self(U16::from_bytes(NIL_STREAM_INDEX.to_le_bytes()));
115
116    /// Checks whether this value is a nil stream index. Returns `Ok` if the value is not a nil
117    /// stream index, or `Err` if it is a nil stream index.
118    pub fn get(self) -> Option<u32> {
119        let s = self.0.get();
120        if s != NIL_STREAM_INDEX {
121            Some(s as u32)
122        } else {
123            None
124        }
125    }
126
127    /// Checks whether this value is a nil stream index. Returns `Ok` if the value is not a nil
128    /// stream index, or `Err` if it is a nil stream index.
129    pub fn get_err(self) -> Result<u32, StreamIndexIsNilError> {
130        let s = self.0.get();
131        if s != NIL_STREAM_INDEX {
132            Ok(s as u32)
133        } else {
134            Err(StreamIndexIsNilError)
135        }
136    }
137}
138
139impl TryFrom<u32> for StreamIndexU16 {
140    type Error = StreamIndexOverflow;
141
142    fn try_from(s: u32) -> Result<Self, Self::Error> {
143        if s < NIL_STREAM_INDEX as u32 {
144            Ok(StreamIndexU16(U16::new(s as u16)))
145        } else {
146            Err(StreamIndexOverflow)
147        }
148    }
149}
150
151impl TryFrom<Option<u32>> for StreamIndexU16 {
152    type Error = StreamIndexOverflow;
153
154    fn try_from(s_opt: Option<u32>) -> Result<Self, Self::Error> {
155        if let Some(s) = s_opt {
156            if s < NIL_STREAM_INDEX as u32 {
157                Ok(StreamIndexU16(U16::new(s as u16)))
158            } else {
159                Err(StreamIndexOverflow)
160            }
161        } else {
162            Ok(Self::NIL)
163        }
164    }
165}