1 个不稳定版本
0.5.0 | 2022 年 5 月 26 日 |
---|
#2451 in Rust 模式
8KB
Packetrs
Packetrs 是一个 Rust 宏,它可以自动生成在宏属性上对结构体包的序列化和反序列化(最终)代码。它的 API 极大地受到了/抄袭了 Deku 的启发。这主要是出于我个人娱乐/教育目的而实现的。
示例
假设你想解析一个 STUN 头部,其布局如下
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
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|0 0| STUN Message Type | Message Length |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Magic Cookie |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| |
| Transaction ID (96 bits) |
| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
在 Packetrs 中,你会这样做
#[derive(Debug, PacketrsRead)]
pub struct StunHeader {
#[packetrs(fixed = "0")]
pub reserved: u2,
pub msg_type: u14,
pub msg_length: u16,
#[packetrs(fixed = "0x2112A442")]
pub cookie: u32,
#[packetrs(count = "12")]
pub transaction_id: Vec<u8>,
}
PacketrsRead 特性
在结构体或枚举上派生 PacketrsRead
会为该类型生成 PacketrsRead
特性的实现
pub trait PacketRsRead<Ctx>: Sized {
fn read(buf: &mut BitCursor, ctx: Ctx) -> PacketRsResult<Self>;
}
待办事项:一旦 BitCursor 仓库上线,请链接到它
PacketrsRead 属性
上下文 & 必要上下文
结构体、字段、枚举和枚举变体都可能需要额外的上下文才能读取。使用 required_ctx
属性,结构体、字段、枚举或枚举变体可以定义必须传递给其 PacketrsRead::read
方法的值或值。另一面是 ctx
属性,它定义了将被传递给任何标记的读取方法的值。
在这个例子中,Address
枚举需要一个名为 address_family
的 u8
值,并使用该值作为 key
来区分枚举变体。
#[derive(Debug, PacketrsRead)]
#[packetrs(required_ctx = "address_family: u8", key = "address_family")]
pub enum Address {
#[packetrs(id = "0x01")]
IpV4(u32),
#[packetrs(id = "0x02")]
IpV6(u128),
}
在这里,另一个枚举将一个 address_family
字段作为上下文传递给一个 Address
字段
#[derive(Debug, PacketrsRead)]
#[packetrs(required_ctx = "message_type: u16, length: u16", key = "message_type")]
pub enum StunAttribute {
#[packetrs(id = "0x0001")]
MappedAddress {
reserved: u8,
address_family: u8,
port: u16,
#[packetrs(ctx = "address_family")]
address: Address,
},
#[packetrs(id = "0x0020")]
XorMappedAddress {
reserved: u8,
address_family: u8,
x_port: u16,
#[packetrs(ctx = "address_family")]
x_address: Address,
},
#[packetrs(id = "0x0006", count = "length")]
Username(Vec<u8>),
#[packetrs(id = "0x0008", count = "length")]
MessageIntegrity(Vec<u8>),
}
泛型字段属性
这些属性可以应用于结构体或枚举变体的字段
计数
可以在集合字段(Vec)上使用 count
属性来描述应该读取多少个内部类型(T
)到集合中。计数可以是一个表达式,并且可以引用任何将在作用域中的字段或方法。
#[derive(PacketrsRead)]
struct MyStruct {
pub length: u8
#[packetrs(count = "length")]
pub values: Vec<u8>
}
fn get_length(length_val: u8) -> u8 { ... }
#[derive(PacketrsRead)]
struct MyOtherStruct {
pub length: u8
#[packetrs(count = "get_length(length)")]
pub values: Vec<u8>
}
固定
fixed
属性描述允许定义一个读取字段必须拥有的值。读取字段后,如果读取的值不匹配 fixed
属性中定义的值,则返回一个错误。
struct Foo {
#[packetrs(fixed = "0b0000")]
reserved: u4,
other_field: u8
}
读取器
reader
属性允许使用自定义的读取方法,而不是自动生成一个。该方法必须返回一个 PacketRsResult<T>
,其中 T
与注解字段的类型相匹配。
fn parse_stun_attributes(buf: &mut BitCursor, _ctx: ()) -> PacketRsResult<Vec<StunAttribute>> { ... }
#[derive(Debug, PacketrsRead)]
pub struct StunPacket {
pub header: StunHeader,
#[packetrs(reader = "parse_stun_attributes")]
pub attributes: Vec<StunAttribute>,
}
枚举/枚举变体属性
这些属性既适用于枚举也适用于枚举变体
键 & Id
key
属性在枚举中必须存在,它是一个表达式,用于匹配语句中区分变体。它的对应属性是 id
,它在枚举变体中必须存在。它应该对应于 key
的结果,以区分其注解变体。
#[derive(Debug, PacketrsRead)]
#[packetrs(required_ctx = "address_family: u8", key = "address_family")]
pub enum Address {
#[packetrs(id = "0x01")]
IpV4(u32),
#[packetrs(id = "0x02")]
IpV6(u128),
}
具有未命名字段的结构体或枚举变体
未命名字段不能被注解,但它们足够常见,因此有特殊支持将结构体或枚举变体自身的注解“传递”到未命名字段。结构体或枚举变体上的任何注解都将被视为存在于所有未命名字段上。待办事项:示例
待办事项
[ ] 更好的文档 [ ] 更好的编译时错误信息 [ ] 单元测试 - 仍需研究如何为 proc macros 进行单元测试 [ ] 实现 PacketrsWrite [ ] 更多功能:为集合字段实现读取直到/当...
依赖项
~4MB
~84K SLoC