2个不稳定版本

0.2.0 2021年6月29日
0.1.0 2020年10月15日

#44#bidirectional

Download history 51/week @ 2024-03-11 45/week @ 2024-03-18 28/week @ 2024-03-25 81/week @ 2024-04-01 10/week @ 2024-04-08 7/week @ 2024-04-22 43/week @ 2024-04-29 23/week @ 2024-05-06 69/week @ 2024-05-13 81/week @ 2024-05-20 24/week @ 2024-05-27 38/week @ 2024-06-03 29/week @ 2024-06-10 62/week @ 2024-06-17 61/week @ 2024-06-24

192 每月下载量
declio 中使用

MIT 许可证

27KB
709

declio

一个声明式I/O序列化库。

declio提供了一对特质,EncodeDecode,这些特质促进了二进制数据流(std::io特质)和任意数据类型之间的双向转换。

这些特质实现了std中的许多类型;整数和浮点原始类型可以编码和解码为其大端或小端二进制表示,而集合和其他容器类型编码和解码它们包含的数据。

但是,也有一些值得注意的例外;例如,没有为boolstr/String提供实现。这是因为这些类型有几种常见的表示形式,为了避免意外误用,您需要显式声明它们的表示形式。一些常见的表示形式在util模块中提供,作为包装类型和帮助模块实现。

此crate还提供了一对派生宏,通过默认功能derive,可以实现任意复合数据类型的EncodeDecode。默认情况下,它将按顺序编码和解码所有字段,但它高度可配置,旨在针对二进制格式中发现的许多不同模式。

此crate的灵感主要来自deku,但根据我自己的观点和偏好进行了一些修改。例如,declio使用来自std::io的字节数据流,而不是deku使用的位操作BitVec

示例

让我们从一个简单的例子开始——将单个整数编码到字节数据缓冲区中

use declio::Encode;
use declio::ctx::Endian;

let mut buf: Vec<u8> = Vec::new();
u32::encode(&0xdeadbeef, Endian::Big, &mut buf)
    .expect("encode failed");

assert_eq!(buf, [0xde, 0xad, 0xbe, 0xef]);

在这个例子中,Endian::Big是一个“上下文”值。declio提供这些作为在运行时配置或修改EncodeDecode实现的一种简单方法,而不是使用包装类型或辅助函数。在这种情况下,它告诉Encode实现为u32以大端字节顺序编码字节。它也可以设置为Endian::Little以反转字节顺序,并且通常,Endian可以传递给任何整数和浮点原始类型以产生类似的效果。

上下文还可以用于抽象编码和解码某些类型所必需的一些字段。例如,像Vec这样的可变长度容器在解码期间接受一个[Len]上下文值,这告诉Decode实现应该解码多少个值。这可以是一个编译时常量,如Len(1024),或者它可以在运行时从另一个值创建,如下所示

use declio::Decode;
use declio::ctx::{Len, Endian};

let mut bytes = &[
    // len
    0x00, 0x02,

    // words[0]
    0xde, 0xad,

    // words[1]
    0xbe, 0xef,
][..];

let len = u16::decode(Endian::Big, &mut bytes)
    .expect("decode len failed");

let words: Vec<u16> = Vec::decode((Len(len as usize), Endian::Big), &mut bytes)
    .expect("decode bytes failed");

assert!(bytes.is_empty()); // did we consume the whole buffer?
assert_eq!(words, [0xdead, 0xbeef]);

原因是VecDecode实现不知道如何读取长度值。它不知道二进制格式使用什么整数大小或字节顺序来编码长度;它甚至不知道长度是否已编码!它可能是格式定义中的一部分的一些固定长度。

Vec::decode还可以接受一个额外的上下文值,将其传递给元素解码器,使用一个2元组,如(Len(len as usize), Endian::Big)。然而,在这个例子中,只传递了Len,这也是有效的,并将()作为上下文传递给元素解码器。

推导

以下是一个使用推导宏来编码和解码用户定义的数据类型的例子。这不是推导宏功能的完整演示;有关更完整的参考,请参阅[mod@derive]模块文档。

use declio::{Encode, Decode};
use declio::ctx::{Endian, Len};
use std::convert::TryInto;

#[derive(Debug, PartialEq, Encode, Decode)]
struct WithLength {
    // Context can be passed to the field decoder with a `ctx` attribute.
    #[declio(ctx = "Endian::Little")]
    len: u16,

    // Context may be different for encode and decode,
    // though they should generally be as symmetric as possible.
    // For example, `Vec` requires a `Len` when decoding, but it is
    // optional for encoding. However, it should be provided anyway
    // because it will be used to check that the encoded length is
    // the same as the actual length.
    //
    // Fields declared before this one can be accessed by name
    // (or by `field_0`, `field_1`, etc for tuple structs):
    #[declio(ctx = "Len((*len).try_into()?)")]
    bytes: Vec<u8>,
}

let bytes: Vec<u8> = vec![0xde, 0xad, 0xbe, 0xef];

let with_length = WithLength {
    len: bytes.len().try_into().expect("length out of range"),
    bytes,
};

let encoded: Vec<u8> = declio::to_bytes(&with_length)
    .expect("encode failed");
assert_eq!(encoded, [0x04, 0x00, 0xde, 0xad, 0xbe, 0xef]);

let decoded: WithLength = declio::from_bytes(&encoded)
    .expect("decode failed");

assert_eq!(decoded, with_length);

依赖关系

~2MB
~42K SLoC