ms_codeview/types/
number.rs

1//! Section 4, numeric leaves
2
3use super::Leaf;
4use crate::parser::{Parse, Parser, ParserError};
5use bstr::BStr;
6use pretty_hex::PrettyHex;
7use std::fmt::{Debug, Display};
8use std::num::TryFromIntError;
9use tracing::warn;
10
11/// A numeric constant defined within a CodeView type or symbol record.
12///
13/// # References
14/// * "Numeric Leaves" section of PDB specification.
15#[derive(Copy, Clone)]
16#[repr(transparent)]
17pub struct Number<'a> {
18    bytes: &'a [u8],
19}
20
21impl<'a> Number<'a> {
22    /// Gets the raw bytes of this `Number`.
23    pub fn as_bytes(&self) -> &'a [u8] {
24        self.bytes
25    }
26
27    /// Gets the kind (representation) of this value.
28    /// If this is an immediate value (integer in `0..=0x7fff`), gets the actual value.
29    pub fn kind(&self) -> Leaf {
30        let mut p = Parser::new(self.bytes);
31        Leaf(p.u16().unwrap())
32    }
33}
34
35impl<'a> Parse<'a> for Number<'a> {
36    fn from_parser(p: &mut Parser<'a>) -> Result<Self, ParserError> {
37        let start = p.peek_rest();
38
39        let more_len = match Leaf(p.u16()?) {
40            lf if lf.is_immediate_numeric() => 0,
41            Leaf::LF_CHAR => 1,
42            Leaf::LF_SHORT => 2,
43            Leaf::LF_USHORT => 2,
44            Leaf::LF_LONG => 4,
45            Leaf::LF_ULONG => 4,
46            Leaf::LF_REAL32 => 4,
47            Leaf::LF_REAL64 => 8,
48            Leaf::LF_REAL80 => 10,
49            Leaf::LF_REAL128 => 16,
50            Leaf::LF_QUADWORD => 8,
51            Leaf::LF_UQUADWORD => 8,
52            Leaf::LF_REAL48 => 6,
53            Leaf::LF_COMPLEX32 => 8,
54            Leaf::LF_COMPLEX64 => 16,
55            Leaf::LF_COMPLEX80 => 20,
56            Leaf::LF_COMPLEX128 => 32,
57            Leaf::LF_VARSTRING => p.u16()? as usize,
58            Leaf::LF_OCTWORD => 16,
59            Leaf::LF_UOCTWORD => 16,
60            Leaf::LF_DECIMAL => 16,
61            Leaf::LF_DATE => 8,
62            Leaf::LF_UTF8STRING => {
63                p.skip_strz()?;
64                0
65            }
66            Leaf::LF_REAL16 => 2,
67            lf => {
68                warn!(leaf = ?lf, "unrecognized numeric leaf");
69                // We don't know how many bytes to consume, so we can't keep parsing.
70                return Err(ParserError::new());
71            }
72        };
73
74        p.skip(more_len)?;
75        Ok(Self {
76            bytes: &start[..start.len() - p.len()],
77        })
78    }
79}
80
81impl<'a> Number<'a> {}
82
83macro_rules! try_from_number {
84    (
85        $t:ty
86    ) => {
87        impl<'a> TryFrom<Number<'a>> for $t {
88            type Error = TryFromIntError;
89
90            #[inline(never)]
91            fn try_from(value: Number<'a>) -> Result<Self, Self::Error> {
92                use map_parser_error_to_int_error as e;
93
94                let mut p = Parser::new(value.bytes);
95                Ok(match Leaf(e(p.u16())?) {
96                    lf if lf.is_immediate_numeric() => Self::try_from(lf.0)?,
97                    Leaf::LF_USHORT => Self::try_from(e(p.u16())?)?,
98                    Leaf::LF_ULONG => Self::try_from(e(p.u32())?)?,
99                    Leaf::LF_UQUADWORD => Self::try_from(e(p.u64())?)?,
100                    Leaf::LF_CHAR => Self::try_from(e(p.i8())?)?,
101                    Leaf::LF_SHORT => Self::try_from(e(p.i16())?)?,
102                    Leaf::LF_LONG => Self::try_from(e(p.i32())?)?,
103                    Leaf::LF_QUADWORD => Self::try_from(e(p.i64())?)?,
104                    Leaf::LF_OCTWORD => Self::try_from(e(p.i128())?)?,
105                    Leaf::LF_UOCTWORD => Self::try_from(e(p.u128())?)?,
106                    _ => return Err(try_from_int_error()),
107                })
108            }
109        }
110    };
111}
112
113try_from_number!(i8);
114try_from_number!(i16);
115try_from_number!(i32);
116try_from_number!(i64);
117try_from_number!(i128);
118
119try_from_number!(u8);
120try_from_number!(u16);
121try_from_number!(u32);
122try_from_number!(u64);
123try_from_number!(u128);
124
125fn map_parser_error_to_int_error<T>(r: Result<T, ParserError>) -> Result<T, TryFromIntError> {
126    match r {
127        Ok(x) => Ok(x),
128        Err(ParserError) => Err(try_from_int_error()),
129    }
130}
131
132/// Error type for conversions from `Number` to `f32`
133#[derive(Copy, Clone, Eq, PartialEq, Debug)]
134pub struct TryFromFloatError;
135
136impl From<ParserError> for TryFromFloatError {
137    fn from(_: ParserError) -> Self {
138        Self
139    }
140}
141
142impl<'a> TryFrom<Number<'a>> for f32 {
143    type Error = TryFromFloatError;
144
145    fn try_from(value: Number<'a>) -> Result<Self, Self::Error> {
146        let mut p = Parser::new(value.bytes);
147        Ok(match Leaf(p.u16()?) {
148            Leaf::LF_REAL32 => f32::from_le_bytes(p.array()?),
149            _ => return Err(TryFromFloatError),
150        })
151    }
152}
153
154impl<'a> TryFrom<Number<'a>> for f64 {
155    type Error = TryFromFloatError;
156
157    fn try_from(value: Number<'a>) -> Result<Self, Self::Error> {
158        let mut p = Parser::new(value.bytes);
159        Ok(match Leaf(p.u16()?) {
160            Leaf::LF_REAL32 => f32::from_le_bytes(p.array::<4>()?) as f64,
161            Leaf::LF_REAL64 => f64::from_le_bytes(p.array::<8>()?),
162            _ => return Err(TryFromFloatError),
163        })
164    }
165}
166
167/// Error type for conversions from `Number` to string
168#[derive(Copy, Clone, Eq, PartialEq, Debug)]
169pub struct TryFromStrError;
170
171impl From<ParserError> for TryFromStrError {
172    fn from(_: ParserError) -> Self {
173        Self
174    }
175}
176impl<'a> TryFrom<Number<'a>> for &'a BStr {
177    type Error = TryFromStrError;
178
179    fn try_from(value: Number<'a>) -> Result<Self, Self::Error> {
180        let mut p = Parser::new(value.bytes);
181        Ok(match Leaf(p.u16()?) {
182            Leaf::LF_UTF8STRING => p.strz()?,
183            Leaf::LF_VARSTRING => {
184                let len = p.u16()?;
185                let bytes = p.bytes(len as usize)?;
186                BStr::new(bytes)
187            }
188            _ => return Err(TryFromStrError),
189        })
190    }
191}
192
193impl<'a> Debug for Number<'a> {
194    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
195        Display::fmt(self, f)
196    }
197}
198
199impl<'a> Display for Number<'a> {
200    #[inline(never)]
201    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
202        fn e<T>(
203            f: &mut std::fmt::Formatter<'_>,
204            r: Result<T, ParserError>,
205        ) -> Result<T, std::fmt::Error> {
206            match r {
207                Ok(x) => Ok(x),
208                Err(ParserError) => {
209                    f.write_str("??(parser error)")?;
210                    Err(std::fmt::Error)
211                }
212            }
213        }
214
215        let mut p = Parser::new(self.bytes);
216
217        match Leaf(p.u16().unwrap()) {
218            lf if lf.is_immediate_numeric() => Display::fmt(&lf.0, f),
219            Leaf::LF_CHAR => Display::fmt(&e(f, p.i8())?, f),
220            Leaf::LF_SHORT => Display::fmt(&e(f, p.i16())?, f),
221            Leaf::LF_USHORT => Display::fmt(&e(f, p.u16())?, f),
222            Leaf::LF_LONG => Display::fmt(&e(f, p.i32())?, f),
223            Leaf::LF_ULONG => Display::fmt(&e(f, p.u32())?, f),
224            Leaf::LF_REAL32 => Display::fmt(&e(f, p.f32())?, f),
225            Leaf::LF_REAL64 => Display::fmt(&e(f, p.f64())?, f),
226            Leaf::LF_QUADWORD => Display::fmt(&e(f, p.i64())?, f),
227            Leaf::LF_UQUADWORD => Display::fmt(&e(f, p.u64())?, f),
228            Leaf::LF_VARSTRING => {
229                // This uses a 2-byte length prefix, not 1-byte.
230                let len = p.u16().unwrap();
231                let s = BStr::new(p.bytes(len as usize).unwrap());
232                <BStr as Display>::fmt(s, f)
233            }
234            Leaf::LF_OCTWORD => Display::fmt(&e(f, p.i128())?, f),
235            Leaf::LF_UOCTWORD => Display::fmt(&e(f, p.u128())?, f),
236            Leaf::LF_UTF8STRING => {
237                let s = p.strz().unwrap();
238                <BStr as Display>::fmt(s, f)
239            }
240
241            lf => {
242                write!(f, "?? {lf:?} {:?}", self.bytes.hex_dump())
243            }
244        }
245    }
246}
247
248fn try_from_int_error() -> TryFromIntError {
249    u32::try_from(-1i8).unwrap_err()
250}
251
252#[cfg(test)]
253fn parse_number(bytes: &[u8]) -> Number {
254    let mut p = Parser::new(bytes);
255    let n = p.number().unwrap();
256    assert!(p.is_empty());
257    n
258}
259
260#[test]
261fn number_error() {
262    assert!(Number::parse(&[]).is_err()); // too short
263    assert!(Number::parse(&[0]).is_err()); // also too short
264    assert!(Number::parse(&[0xff, 0xff]).is_err()); // unrecognized kind
265}
266
267#[test]
268fn number_immediate() {
269    // Values below 0x8000 are literal uint16 constants.
270    let n = parse_number(&[0xaa, 0x70]);
271    assert_eq!(n.as_bytes(), &[0xaa, 0x70]);
272    assert_eq!(u32::try_from(n).unwrap(), 0x70aa);
273}
274
275#[test]
276fn number_char() {
277    // LF_CHAR
278    let n = parse_number(&[0x00, 0x80, (-33i8) as u8]);
279    assert_eq!(i32::try_from(n).unwrap(), -33);
280
281    assert!(f32::try_from(n).is_err());
282    assert!(f64::try_from(n).is_err());
283    assert!(<&BStr>::try_from(n).is_err());
284}
285
286#[test]
287fn number_short() {
288    // LF_SHORT
289    let n = parse_number(&[0x01, 0x80, 0xaa, 0x55]);
290    assert_eq!(i32::try_from(n).unwrap(), 0x55aa_i32);
291    assert_eq!(u32::try_from(n).unwrap(), 0x55aa_u32);
292
293    let n = parse_number(&[0x01, 0x80, 0x55, 0xaa]);
294    assert_eq!(i32::try_from(n).unwrap(), -21931_i32);
295    assert!(u32::try_from(n).is_err());
296
297    assert!(f32::try_from(n).is_err());
298    assert!(f64::try_from(n).is_err());
299    assert!(<&BStr>::try_from(n).is_err());
300}
301
302#[test]
303fn number_long() {
304    // LF_LONG
305    let n = parse_number(&[0x03, 0x80, 1, 2, 3, 4]);
306    assert_eq!(u32::try_from(n).unwrap(), 0x04030201_u32);
307    assert_eq!(i32::try_from(n).unwrap(), 0x04030201_i32);
308    assert!(u16::try_from(n).is_err());
309    assert!(i16::try_from(n).is_err());
310    assert!(u8::try_from(n).is_err());
311    assert!(i8::try_from(n).is_err());
312
313    // unsigned cannot decode negative numbers
314    let n = parse_number(&[0x03, 0x80, 0xfe, 0xff, 0xff, 0xff]);
315    assert!(u8::try_from(n).is_err());
316    assert!(u16::try_from(n).is_err());
317    assert!(u32::try_from(n).is_err());
318    assert!(u64::try_from(n).is_err());
319    assert!(u128::try_from(n).is_err());
320    assert_eq!(i8::try_from(n).unwrap(), -2);
321    assert_eq!(i16::try_from(n).unwrap(), -2);
322    assert_eq!(i32::try_from(n).unwrap(), -2);
323    assert_eq!(i64::try_from(n).unwrap(), -2);
324    assert_eq!(i128::try_from(n).unwrap(), -2);
325
326    assert!(f32::try_from(n).is_err());
327    assert!(f64::try_from(n).is_err());
328    assert!(<&BStr>::try_from(n).is_err());
329}
330
331#[test]
332fn number_real32() {
333    use std::f32::consts::PI;
334
335    let b: [u8; 4] = PI.to_le_bytes();
336    assert_eq!(b, [0xdb, 0x0f, 0x49, 0x40]); // 0x400490fdb, pi in f32
337    println!("f32 PI bytes: {:#x?}", b);
338
339    // 8005 is LF_REAL32
340    let n = parse_number(&[0x05, 0x80, 0xdb, 0x0f, 0x49, 0x40]);
341
342    // LF_REAL32 is not convertible to any of the integer types
343    assert!(u8::try_from(n).is_err());
344    assert!(u16::try_from(n).is_err());
345    assert!(u32::try_from(n).is_err());
346    assert!(u64::try_from(n).is_err());
347    assert!(u128::try_from(n).is_err());
348
349    assert!(i8::try_from(n).is_err());
350    assert!(i16::try_from(n).is_err());
351    assert!(i32::try_from(n).is_err());
352    assert!(i64::try_from(n).is_err());
353    assert!(i128::try_from(n).is_err());
354
355    // Floating-point exact equality can be weird.
356    assert_eq!(f32::try_from(n).unwrap(), PI);
357
358    // We convert to f64 but do not verify the value, because again, floating-point is weird.
359    let _ = f64::try_from(n).unwrap();
360}
361
362#[test]
363fn number_real64() {
364    use std::f64::consts::PI;
365
366    let b: [u8; 8] = PI.to_le_bytes();
367    assert_eq!(b, [0x18, 0x2d, 0x44, 0x54, 0xfb, 0x21, 0x9, 0x40]);
368    // assert_eq!(b, [0xdb, 0x0f, 0x49, 0x40]); // 0x400921fb54442d18, pi in f64
369    println!("f64 PI bytes: {:#x?}", b);
370
371    // 8006 is LF_REAL64
372    let n = parse_number(&[0x06, 0x80, 0x18, 0x2d, 0x44, 0x54, 0xfb, 0x21, 0x9, 0x40]);
373
374    // LF_REAL64 is not convertible to any of the integer types
375    assert!(u8::try_from(n).is_err());
376    assert!(u16::try_from(n).is_err());
377    assert!(u32::try_from(n).is_err());
378    assert!(u64::try_from(n).is_err());
379    assert!(u128::try_from(n).is_err());
380
381    assert!(i8::try_from(n).is_err());
382    assert!(i16::try_from(n).is_err());
383    assert!(i32::try_from(n).is_err());
384    assert!(i64::try_from(n).is_err());
385    assert!(i128::try_from(n).is_err());
386
387    // Floating-point exact equality can be weird.
388    assert_eq!(f64::try_from(n).unwrap(), PI);
389}
390
391#[test]
392fn number_strz() {
393    let n = parse_number(b"\x1b\x80Hello, world\0");
394    assert_eq!(n.kind(), Leaf::LF_UTF8STRING);
395    assert_eq!(<&BStr>::try_from(n).unwrap(), "Hello, world");
396
397    let n = parse_number(&[0x00, 0x80, (-33i8) as u8]);
398    assert!(<&BStr>::try_from(n).is_err());
399}
400
401#[test]
402fn number_varstring() {
403    let s = parse_number(b"\x10\x80\x0c\x00Hello, world");
404    assert_eq!(s.kind(), Leaf::LF_VARSTRING);
405    assert_eq!(<&BStr>::try_from(s).unwrap(), "Hello, world");
406}
407
408#[test]
409fn number_unsupported_types() {
410    // We can test decoding the prefix for these types, even if we can't currently display them
411    // or convert them to something useful.
412    let cases: &[(Leaf, usize)] = &[
413        (Leaf::LF_REAL80, 10),
414        (Leaf::LF_REAL128, 16),
415        (Leaf::LF_REAL48, 6),
416        (Leaf::LF_COMPLEX32, 8),
417        (Leaf::LF_COMPLEX64, 16),
418        (Leaf::LF_COMPLEX80, 20),
419        (Leaf::LF_COMPLEX128, 32),
420        (Leaf::LF_DECIMAL, 16),
421        (Leaf::LF_DATE, 8),
422        (Leaf::LF_REAL16, 2),
423    ];
424
425    for &(kind, num_zeroes) in cases.iter() {
426        let mut input = vec![0; 2 + num_zeroes];
427        input[0] = kind.0 as u8;
428        input[1] = (kind.0 >> 8) as u8;
429        let n = parse_number(&input);
430        assert_eq!(kind, n.kind());
431    }
432}
433
434#[test]
435fn display() {
436    let cases: &[(&[u8], &str)] = &[
437        (&[0x01, 0x04], "immediate 1025"),
438        (&[0x00, 0x80, 0xff], "LF_CHAR -1"),
439        (&[0x01, 0x80, 0xfe, 0xff], "LF_SHORT -2"),
440        (&[0x02, 0x80, 0xfd, 0xff], "LF_USHORT 65533"),
441        (&[0x03, 0x80, 0xfc, 0xff, 0xff, 0xff], "LF_LONG -4"),
442        (&[0x04, 0x80, 0x00, 0x00, 0x02, 0x00], "LF_ULONG 131072"),
443        (&[0x05, 0x80, 0xdb, 0x0f, 0x49, 0x40], "LF_REAL32 3.1415927"),
444        (
445            &[0x06, 0x80, 0x18, 0x2d, 0x44, 0x54, 0xfb, 0x21, 0x9, 0x40],
446            "LF_REAL64 3.141592653589793",
447        ),
448        (
449            &[0x09, 0x80, 0xfb, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff],
450            "LF_QUADWORD -5",
451        ),
452        (
453            &[0x0a, 0x80, 0x00, 0xe4, 0x0b, 0x54, 0x02, 0x00, 0x00, 0x00],
454            "LF_UQUADWORD 10000000000",
455        ),
456        (
457            &[
458                0x17, 0x80, 0xfa, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
459                0xff, 0xff, 0xff, 0xff,
460            ],
461            "LF_OCTWORD -6",
462        ),
463        (
464            &[
465                0x18, 0x80, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
466                0x00, 0x00, 0x00, 0x00,
467            ],
468            "LF_UOCTWORD 1",
469        ),
470    ];
471
472    for &(input, expected_output) in cases.iter() {
473        let mut p = Parser::new(input);
474        let leaf = Leaf(p.u16().unwrap());
475
476        let n = parse_number(input);
477
478        let actual_output = if leaf.is_immediate_numeric() {
479            format!("immediate {n}")
480        } else {
481            format!("{leaf:?} {n}")
482        };
483
484        assert_eq!(actual_output, expected_output, "bytes: {:#x?}", input);
485
486        // Cover Debug::fmt. It just trivially defers to Display.
487        let _ = format!("{:?}", n);
488    }
489}
490
491#[test]
492fn display_bogus() {
493    // Because the byte slice within Number is private and Number::parse() does not construct
494    // a Number for kinds it does not recognize, it is impossible (outside of this module)
495    // to construct a Number over an invalid, non-immediate Leaf value.  But the Display code
496    // has to have a case for that, so we construct a bogus Number just so we can display it.
497    let bogus_num = Number {
498        bytes: &[0xff, 0xff, 0xaa, 0xaa, 0xaa],
499    };
500    println!("bogus_num = {bogus_num}");
501}