use crate::{
file::parser::Parser,
metadata::marshalling::types::{
MarshallingInfo, NativeType, MAX_RECURSION_DEPTH, NATIVE_TYPE, VARIANT_TYPE,
},
Error::RecursionLimit,
Result,
};
pub fn parse_marshalling_descriptor(data: &[u8]) -> Result<MarshallingInfo> {
let mut parser = MarshallingParser::new(data);
parser.parse_descriptor()
}
pub struct MarshallingParser<'a> {
parser: Parser<'a>,
depth: usize,
}
impl<'a> MarshallingParser<'a> {
#[must_use]
pub fn new(data: &'a [u8]) -> Self {
MarshallingParser {
parser: Parser::new(data),
depth: 0,
}
}
fn parse_optional_compressed_uint(&mut self) -> Result<Option<u32>> {
if self.parser.has_more_data() && self.parser.peek_byte()? != NATIVE_TYPE::END {
Ok(Some(self.parser.read_compressed_uint()?))
} else {
Ok(None)
}
}
fn parse_optional_byte(&mut self) -> Result<Option<u8>> {
if self.parser.has_more_data() && self.parser.peek_byte()? != NATIVE_TYPE::END {
Ok(Some(self.parser.read_le::<u8>()?))
} else {
Ok(None)
}
}
fn has_optional_data(&mut self) -> Result<bool> {
Ok(self.parser.has_more_data() && self.parser.peek_byte()? != NATIVE_TYPE::END)
}
pub fn parse_native_type(&mut self) -> Result<NativeType> {
self.depth += 1;
if self.depth >= MAX_RECURSION_DEPTH {
self.depth -= 1;
return Err(RecursionLimit(MAX_RECURSION_DEPTH));
}
let result = self.parse_native_type_inner();
self.depth -= 1;
result
}
fn parse_native_type_inner(&mut self) -> Result<NativeType> {
let head_byte = self.parser.read_le::<u8>()?;
match head_byte {
NATIVE_TYPE::END | NATIVE_TYPE::MAX => Ok(NativeType::End),
NATIVE_TYPE::VOID => Ok(NativeType::Void),
NATIVE_TYPE::BOOLEAN => Ok(NativeType::Boolean),
NATIVE_TYPE::I1 => Ok(NativeType::I1),
NATIVE_TYPE::U1 => Ok(NativeType::U1),
NATIVE_TYPE::I2 => Ok(NativeType::I2),
NATIVE_TYPE::U2 => Ok(NativeType::U2),
NATIVE_TYPE::I4 => Ok(NativeType::I4),
NATIVE_TYPE::U4 => Ok(NativeType::U4),
NATIVE_TYPE::I8 => Ok(NativeType::I8),
NATIVE_TYPE::U8 => Ok(NativeType::U8),
NATIVE_TYPE::R4 => Ok(NativeType::R4),
NATIVE_TYPE::R8 => Ok(NativeType::R8),
NATIVE_TYPE::SYSCHAR => Ok(NativeType::SysChar),
NATIVE_TYPE::VARIANT => Ok(NativeType::Variant),
NATIVE_TYPE::CURRENCY => Ok(NativeType::Currency),
NATIVE_TYPE::DECIMAL => Ok(NativeType::Decimal),
NATIVE_TYPE::DATE => Ok(NativeType::Date),
NATIVE_TYPE::INT => Ok(NativeType::Int),
NATIVE_TYPE::UINT => Ok(NativeType::UInt),
NATIVE_TYPE::ERROR => Ok(NativeType::Error),
NATIVE_TYPE::BSTR => Ok(NativeType::BStr),
NATIVE_TYPE::LPSTR => {
let size_param_index = self.parse_optional_compressed_uint()?;
Ok(NativeType::LPStr { size_param_index })
}
NATIVE_TYPE::LPWSTR => {
let size_param_index = self.parse_optional_compressed_uint()?;
Ok(NativeType::LPWStr { size_param_index })
}
NATIVE_TYPE::LPTSTR => {
let size_param_index = self.parse_optional_compressed_uint()?;
Ok(NativeType::LPTStr { size_param_index })
}
NATIVE_TYPE::LPUTF8STR => {
let size_param_index = self.parse_optional_compressed_uint()?;
Ok(NativeType::LPUtf8Str { size_param_index })
}
NATIVE_TYPE::FIXEDSYSSTRING => {
let size = self.parser.read_compressed_uint()?;
Ok(NativeType::FixedSysString { size })
}
NATIVE_TYPE::OBJECTREF => Ok(NativeType::ObjectRef),
NATIVE_TYPE::IUNKNOWN => Ok(NativeType::IUnknown),
NATIVE_TYPE::IDISPATCH => Ok(NativeType::IDispatch),
NATIVE_TYPE::IINSPECTABLE => Ok(NativeType::IInspectable),
NATIVE_TYPE::STRUCT => {
let packing_size = self.parse_optional_byte()?;
let class_size = self.parse_optional_compressed_uint()?;
Ok(NativeType::Struct {
packing_size,
class_size,
})
}
NATIVE_TYPE::INTERFACE => {
let iid_param_index = self.parse_optional_compressed_uint()?;
Ok(NativeType::Interface { iid_param_index })
}
NATIVE_TYPE::SAFEARRAY => {
let mut variant_type = VARIANT_TYPE::EMPTY;
let mut user_defined_name = None;
if self.parser.has_more_data() {
variant_type = u16::from(self.parser.read_le::<u8>()?) & VARIANT_TYPE::TYPEMASK;
if self.parser.has_more_data() && self.parser.peek_byte()? != NATIVE_TYPE::END {
user_defined_name = Some(self.parser.read_string_utf8()?);
}
}
Ok(NativeType::SafeArray {
variant_type,
user_defined_name,
})
}
NATIVE_TYPE::FIXEDARRAY => {
let size = self.parser.read_compressed_uint()?;
let element_type = if self.has_optional_data()? {
Some(Box::new(self.parse_native_type()?))
} else {
None
};
Ok(NativeType::FixedArray { size, element_type })
}
NATIVE_TYPE::ARRAY => {
let array_type = self.parse_native_type()?;
let num_param = self.parse_optional_compressed_uint()?;
let num_element = self.parse_optional_compressed_uint()?;
Ok(NativeType::Array {
element_type: Box::new(array_type),
num_param,
num_element,
})
}
NATIVE_TYPE::NESTEDSTRUCT => Ok(NativeType::NestedStruct),
NATIVE_TYPE::BYVALSTR => {
let size = self.parser.read_compressed_uint()?;
Ok(NativeType::ByValStr { size })
}
NATIVE_TYPE::ANSIBSTR => Ok(NativeType::AnsiBStr),
NATIVE_TYPE::TBSTR => Ok(NativeType::TBStr),
NATIVE_TYPE::VARIANTBOOL => Ok(NativeType::VariantBool),
NATIVE_TYPE::FUNC => Ok(NativeType::Func),
NATIVE_TYPE::ASANY => Ok(NativeType::AsAny),
NATIVE_TYPE::LPSTRUCT => Ok(NativeType::LPStruct),
NATIVE_TYPE::CUSTOMMARSHALER => {
let guid = self.parser.read_string_utf8()?;
let native_type_name = self.parser.read_string_utf8()?;
let cookie = self.parser.read_string_utf8()?;
let type_reference = self.parser.read_string_utf8()?;
Ok(NativeType::CustomMarshaler {
guid,
native_type_name,
cookie,
type_reference,
})
}
NATIVE_TYPE::HSTRING => Ok(NativeType::HString),
NATIVE_TYPE::PTR => {
let ref_type = if self.has_optional_data()? {
Some(Box::new(self.parse_native_type()?))
} else {
None
};
Ok(NativeType::Ptr { ref_type })
}
_ => Err(malformed_error!("Invalid NATIVE_TYPE byte - {}", head_byte)),
}
}
pub fn parse_descriptor(&mut self) -> Result<MarshallingInfo> {
let native_type = self.parse_native_type()?;
let mut descriptor = MarshallingInfo {
primary_type: native_type,
additional_types: Vec::new(),
};
while self.parser.has_more_data() {
if self.parser.peek_byte()? == NATIVE_TYPE::END {
self.parser.read_le::<u8>()?; break;
}
let additional_type = self.parse_native_type()?;
descriptor.additional_types.push(additional_type);
}
Ok(descriptor)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::Error;
#[test]
fn test_parse_simple_types() {
let test_cases = vec![
(vec![NATIVE_TYPE::VOID], NativeType::Void),
(vec![NATIVE_TYPE::BOOLEAN], NativeType::Boolean),
(vec![NATIVE_TYPE::I1], NativeType::I1),
(vec![NATIVE_TYPE::U1], NativeType::U1),
(vec![NATIVE_TYPE::I2], NativeType::I2),
(vec![NATIVE_TYPE::U2], NativeType::U2),
(vec![NATIVE_TYPE::I4], NativeType::I4),
(vec![NATIVE_TYPE::U4], NativeType::U4),
(vec![NATIVE_TYPE::I8], NativeType::I8),
(vec![NATIVE_TYPE::U8], NativeType::U8),
(vec![NATIVE_TYPE::R4], NativeType::R4),
(vec![NATIVE_TYPE::R8], NativeType::R8),
(vec![NATIVE_TYPE::INT], NativeType::Int),
(vec![NATIVE_TYPE::UINT], NativeType::UInt),
(vec![NATIVE_TYPE::VARIANTBOOL], NativeType::VariantBool),
(vec![NATIVE_TYPE::IINSPECTABLE], NativeType::IInspectable),
(vec![NATIVE_TYPE::HSTRING], NativeType::HString),
];
for (input, expected) in test_cases {
let mut parser = MarshallingParser::new(&input);
let result = parser.parse_native_type().unwrap();
assert_eq!(result, expected);
}
}
#[test]
fn test_parse_lpstr() {
let input = vec![NATIVE_TYPE::LPSTR, 0x05];
let mut parser = MarshallingParser::new(&input);
let result = parser.parse_native_type().unwrap();
assert_eq!(
result,
NativeType::LPStr {
size_param_index: Some(5)
}
);
let input = vec![NATIVE_TYPE::LPSTR, NATIVE_TYPE::END];
let mut parser = MarshallingParser::new(&input);
let result = parser.parse_native_type().unwrap();
assert_eq!(
result,
NativeType::LPStr {
size_param_index: None
}
);
}
#[test]
fn test_parse_lputf8str() {
let input = vec![NATIVE_TYPE::LPUTF8STR, 0x10];
let mut parser = MarshallingParser::new(&input);
let result = parser.parse_native_type().unwrap();
assert_eq!(
result,
NativeType::LPUtf8Str {
size_param_index: Some(16)
}
);
let input = vec![NATIVE_TYPE::LPUTF8STR, NATIVE_TYPE::END];
let mut parser = MarshallingParser::new(&input);
let result = parser.parse_native_type().unwrap();
assert_eq!(
result,
NativeType::LPUtf8Str {
size_param_index: None
}
);
}
#[test]
fn test_parse_array() {
let input = vec![NATIVE_TYPE::ARRAY, NATIVE_TYPE::I4, 0x03, 0x01];
let mut parser = MarshallingParser::new(&input);
let result = parser.parse_native_type().unwrap();
assert_eq!(
result,
NativeType::Array {
element_type: Box::new(NativeType::I4),
num_element: Some(1),
num_param: Some(3)
}
);
let input = vec![NATIVE_TYPE::ARRAY, NATIVE_TYPE::I4, 0x03];
let mut parser = MarshallingParser::new(&input);
let result = parser.parse_native_type().unwrap();
assert_eq!(
result,
NativeType::Array {
element_type: Box::new(NativeType::I4),
num_element: None,
num_param: Some(3)
}
);
let input = vec![NATIVE_TYPE::ARRAY, NATIVE_TYPE::I4];
let mut parser = MarshallingParser::new(&input);
let result = parser.parse_native_type().unwrap();
assert_eq!(
result,
NativeType::Array {
element_type: Box::new(NativeType::I4),
num_element: None,
num_param: None
}
);
}
#[test]
fn test_parse_fixed_array() {
let input = vec![NATIVE_TYPE::FIXEDARRAY, 0x0A, NATIVE_TYPE::I4];
let mut parser = MarshallingParser::new(&input);
let result = parser.parse_native_type().unwrap();
assert_eq!(
result,
NativeType::FixedArray {
size: 10,
element_type: Some(Box::new(NativeType::I4))
}
);
let input = vec![NATIVE_TYPE::FIXEDARRAY, 0x0A, NATIVE_TYPE::END];
let mut parser = MarshallingParser::new(&input);
let result = parser.parse_native_type().unwrap();
assert_eq!(
result,
NativeType::FixedArray {
size: 10,
element_type: None
}
);
}
#[test]
fn test_parse_complete_descriptor() {
let input = vec![NATIVE_TYPE::I4, NATIVE_TYPE::END];
let descriptor = parse_marshalling_descriptor(&input).unwrap();
assert_eq!(descriptor.primary_type, NativeType::I4);
assert_eq!(descriptor.additional_types.len(), 0);
let input = vec![
NATIVE_TYPE::LPSTR,
0x01, NATIVE_TYPE::BOOLEAN, NATIVE_TYPE::END, ];
let descriptor = parse_marshalling_descriptor(&input).unwrap();
assert_eq!(
descriptor.primary_type,
NativeType::LPStr {
size_param_index: Some(1)
}
);
assert_eq!(descriptor.additional_types.len(), 1);
assert_eq!(descriptor.additional_types[0], NativeType::Boolean);
let input = vec![NATIVE_TYPE::END];
let descriptor = parse_marshalling_descriptor(&input).unwrap();
assert_eq!(descriptor.primary_type, NativeType::End);
assert_eq!(descriptor.additional_types.len(), 0);
}
#[test]
fn test_error_conditions() {
let input: Vec<u8> = vec![];
let result = parse_marshalling_descriptor(&input);
assert!(result.is_err());
assert!(matches!(result.unwrap_err(), Error::OutOfBounds { .. }));
let input = vec![0xFF];
let result = parse_marshalling_descriptor(&input);
assert!(result.is_err());
let input = vec![NATIVE_TYPE::LPSTR, 0xC0]; let result = parse_marshalling_descriptor(&input);
assert!(result.is_err());
assert!(matches!(result.unwrap_err(), Error::OutOfBounds { .. }));
}
#[test]
fn test_parse_struct() {
let input = vec![NATIVE_TYPE::STRUCT, 0x04, 0x20, NATIVE_TYPE::END];
let mut parser = MarshallingParser::new(&input);
let result = parser.parse_native_type().unwrap();
assert_eq!(
result,
NativeType::Struct {
packing_size: Some(4),
class_size: Some(32)
}
);
let input = vec![NATIVE_TYPE::STRUCT, 0x04, NATIVE_TYPE::END];
let mut parser = MarshallingParser::new(&input);
let result = parser.parse_native_type().unwrap();
assert_eq!(
result,
NativeType::Struct {
packing_size: Some(4),
class_size: None
}
);
let input = vec![NATIVE_TYPE::STRUCT, NATIVE_TYPE::END];
let mut parser = MarshallingParser::new(&input);
let result = parser.parse_native_type().unwrap();
assert_eq!(
result,
NativeType::Struct {
packing_size: None,
class_size: None
}
);
}
#[test]
fn test_parse_custom_marshaler() {
let input = vec![
NATIVE_TYPE::CUSTOMMARSHALER,
0x41,
0x42,
0x43,
0x44,
0x00,
0x4E,
0x61,
0x74,
0x69,
0x76,
0x65,
0x00,
0x43,
0x6F,
0x6F,
0x6B,
0x69,
0x65,
0x00,
0x54,
0x79,
0x70,
0x65,
0x00,
];
let mut parser = MarshallingParser::new(&input);
let result = parser.parse_native_type().unwrap();
assert_eq!(
result,
NativeType::CustomMarshaler {
guid: "ABCD".to_string(),
native_type_name: "Native".to_string(),
cookie: "Cookie".to_string(),
type_reference: "Type".to_string(),
}
);
}
}