寻求更地道的Rust二进制文件类型解析转换实现方案
我自己实现了一套基于XML描述,把二进制解析后的&[u8]转换成Rust可用类型的方案,但总觉得太复杂。因为对Rust不够熟,暂时没找到更简洁的实现方式。下面是我试了好几次才跑通的代码:
背景
- 读取描述二进制文件记录结构的XML文件(代码中已模拟该步骤,详见注释);
- 每条记录包含
data_type字段,存储为&str或String(取决于存储记录的Rust结构体定义); - 利用XML描述信息自动将二进制文件解析得到的
&[u8]转换为Rust可用类型(代码中模拟了二进制数据,解析二进制文件并非重点)。
简化XML描述文件示例
假设存在更多记录和数据类型,该示例未在工作代码中使用:
use quick_xml; let xml_desc = r#"" <Records> <RecordDesc> <name>Single</name> <number>1</number> <location unit="byte">0</location> <data_type>IEEE754LSBSingle</data_type> <length unit="byte">4</length> </RecordDesc> <RecordDesc> <name>Double</name> <number>1</number> <location unit="byte">5</location> <data_type>IEEE754LSBDouble</data_type> <length unit="byte">8</length> </RecordDesc> </Records> ""#; // quick_xml/serde 用于将XML字符串解析为Rust结构体 #[derive(Serialize, Deserialize, Debug)] pub struct Records { pub records: Vec<Record>, } #[derive(Serialize, Deserialize, Debug)] pub struct Record { ..., pub data_type: &str, // 也可以是String,本示例未使用该结构体 ..., } // 根据每条记录的data_type将数据转换为Rust可用类型
工作代码
第一个宏
用于创建将&mut &[u8]转换为特定Rust类型的函数,示例输出见first_example:
macro_rules! type_cast_function { ($func_name:ident, $endian:ident, $output_type:ty ) => { fn $func_name(input: &mut &[u8]) -> $output_type { let (int_bytes, _) = input.split_at(std::mem::size_of::<$output_type>()); <$output_type>::$endian(int_bytes.try_into().unwrap()) } }; }
第二个宏
用于创建从DataTypes枚举变体中提取特定值的impl块,示例输出见first_example:
macro_rules! create_unwrap_impl_for_type { ($unwrap_name:ident, $variant:path, $output_type:ty) => { impl DataTypes { pub fn $unwrap_name(self) -> $output_type { match self { $variant(val) => val, _ => panic!(), } } } }; }
创建多数据类型枚举
注意:枚举变体名称大小写与xml_desc中的一致:
#[derive(Debug)] pub enum DataTypes { // 4字节 IEEE754LSBSingle(f32), // 8字节 IEEE754LSBDouble(f64), }
示例一
匹配data_type: &str描述,生成对应的转换函数和提取impl块供后续使用:
fn first_example(){ // 模拟从二进制文件解析得到的数据 let mut data: &[u8] = &[172, 152, 111, 195]; let mut data2: &[u8] = &[172, 152, 111, 195, 117, 93, 133, 192]; // 模拟遍历不同类型的记录 for dtype in ["IEEE754LSBSingle", "IEEE754LSBDouble"] { match dtype { "IEEE754LSBSingle" => { create_unwrap_impl_for_type!(unwrap_le_f32,DataTypes::IEEE754LSBSingle,f32); /* 生成代码: impl DataTypes { pub fn unwrap_le_f32(self) -> f32 { match self { DataTypes::IEEE754LSBSingle(val) => val, _ => panic!(), } } } */ type_cast_function!(read_le_f32, from_le_bytes, f32); /* 生成代码: fn read_le_f32(input: &mut &[u8]) -> f32 { let (int_bytes, _) = input.split_at(std::mem::size_of::<f32>()); f32::from_le_bytes(int_bytes.try_into().unwrap()) } */ let single = DataTypes::IEEE754LSBSingle(read_le_f32(&mut data)).unwrap_le_f32(); println!("First Example\tIEEE754LSBSingle {:?}",single); }, "IEEE754LSBDouble" => { create_unwrap_impl_for_type!(unwrap_le_f64,DataTypes::IEEE754LSBDouble,f64); /* 生成代码: impl DataTypes { pub fn unwrap_le_f64(self) -> f64 { match self { DataTypes::IEEE754LSBDouble(val) => val, _ => panic!(), } } } */ type_cast_function!(read_le_f64, from_le_bytes, f64); /* 生成代码: fn read_le_f64(input: &mut &[u8]) -> f64 { let (int_bytes, _) = input.split_at(std::mem::size_of::<f64>()); f64::from_le_bytes(int_bytes.try_into().unwrap()) } */ let double = DataTypes::IEEE754LSBDouble(read_le_f64(&mut data2)).unwrap_le_f64(); println!("First Example\tIEEE754LSBDouble {:?}",double); }, _ => panic!(), }; } }
统一宏
整合上述两个宏,用于生成转换函数和提取impl块,示例二使用该宏:
macro_rules! generate_casting_extraction_functions { ($func_name:ident, $endian:ident, $unwrap_name:ident, $variant:path, $output_type:ty) => { create_unwrap_impl_for_type!($unwrap_name, $variant, $output_type); type_cast_function!($func_name, $endian, $output_type); } }
示例二
匹配data_type: &str描述,通过统一宏生成对应的转换函数和提取impl块供后续使用:
fn second_example(){ // 模拟从二进制文件解析得到的数据 let mut data: &[u8] = &[172, 152, 111, 195]; let mut data2: &[u8] = &[172, 152, 111, 195, 117, 93, 133, 192]; // 模拟遍历不同类型的记录 for dtype in ["IEEE754LSBSingle", "IEEE754LSBDouble"] { match dtype { "IEEE754LSBSingle" => { // 生成代码与示例一一致 generate_casting_extraction_functions!(read_le_f32_2, from_le_bytes,unwrap_le_f32_2,DataTypes::IEEE754LSBSingle,f32); let single = DataTypes::IEEE754LSBSingle(read_le_f32_2(&mut data)).unwrap_le_f32_2(); println!("Second Example\tIEEE754LSBSingle {:?}",single); }, "IEEE754LSBDouble" => { // 生成代码与示例一一致 generate_casting_extraction_functions!(read_le_f64_2, from_le_bytes,unwrap_le_f64_2,DataTypes::IEEE754LSBDouble,f64); let double = DataTypes::IEEE754LSBDouble(read_le_f64_2(&mut data2)).unwrap_le_f64_2(); println!("Second Example\tIEEE754LSBDouble {:?}",double); }, _ => panic!(), }; } }
主函数
fn main() { first_example(); second_example(); }
内容的提问来源于stack exchange,提问作者William
相关产品推荐
相关产品推荐

