#parse #format #compiler #pattern #compiler-plugin #map #bitrange

bitrange_plugin

解析格式的位域编译器插件

4个版本

0.3.0 2022年4月3日
0.1.2 2018年4月7日
0.1.1 2018年4月6日
0.1.0 2018年4月6日

#34 in #compiler-plugin


bitrange 中使用

MIT 许可证

11KB
262

bitrange

入门指南

要开始使用,请将以下内容添加到您的 Cargo.toml

[dependencies]
bitrange = { git = "https://github.com/trangar/bitrange" }
bitrange_plugin = { git = "https://github.com/trangar/bitrange" }

然后,将以下代码添加到您的 main.rslib.rs

#[macro_use]
extern crate bitrange;
#[macro_use]
extern crate bitrange_plugin;

由于位域使用了尚未稳定的 proc_macro 功能,bitrange 需要编译器的夜间版本

引用字符串

由于RFC 2320的未决状态,stringify! 不能在过程宏属性中使用。此crate生成的代码包含

#[derive(Bitrange)]
#[BitrangeMask = $format]
#[BitrangeSize = $struct_size_string]

其中 $format$struct_size_string 来自 bitrange 宏。

在这个例子中,我们想使用 stringify!($format)stringify($struct_size_string),然而这直到 RFC 2320 实施才能工作。

因此,格式需要被引用,并且我们需要对类型进行两次注解。例如:

bitrange! {
    Test: u8, "u8",
    "aaaa_bbbb",
    a: first,
    b: second
}

而不是预期的

bitrange! {
    Test: u8,
    aaaa_bbbb,
    a: first,
    b: second
}

示例

Bitrange帮助您将位字段映射到适当的getter和setter。

比如说,您正在尝试制作一个IP解析器。该rfc将为您提供以下内容

    0                   1                   2                   3
    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |Version|  IHL  |Type of Service|          Total Length         |

如果您想用Rust解析这部分,您需要做以下映射

  • 前4位映射到 version
  • 接下来的4位映射到 ihl
  • 接下来的8位映射到 type_of_service
  • 最后16位映射到 total_length

有了位域,您可以轻松地将字节映射到字段。要解析协议的这部分,只需简单地写下

#![feature(proc_macro)]
#[macro_use]
extern crate bitrange;

bitrange! {
    IpHeader: u32, "u32",                    // struct name
    "aaaa_bbbb_cccccccc_dddddddddddddddd",   // pattern that we're matching against
    a: version,                              // map character 'a' to field 'version'
    b: ihl,                                  // map character 'b' to field 'ihl'
    c: type_of_service,                      // map character 'c' to field 'type_of_service'
    d: total_length                          // map character 'd' to field 'total_length'
}

fn main() {
    let header = IpHeader::from(0b0001_0010_00000011_0000000000000100);
    assert_eq!(header.version(), 0b0001);
    assert_eq!(header.ihl(), 0b0010);
    assert_eq!(header.type_of_service(), 0b0011);
    assert_eq!(header.total_length(), 0b0100);
}

如果您想使一个字段可变,只需在字段映射中添加第二个标识符,例如:


bitrange! {
    IpHeader: u32, "u32",                    // struct name
    "aaaa_bbbb_cccccccc_dddddddddddddddd",   // pattern that we're matching against
    a: version set_version,                  // map character 'a' to field 'version', and create setter 'set_version'
    b: ihl,                                  // map character 'b' to field 'ihl'
    c: type_of_service,                      // map character 'c' to field 'type_of_service'
    d: total_length                          // map character 'd' to field 'total_length'
}

fn main() {
    let mut header = IpHeader::from(0b0001_0010_00000011_0000000000000100);
    assert_eq!(header.version(), 0b0001);
    assert_eq!(header.ihl(), 0b0010);
    assert_eq!(header.type_of_service(), 0b0011);
    assert_eq!(header.total_length(), 0b0100);

    header.set_version(0b0100);
    assert_eq!(header.version(), 0b0100);
}

此外,您还可以为必须始终为0或1的位定义约束。


bitrange! {
    Test: u8, "u8",
    // from left (highest) to right (lowest)
    // first 3 bits are mapped to a
    // the next bit is always 1
    // the next bit is always 0
    // the last 3 bits are mapped to b
    "aaa1_0bbb",
    a: first,
    b: second
}

fn main() {
    // This panics at runtime
    // Because the 4th highest bit should always be 1
    // Test::from(0);

    // The enum also implements Default, so you can simply do:
    let _test = Test::default();
    // And this will have value 0b0001_0000
}

编译时检查

bitrange还会在编译时检查字段是否存在。

bitrange! { 
    Test: u8, "u8",
    "aaa1_0bbb",
    a: first,
    b: second,
    c: third // this will panic with
             // Token 'c' is not found in pattern "aaa10bbb"
}

然而,这并不适用于未映射的字段。

bitrange! {
    Test: u8, "u8",
    "aaa1_0bbb",
    a: first,
    // b is not mapped
    // Does not give a warning
}

依赖关系

~1.5MB
~35K SLoC