use super::Leaf;
use crate::parser::{Parse, Parser, ParserError};
use bstr::BStr;
use pretty_hex::PrettyHex;
use std::fmt::{Debug, Display};
use std::num::TryFromIntError;
use tracing::warn;
#[derive(Copy, Clone)]
#[repr(transparent)]
pub struct Number<'a> {
bytes: &'a [u8],
}
impl<'a> Number<'a> {
pub fn as_bytes(&self) -> &'a [u8] {
self.bytes
}
pub fn kind(&self) -> Leaf {
let mut p = Parser::new(self.bytes);
Leaf(p.u16().unwrap())
}
}
impl<'a> Parse<'a> for Number<'a> {
fn from_parser(p: &mut Parser<'a>) -> Result<Self, ParserError> {
let start = p.peek_rest();
let more_len = match Leaf(p.u16()?) {
lf if lf.is_immediate_numeric() => 0,
Leaf::LF_CHAR => 1,
Leaf::LF_SHORT => 2,
Leaf::LF_USHORT => 2,
Leaf::LF_LONG => 4,
Leaf::LF_ULONG => 4,
Leaf::LF_REAL32 => 4,
Leaf::LF_REAL64 => 8,
Leaf::LF_REAL80 => 10,
Leaf::LF_REAL128 => 16,
Leaf::LF_QUADWORD => 8,
Leaf::LF_UQUADWORD => 8,
Leaf::LF_REAL48 => 6,
Leaf::LF_COMPLEX32 => 8,
Leaf::LF_COMPLEX64 => 16,
Leaf::LF_COMPLEX80 => 20,
Leaf::LF_COMPLEX128 => 32,
Leaf::LF_VARSTRING => p.u16()? as usize,
Leaf::LF_OCTWORD => 16,
Leaf::LF_UOCTWORD => 16,
Leaf::LF_DECIMAL => 16,
Leaf::LF_DATE => 8,
Leaf::LF_UTF8STRING => {
p.skip_strz()?;
0
}
Leaf::LF_REAL16 => 2,
lf => {
warn!(leaf = ?lf, "unrecognized numeric leaf");
return Err(ParserError::new());
}
};
p.skip(more_len)?;
Ok(Self {
bytes: &start[..start.len() - p.len()],
})
}
}
impl<'a> Number<'a> {}
macro_rules! try_from_number {
(
$t:ty
) => {
impl<'a> TryFrom<Number<'a>> for $t {
type Error = TryFromIntError;
#[inline(never)]
fn try_from(value: Number<'a>) -> Result<Self, Self::Error> {
use map_parser_error_to_int_error as e;
let mut p = Parser::new(value.bytes);
Ok(match Leaf(e(p.u16())?) {
lf if lf.is_immediate_numeric() => Self::try_from(lf.0)?,
Leaf::LF_USHORT => Self::try_from(e(p.u16())?)?,
Leaf::LF_ULONG => Self::try_from(e(p.u32())?)?,
Leaf::LF_UQUADWORD => Self::try_from(e(p.u64())?)?,
Leaf::LF_CHAR => Self::try_from(e(p.i8())?)?,
Leaf::LF_SHORT => Self::try_from(e(p.i16())?)?,
Leaf::LF_LONG => Self::try_from(e(p.i32())?)?,
Leaf::LF_QUADWORD => Self::try_from(e(p.i64())?)?,
Leaf::LF_OCTWORD => Self::try_from(e(p.i128())?)?,
Leaf::LF_UOCTWORD => Self::try_from(e(p.u128())?)?,
_ => return Err(try_from_int_error()),
})
}
}
};
}
try_from_number!(i8);
try_from_number!(i16);
try_from_number!(i32);
try_from_number!(i64);
try_from_number!(i128);
try_from_number!(u8);
try_from_number!(u16);
try_from_number!(u32);
try_from_number!(u64);
try_from_number!(u128);
fn map_parser_error_to_int_error<T>(r: Result<T, ParserError>) -> Result<T, TryFromIntError> {
match r {
Ok(x) => Ok(x),
Err(ParserError) => Err(try_from_int_error()),
}
}
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
pub struct TryFromFloatError;
impl From<ParserError> for TryFromFloatError {
fn from(_: ParserError) -> Self {
Self
}
}
impl<'a> TryFrom<Number<'a>> for f32 {
type Error = TryFromFloatError;
fn try_from(value: Number<'a>) -> Result<Self, Self::Error> {
let mut p = Parser::new(value.bytes);
Ok(match Leaf(p.u16()?) {
Leaf::LF_REAL32 => f32::from_le_bytes(p.array()?),
_ => return Err(TryFromFloatError),
})
}
}
impl<'a> TryFrom<Number<'a>> for f64 {
type Error = TryFromFloatError;
fn try_from(value: Number<'a>) -> Result<Self, Self::Error> {
let mut p = Parser::new(value.bytes);
Ok(match Leaf(p.u16()?) {
Leaf::LF_REAL32 => f32::from_le_bytes(p.array::<4>()?) as f64,
Leaf::LF_REAL64 => f64::from_le_bytes(p.array::<8>()?),
_ => return Err(TryFromFloatError),
})
}
}
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
pub struct TryFromStrError;
impl From<ParserError> for TryFromStrError {
fn from(_: ParserError) -> Self {
Self
}
}
impl<'a> TryFrom<Number<'a>> for &'a BStr {
type Error = TryFromStrError;
fn try_from(value: Number<'a>) -> Result<Self, Self::Error> {
let mut p = Parser::new(value.bytes);
Ok(match Leaf(p.u16()?) {
Leaf::LF_UTF8STRING => p.strz()?,
Leaf::LF_VARSTRING => {
let len = p.u16()?;
let bytes = p.bytes(len as usize)?;
BStr::new(bytes)
}
_ => return Err(TryFromStrError),
})
}
}
impl<'a> Debug for Number<'a> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
Display::fmt(self, f)
}
}
impl<'a> Display for Number<'a> {
#[inline(never)]
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
fn e<T>(
f: &mut std::fmt::Formatter<'_>,
r: Result<T, ParserError>,
) -> Result<T, std::fmt::Error> {
match r {
Ok(x) => Ok(x),
Err(ParserError) => {
f.write_str("??(parser error)")?;
Err(std::fmt::Error)
}
}
}
let mut p = Parser::new(self.bytes);
match Leaf(p.u16().unwrap()) {
lf if lf.is_immediate_numeric() => Display::fmt(&lf.0, f),
Leaf::LF_CHAR => Display::fmt(&e(f, p.i8())?, f),
Leaf::LF_SHORT => Display::fmt(&e(f, p.i16())?, f),
Leaf::LF_USHORT => Display::fmt(&e(f, p.u16())?, f),
Leaf::LF_LONG => Display::fmt(&e(f, p.i32())?, f),
Leaf::LF_ULONG => Display::fmt(&e(f, p.u32())?, f),
Leaf::LF_REAL32 => Display::fmt(&e(f, p.f32())?, f),
Leaf::LF_REAL64 => Display::fmt(&e(f, p.f64())?, f),
Leaf::LF_QUADWORD => Display::fmt(&e(f, p.i64())?, f),
Leaf::LF_UQUADWORD => Display::fmt(&e(f, p.u64())?, f),
Leaf::LF_VARSTRING => {
let len = p.u16().unwrap();
let s = BStr::new(p.bytes(len as usize).unwrap());
<BStr as Display>::fmt(s, f)
}
Leaf::LF_OCTWORD => Display::fmt(&e(f, p.i128())?, f),
Leaf::LF_UOCTWORD => Display::fmt(&e(f, p.u128())?, f),
Leaf::LF_UTF8STRING => {
let s = p.strz().unwrap();
<BStr as Display>::fmt(s, f)
}
lf => {
write!(f, "?? {lf:?} {:?}", self.bytes.hex_dump())
}
}
}
}
fn try_from_int_error() -> TryFromIntError {
u32::try_from(-1i8).unwrap_err()
}
#[cfg(test)]
fn parse_number(bytes: &[u8]) -> Number {
let mut p = Parser::new(bytes);
let n = p.number().unwrap();
assert!(p.is_empty());
n
}
#[test]
fn number_error() {
assert!(Number::parse(&[]).is_err()); assert!(Number::parse(&[0]).is_err()); assert!(Number::parse(&[0xff, 0xff]).is_err()); }
#[test]
fn number_immediate() {
let n = parse_number(&[0xaa, 0x70]);
assert_eq!(n.as_bytes(), &[0xaa, 0x70]);
assert_eq!(u32::try_from(n).unwrap(), 0x70aa);
}
#[test]
fn number_char() {
let n = parse_number(&[0x00, 0x80, (-33i8) as u8]);
assert_eq!(i32::try_from(n).unwrap(), -33);
assert!(f32::try_from(n).is_err());
assert!(f64::try_from(n).is_err());
assert!(<&BStr>::try_from(n).is_err());
}
#[test]
fn number_short() {
let n = parse_number(&[0x01, 0x80, 0xaa, 0x55]);
assert_eq!(i32::try_from(n).unwrap(), 0x55aa_i32);
assert_eq!(u32::try_from(n).unwrap(), 0x55aa_u32);
let n = parse_number(&[0x01, 0x80, 0x55, 0xaa]);
assert_eq!(i32::try_from(n).unwrap(), -21931_i32);
assert!(u32::try_from(n).is_err());
assert!(f32::try_from(n).is_err());
assert!(f64::try_from(n).is_err());
assert!(<&BStr>::try_from(n).is_err());
}
#[test]
fn number_long() {
let n = parse_number(&[0x03, 0x80, 1, 2, 3, 4]);
assert_eq!(u32::try_from(n).unwrap(), 0x04030201_u32);
assert_eq!(i32::try_from(n).unwrap(), 0x04030201_i32);
assert!(u16::try_from(n).is_err());
assert!(i16::try_from(n).is_err());
assert!(u8::try_from(n).is_err());
assert!(i8::try_from(n).is_err());
let n = parse_number(&[0x03, 0x80, 0xfe, 0xff, 0xff, 0xff]);
assert!(u8::try_from(n).is_err());
assert!(u16::try_from(n).is_err());
assert!(u32::try_from(n).is_err());
assert!(u64::try_from(n).is_err());
assert!(u128::try_from(n).is_err());
assert_eq!(i8::try_from(n).unwrap(), -2);
assert_eq!(i16::try_from(n).unwrap(), -2);
assert_eq!(i32::try_from(n).unwrap(), -2);
assert_eq!(i64::try_from(n).unwrap(), -2);
assert_eq!(i128::try_from(n).unwrap(), -2);
assert!(f32::try_from(n).is_err());
assert!(f64::try_from(n).is_err());
assert!(<&BStr>::try_from(n).is_err());
}
#[test]
fn number_real32() {
use std::f32::consts::PI;
let b: [u8; 4] = PI.to_le_bytes();
assert_eq!(b, [0xdb, 0x0f, 0x49, 0x40]); println!("f32 PI bytes: {:#x?}", b);
let n = parse_number(&[0x05, 0x80, 0xdb, 0x0f, 0x49, 0x40]);
assert!(u8::try_from(n).is_err());
assert!(u16::try_from(n).is_err());
assert!(u32::try_from(n).is_err());
assert!(u64::try_from(n).is_err());
assert!(u128::try_from(n).is_err());
assert!(i8::try_from(n).is_err());
assert!(i16::try_from(n).is_err());
assert!(i32::try_from(n).is_err());
assert!(i64::try_from(n).is_err());
assert!(i128::try_from(n).is_err());
assert_eq!(f32::try_from(n).unwrap(), PI);
let _ = f64::try_from(n).unwrap();
}
#[test]
fn number_real64() {
use std::f64::consts::PI;
let b: [u8; 8] = PI.to_le_bytes();
assert_eq!(b, [0x18, 0x2d, 0x44, 0x54, 0xfb, 0x21, 0x9, 0x40]);
println!("f64 PI bytes: {:#x?}", b);
let n = parse_number(&[0x06, 0x80, 0x18, 0x2d, 0x44, 0x54, 0xfb, 0x21, 0x9, 0x40]);
assert!(u8::try_from(n).is_err());
assert!(u16::try_from(n).is_err());
assert!(u32::try_from(n).is_err());
assert!(u64::try_from(n).is_err());
assert!(u128::try_from(n).is_err());
assert!(i8::try_from(n).is_err());
assert!(i16::try_from(n).is_err());
assert!(i32::try_from(n).is_err());
assert!(i64::try_from(n).is_err());
assert!(i128::try_from(n).is_err());
assert_eq!(f64::try_from(n).unwrap(), PI);
}
#[test]
fn number_strz() {
let n = parse_number(b"\x1b\x80Hello, world\0");
assert_eq!(n.kind(), Leaf::LF_UTF8STRING);
assert_eq!(<&BStr>::try_from(n).unwrap(), "Hello, world");
let n = parse_number(&[0x00, 0x80, (-33i8) as u8]);
assert!(<&BStr>::try_from(n).is_err());
}
#[test]
fn number_varstring() {
let s = parse_number(b"\x10\x80\x0c\x00Hello, world");
assert_eq!(s.kind(), Leaf::LF_VARSTRING);
assert_eq!(<&BStr>::try_from(s).unwrap(), "Hello, world");
}
#[test]
fn number_unsupported_types() {
let cases: &[(Leaf, usize)] = &[
(Leaf::LF_REAL80, 10),
(Leaf::LF_REAL128, 16),
(Leaf::LF_REAL48, 6),
(Leaf::LF_COMPLEX32, 8),
(Leaf::LF_COMPLEX64, 16),
(Leaf::LF_COMPLEX80, 20),
(Leaf::LF_COMPLEX128, 32),
(Leaf::LF_DECIMAL, 16),
(Leaf::LF_DATE, 8),
(Leaf::LF_REAL16, 2),
];
for &(kind, num_zeroes) in cases.iter() {
let mut input = vec![0; 2 + num_zeroes];
input[0] = kind.0 as u8;
input[1] = (kind.0 >> 8) as u8;
let n = parse_number(&input);
assert_eq!(kind, n.kind());
}
}
#[test]
fn display() {
let cases: &[(&[u8], &str)] = &[
(&[0x01, 0x04], "immediate 1025"),
(&[0x00, 0x80, 0xff], "LF_CHAR -1"),
(&[0x01, 0x80, 0xfe, 0xff], "LF_SHORT -2"),
(&[0x02, 0x80, 0xfd, 0xff], "LF_USHORT 65533"),
(&[0x03, 0x80, 0xfc, 0xff, 0xff, 0xff], "LF_LONG -4"),
(&[0x04, 0x80, 0x00, 0x00, 0x02, 0x00], "LF_ULONG 131072"),
(&[0x05, 0x80, 0xdb, 0x0f, 0x49, 0x40], "LF_REAL32 3.1415927"),
(
&[0x06, 0x80, 0x18, 0x2d, 0x44, 0x54, 0xfb, 0x21, 0x9, 0x40],
"LF_REAL64 3.141592653589793",
),
(
&[0x09, 0x80, 0xfb, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff],
"LF_QUADWORD -5",
),
(
&[0x0a, 0x80, 0x00, 0xe4, 0x0b, 0x54, 0x02, 0x00, 0x00, 0x00],
"LF_UQUADWORD 10000000000",
),
(
&[
0x17, 0x80, 0xfa, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff,
],
"LF_OCTWORD -6",
),
(
&[
0x18, 0x80, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
],
"LF_UOCTWORD 1",
),
];
for &(input, expected_output) in cases.iter() {
let mut p = Parser::new(input);
let leaf = Leaf(p.u16().unwrap());
let n = parse_number(input);
let actual_output = if leaf.is_immediate_numeric() {
format!("immediate {n}")
} else {
format!("{leaf:?} {n}")
};
assert_eq!(actual_output, expected_output, "bytes: {:#x?}", input);
let _ = format!("{:?}", n);
}
}
#[test]
fn display_bogus() {
let bogus_num = Number {
bytes: &[0xff, 0xff, 0xaa, 0xaa, 0xaa],
};
println!("bogus_num = {bogus_num}");
}