#serialization #endian #deserialize #encode-decode #encode #decode

resend

resend 是一个易于使用、性能良好、可定制和可扩展的 Rust 库,用于小端/大端序列化和反序列化

4 个版本

0.1.3 2023 年 2 月 18 日
0.1.2 2023 年 2 月 18 日
0.1.1 2022 年 12 月 21 日
0.1.0 2022 年 12 月 19 日

#1532编码

MIT/Apache

80KB
2.5K SLoC

Resend

Resend 是一个易于使用、性能良好、可定制和可扩展的 Rust 库,用于小端/大端序列化和反序列化。

示例

仅两个函数

snd() 用于任何 Write 实现者(文件、TcpStream 等)

rcv() 用于任何 Read 实现者(文件、TcpStream 等)

Cargo.toml

[dependencies]
#with little-endian feature
resend = {version = "0.1", features = ["little"]}

代码

use resend::{Snd, Rcv};

let mut vec = Vec::new();
vec.snd(-8_i8)?;
vec.snd(22_u16)?;
vec.snd(0xFFABCDEF as u32)?;
vec.snd("Test")?;

let mut buf = &vec[..];
let v: i8 = buf.rcv()?;
let v: u16 = buf.rcv()?;
let v: u32 = buf.rcv()?;
let v: String = buf.rcv()?;

派生

[dependencies]
resend = {version = "0.1", features = ["little"]}
resend_derive = "0.1"
use resend::{Snd, Rcv, endian::{Ascii, UTF16}};
use resend_derive::{Snd, Rcv};

#[repr(u32)]
#[derive(Snd, Rcv)]
pub enum DeviceType {
    PrinterType(IoPrinter) = 4,
    ScardType = 0x20,
}
#[derive(Snd, Rcv)]
struct Device{
    device_id: u32,
    #[len(8)]
    dos_name: Ascii,
}

#[derive(Snd, Rcv)]
pub struct IoPrinter{
    device: Device,
    length: u32,
    flags: u32,
    code_page: u32,
    pnp_name_len: u32,
    driver_name_len: u32,
    #[len(pnp_name_len)]
    pnp_name: UTF16,
    #[len(driver_name_len)]
    driver_name: UTF16,
}

...
let dt: DeviceType = stream.rcv()?;
stream.snd(&dt)?;

性能良好

基于 Write/Read 特性,无中间变量。

格式

  • 布尔值序列化为 0_u8(false)或 1_u8(true)。
  • String、Vec、Array、Slice、Collections、Ascii、UTF16:u32_length_header + data,如果使用 "len" 属性则无长度头。
  • Option 序列化为 bool_header + 可选数据,如果使用 "when" 属性则无 bool_header。
  • Enum 序列化为 tag value(int) + 可选数据。使用 "repr" 属性来设置标签值的尺寸。
#[derive(Snd, Rcv)]
#[repr(u16)]
enum Color {
    Red,
    Blue = 32,
    Green =4,
}

Color::Red 序列化为 0_u16。Color::Blue 序列化为 32_u16。

#[repr(u32)]
#[derive(Snd, Rcv)]
pub enum DeviceType {
    PrinterType(IoPrinter) = 4,
    ScardType = 0x20,
}

DeviceType::PrinterType(printer) 序列化为 4_u32 + IoPrinter 数据。DeviceType::ScardType 序列化为 0x20_u32。

请注意:非单位变体的判别符从 Rust 1.66 开始稳定,您必须使用 Rust nightly 版本以支持早期版本。

可定制(属性)

  1. 同时发送小端和大端,使用 resend::endian::little::LE 和 resend::endian::big::BE
stream.snd(BE(100_u32))?;
  1. 使用 #[skip] 属性时不进行序列化。

  2. String、Vector 等的长度可以使用 #[len(field_name_or_const)] 属性从另一个字段或常量中获取

#[len(pnp_name_len)]
#[len(8)]
  1. 在 Option 字段上使用 #[when(expr)] 属性。只有当 expr 为真时,此字段才会被反序列化。警告:"expr" 在序列化时不会被检查,此情况下没有额外的 bool 值。
#[when(code_page > 0)]
#[when((flags & 2) != 0)]
  1. 长度可以是 u16 或 VLQ(默认为 u32)
resend = {version = "0.1", features = ["little", "len_16"]}
resend = {version = "0.1", features = ["big", "len_vlq"]}
  1. 具有特性的限制长度:MAX_LEN_100M、MAX_LEN_500M、MAX_LEN_2G
resend = {version = "0.1", features = ["little", "len_16", "MAX_LEN_100M"]}

可扩展

例如,您想要一个具有 可变长度量 的字符串

pub struct VarLenString (pub String);

impl Sendable for VarLenString {
    fn snd_to<S>(&self, writer: &mut S) -> io::Result<()>
    where
        S: resend::Sender {
        writer.snd(resend::endian::VLQ(self.0.len()))?;
        writer.snd_all(self.0.as_bytes())
    }
}

impl Receivable for VarLenString {
    fn rcv_from<R>(reader: &mut R) -> io::Result<Self>
    where
        R: resend::Receiver {
        let len: VLQ = reader.rcv()?;
        let b = reader.rcv_bytes(*len)?;
        let s = std::str::from_utf8(&b)?;
        Ok(Self(s.to_string()))
    }
}

Resend 包含以下类型以方便您使用

use resend::endian::{Ascii, UTF16, UTF16Char, VLQ};

如果需要 "len(field_name)" 属性在您的类型上工作,则实现 resend::FromReader 和 resend::IntoWriter。例如

impl FromReader for Vec<u8> {
    #[inline]
    fn from_reader<R: Receiver>(reader: &mut R, len: usize) -> crate::Result<Self> {
        reader.rcv_bytes(len)
    }
}

impl IntoWriter for Vec<u8> {
    #[inline]
    fn into_writer<S: Sender>(&self, writer: &mut S, len: usize) -> crate::Result<()> {
        let len_s = self.len();
        let (l, left) = if len > len_s {
            (len_s, len - len_s)
        } else {
            (len, 0)
        };

        let b = &self[..l];

        writer.snd_all(b)?;

        if left > 0 {
            writer.snd_all(&vec![0; left])?;
        }

        Ok(())
    }
}

提示

  1. 具有#[len(field_name_or_const)]属性的字符串、Ascii和UTF16:如果指定的长度大于实际长度,则在序列化时会附加额外的'\0',在反序列化后移除这些额外的'\0';如果指定的长度小于实际长度,字符串将被截断到该长度。这适用于需要以null终止的固定长度字符串。

  2. 将整数转换为枚举

//Conver little-endian u16 to Blue ("little" feature)
//[u8] doesn't implement Read, convert it to &[u8] with as_ref()
let c: Color = [32_u8, 0].as_ref().rcv()?;
  1. 使用枚举序列化面向对象类
//type value (enum tag value) after the parent class
struct YourObject {
    parent: ParentClass,
    child: EnumOfChildClass,
}
//type value (enum tag value) before the parent class
struct Child {
    parent: ParentClass,
    child_field,
    ...
}
enum {
    child1,
    child2,
}

  1. resend::endian:Length处理3种类型:u32、u16、VLQ。最好直接在您的对象中使用这种Length类型。

许可证

MIT或Apache-2.0

致谢

本库是为Remote Spark Corp的RDP(远程桌面协议)项目开发的。Remote Spark Corp的RDP (Remote Desktop Protocol) 项目

依赖项

~1.5MB
~35K SLoC