#binary-encoding #binary-format #encode-decode #pickle #world #vodozemac #libolm

matrix-pickle

Matrix世界中使用的简单二进制编码格式

3个不稳定版本

0.2.0 2024年3月25日
0.1.1 2023年10月6日
0.1.0 2022年11月16日

#541 in 编码

Download history 1893/week @ 2024-04-23 1896/week @ 2024-04-30 1568/week @ 2024-05-07 1663/week @ 2024-05-14 1576/week @ 2024-05-21 1494/week @ 2024-05-28 1355/week @ 2024-06-04 1334/week @ 2024-06-11 1421/week @ 2024-06-18 1397/week @ 2024-06-25 1321/week @ 2024-07-02 1532/week @ 2024-07-09 1742/week @ 2024-07-16 1774/week @ 2024-07-23 1253/week @ 2024-07-30 1085/week @ 2024-08-06

6,073 个月下载量
11 个crate中(通过 vodozemac)使用

MIT 许可证

20KB
267

Build Status codecov License Docs

Matrix世界中使用的简单二进制编码格式。

matrix-pickle 二进制编码格式在 libolmvodozemac 加密库中使用。

如何使用

使用 matrix-pickle 最简单的方法是使用 derive 宏

use anyhow::Result;
use matrix_pickle::{Encode, Decode};

fn main() -> Result<()> {
    #[derive(Clone, Debug, Decode, Encode, PartialEq, Eq)]
    struct MyStruct {
        public_key: [u8; 32],
        data: Vec<u8>,
    }
    
    let data = MyStruct {
        public_key: [5u8; 32],
        data: vec![1, 2, 3],
    };
    
    let encoded = data.encode_to_vec()?;
    let decoded = MyStruct::decode_from_slice(&encoded)?;
    
    assert_eq!(data, decoded);

    Ok(())
}

格式定义

matrix-pickle 编码大多数值时没有元数据,通常是直接编码结构体中的字节。

下表定义了常见类型的编码方式。

类型 示例值 编码值 注释
u8 255 [FF] 直接编码
bool true [01] 编码前转换为 u8
[u8;N] [1u8, 2u8] [01, 02] 直接编码
u32 16 [00, 00, 00, 10] 以大端形式编码为字节数组
usize 32 [00, 00, 00, 20] 编码前转换为 u32
&[T] &[3u8, 4u8] [00, 00, 00, 02, 03, 04] 首先编码长度,然后编码每个元素

Derive 支持

只要 crate 中的类型实现了 EncodeDecode,则此 crate 支持 struct 和 enum 的 derive 实现。

Structs

Structs 的 derive 支持简单地按照定义的顺序编码 struct 的每个字段,例如

use std::io::Write;
use matrix_pickle::{Encode, EncodeError};

struct Foo {
    first: [u8; 32],
    second: Vec<u8>,
}

impl Encode for Foo {
    fn encode(&self, writer: &mut impl Write) -> Result<usize, EncodeError> {
        let mut ret = 0;

        // Encode the first struct field.
        ret += self.first.encode(writer)?;
        // Now encode the second struct field.
        ret += self.second.encode(writer)?;

        Ok(ret)
    }
}

Enums

另一方面,首先将 variant 的数量编码为 u8,然后编码枚举的值。

仅支持具有单个关联数据值的 variant 的枚举。

use std::io::Write;
use matrix_pickle::{Encode, EncodeError};

enum Bar {
    First(u32),
    Second(u32),
}

impl Encode for Bar {
    fn encode(&self, writer: &mut impl Write) -> Result<usize, EncodeError> {
        let mut ret = 0;

        match self {
            Bar::First(value) => {
                // This is our first variant, encode a 0u8 first.
                ret += 0u8.encode(writer)?;
                // Now encode the associated value.
                ret += value.encode(writer)?;
            },
            Bar::Second(value) => {
                // This is our second variant, encode a 1u8 first.
                ret += 1u8.encode(writer)?;
                // Now encode the associated value.
                ret += value.encode(writer)?;
            },
        }

        Ok(ret)
    }
}

编码和解码机密值

对于解码预期为机密值的值,请确保将数组装箱。我们有一个辅助属性,它会提醒您预期保持机密性的值应该被装箱。

只需使用#[secret]属性来注释任何结构体字段。

如果打算作为秘密的值没有被封装,编译器将抛出错误。例如,以下代码段将无法编译。

use matrix_pickle::{Encode, Decode};

#[derive(Encode, Decode)]
struct Key {
    #[secret]
    private: [u8; 32],
    public: [u8; 32],
}

另一方面,这个示例可以编译。

use matrix_pickle::{Encode, Decode};

#[derive(Encode, Decode)]
struct Key {
    #[secret]
    private: Box<[u8; 32]>,
    public: [u8; 32],
}

与bincode的比较

二进制格式类似于bincode存储库提供的格式,配置如下

let config = bincode::config::standard()
    .with_big_endian()
    .with_fixed_int_encoding()
    .skip_fixed_array_length();

与格式的主要两个区别是

  • bincode使用u64来编码切片长度
  • matrix-pickle使用u32来编码切片长度

其他区别包括

  • 不支持配置编码格式,如果您需要调整格式,请使用bincode。
  • 没有不安全代码。优化以简单为主,而非纯粹的性能

依赖项

~0.3–1MB
~21K SLoC