2个不稳定版本

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

#1939编码

Download history 17/week @ 2024-04-06 2/week @ 2024-04-13 1/week @ 2024-04-20 39/week @ 2024-04-27 19/week @ 2024-05-04 63/week @ 2024-05-11 79/week @ 2024-05-18 19/week @ 2024-05-25 38/week @ 2024-06-01 23/week @ 2024-06-08 58/week @ 2024-06-15 55/week @ 2024-06-22 62/week @ 2024-06-29 58/week @ 2024-07-06 37/week @ 2024-07-13 44/week @ 2024-07-20

每月 208 次下载

MIT 许可证

42KB
661

declio

声明式I/O序列化库。

declio 提供了一对特质, [Encode] 和 [Decode],它们简化了二进制数据流(std::io 特质)和任意数据类型之间的双向转换。

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

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

此crate还提供了一对 derive 宏,通过默认功能 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] 是一个“上下文”值。《code>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,这也是有效的,并且会将 () 作为上下文传递给元素解码器。

派生

以下是一个示例,它使用 derive 宏来编码和解码用户定义的数据类型。这不是 derive 宏功能的完整演示;有关更完整的参考,请参阅 [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);

依赖关系

~0–270KB