#parser #generate #binary #endian #byte #read #simple-parse

simple_parse_derive

实现simple_parse特质的derive宏

9个不稳定版本 (3个重大变更)

0.6.3 2021年3月18日
0.6.2 2021年3月5日
0.6.1 2020年12月29日
0.5.0 2020年12月22日
0.1.0 2020年6月23日

#77 in #endian

每月下载量38次
simple_parse 中使用

MIT/Apache

60KB
1K SLoC

simple_parse

crates.io mio Lines of Code

simple_parse 是一个声明式二进制流解析器,旨在为您的自定义类型生成尽可能高效的解析代码,同时保持安全性。

功能 描述
单次“复制” 尽可能将数据直接读取到其最终目的地
内置端序支持 使用 endian 注释结构体/字段可以控制数字的解析方式
转换回字节 除了解析任意字节外,simple_parse 还允许将结构体以二进制形式回溯

如果 simple_parse 无法描述您的复杂/非标准二进制格式,请查看 dekubinrw

使用方法

查看 client_server 以获取完整示例。

use ::simple_parse::{SpRead, SpWrite};

#[derive(SpRead, SpWrite)]
pub enum Message {
    Ping,
    Pong,
    Chat(String),
    Key {
        private: Vec<u8>,
        public: Vec<u8>,
    },
    Disconnect,
}

pub fn main() {
    /* <...> */
    
    // Declare a destination buffer to use when parsing
    let mut dst: MaybeUninit<Message> = MaybeUninit::uninit();

    loop {
        // Receive & parse bytes from the socket as a `Message` using SpRead
        let msg = Message::from_reader(&mut sock, &mut dst).expect("[server] Failed to receive message");

        match msg {
            Message::Ping => {
                println!("[server] Got Ping ! Sending Pong...");
                // Respond with a Pong using SpWrite
                (Message::Pong).to_writer(&mut sock).expect("[server] Failed to send Pong");
            },
            Message::Pong => println!("[server] got pong !"),
            Message::Chat(s) => println!("[server] Received chat : '{s}'"),
            Message::Key{private, public} => println!("[server] got keys : {private:X?}:{public:X?}"),
            Message::Disconnect => break,
        }
    }

    /* <...> */
}

更多示例请参阅: examples/

项目目标

按照优先级大致顺序,simple_parse 旨在提供

  1. 安全性
  2. 性能
  3. 易用性
  4. 适应性

换句话说,simple_parse 将尝试生成最高效的代码,同时从不妥协安全性。

其次,将通过提供适用于大多数情况的默认实现来优先考虑易用性,同时允许对无法控制的二进制格式进行一些自定义。

高级用法

simple_parse 提供了几种增强生成解析代码的方法。请参阅 attributes.rs 以获取选项的完整列表。

验证

可以在解析/写入过程的任何位置插入验证“钩子”。

例如,BMP图像头必须始终以两个字节开始,分别是 'BM'

#[derive(SpRead, SpWrite)]
struct BmpHeader {
    #[sp(validate = "validate_header")]
    magic: u16,
    #[sp(endian="big")]
    size: u32,
    reserved1: u16,
    reserved2: u16,
    pixel_array_offset: u32,
    // ...

这告诉 simple_parse 在读取时填充 u16 后直接调用 validate_header(magic: &u16, ctx: &mut SpCtx),并在写入时将结构体作为字节写入之前。

自定义长度(适用于TLV样式)

simple_parse 通过在元素前简单添加元素数量(len)来提供动态大小类型的默认实现。

例如,默认情况下,包含三个值的 Vec<u8> 将映射为

// [len] | [len] * [elements]
[3u32][val1][val2][val3]

当解析不遵循此布局的二进制格式时,您可以注释动态大小字段上的 len

pub struct File {
    pub content_len: u16,
    pub filename: String, // Use the default prepended len
    #[sp(len="content_len")]
    pub contents: Vec<u8>, // Use an existing field as the len

content_len 字段将用于填充 contents,并在写入时在该偏移量写入 contents.len()

自定义读取/写入

simple_parse 的默认读取和写入实现不适合您的格式时,您可以使用 readerwriter 属性来覆盖它们。

struct BmpHeader {
    comp_bitmask: u32,
    #[sp(
        reader="BmpComp::read, comp_bitmask",
        writer="BmpComp::write",
    )]
    compression_info: BmpComp,
    //...

读取时,这将生成如下代码

compression_info = BmpComp::read(comp_bitmask: &u32, src: &mut Read, ctx: &mut SpCtx)?;

写入时

written_sz += BmpComp::write(&self.compression_info, ctx: &mut SpCtx, dst: &mut Write)?;

许可证

贡献

除非您明确声明,否则,根据Apache-2.0许可证定义的,您提交的任何有意包含在作品中的贡献,将根据上述条款双许可,无需任何附加条款或条件。

依赖项

~0.7–1.2MB
~26K SLoC