use crate::{
error::Error,
reader::Reader,
ty::{BaseType, Type, TypeBody},
};
#[derive(Clone, Debug, PartialEq)]
#[non_exhaustive]
pub enum Literal<'a> {
Set(&'a [u8]),
U8(u8),
S8(i8),
Char(u8),
U16(u16),
S16(i16),
WideChar(u16),
U32(u32),
S32(i32),
ProcPtr(u32),
Single([u8; 4]),
U64(u64),
S64(i64),
Double([u8; 8]),
Currency([u8; 8]),
Extended([u8; 10]),
String(&'a [u8]),
WideString(&'a [u8]),
UnicodeString(&'a [u8]),
}
const MAX_LEN: u32 = 0x4000_0000;
pub(crate) fn parse_literal<'a>(
reader: &mut Reader<'a>,
ty: &Type<'a>,
) -> Result<Literal<'a>, Error> {
let value = match ty.base_type {
BaseType::Set => {
let bit_size = match ty.body {
TypeBody::Set { bit_size } => bit_size,
_ => {
return Err(Error::UnknownBaseType {
byte: BaseType::Set as u8,
});
}
};
let byte_size = bit_size.checked_add(7).ok_or(Error::Overflow {
what: "literal Set byte size",
})? / 8;
let bytes = reader.take(byte_size as usize, "literal Set bytes")?;
Literal::Set(bytes)
}
BaseType::U8 => Literal::U8(reader.u8("literal U8")?),
BaseType::S8 => {
let raw = reader.u8("literal S8")?;
#[allow(clippy::cast_possible_wrap)]
{
Literal::S8(raw as i8)
}
}
BaseType::Char => Literal::Char(reader.u8("literal Char")?),
BaseType::U16 => {
let bytes = reader.array::<2>("literal U16")?;
Literal::U16(u16::from_le_bytes(bytes))
}
BaseType::S16 => {
let bytes = reader.array::<2>("literal S16")?;
Literal::S16(i16::from_le_bytes(bytes))
}
BaseType::WideChar => {
let bytes = reader.array::<2>("literal WideChar")?;
Literal::WideChar(u16::from_le_bytes(bytes))
}
BaseType::U32 => Literal::U32(reader.u32_le("literal U32")?),
BaseType::S32 => Literal::S32(reader.i32_le("literal S32")?),
BaseType::ProcPtr => Literal::ProcPtr(reader.u32_le("literal ProcPtr")?),
BaseType::Single => Literal::Single(reader.array::<4>("literal Single")?),
BaseType::U64 => {
let bytes = reader.array::<8>("literal U64")?;
Literal::U64(u64::from_le_bytes(bytes))
}
BaseType::S64 => {
let bytes = reader.array::<8>("literal S64")?;
Literal::S64(i64::from_le_bytes(bytes))
}
BaseType::Double => Literal::Double(reader.array::<8>("literal Double")?),
BaseType::Currency => Literal::Currency(reader.array::<8>("literal Currency")?),
BaseType::Extended => Literal::Extended(reader.array::<10>("literal Extended")?),
BaseType::PChar | BaseType::String => {
let len = reader.u32_le("literal String length")?;
if len > MAX_LEN {
return Err(Error::Overflow {
what: "literal String length",
});
}
let bytes = reader.take(len as usize, "literal String bytes")?;
Literal::String(bytes)
}
BaseType::WideString => {
let len = reader.u32_le("literal WideString length")?;
let byte_count = (len as usize).checked_mul(2).ok_or(Error::Overflow {
what: "literal WideString byte count",
})?;
let bytes = reader.take(byte_count, "literal WideString bytes")?;
Literal::WideString(bytes)
}
BaseType::UnicodeString => {
let len = reader.u32_le("literal UnicodeString length")?;
let byte_count = (len as usize).checked_mul(2).ok_or(Error::Overflow {
what: "literal UnicodeString byte count",
})?;
let bytes = reader.take(byte_count, "literal UnicodeString bytes")?;
Literal::UnicodeString(bytes)
}
other => return Err(Error::UnknownBaseType { byte: other as u8 }),
};
Ok(value)
}