Rust序列化库matrix-pickle的使用,高效二进制数据序列化与反序列化解决方案
Rust序列化库matrix-pickle的使用,高效二进制数据序列化与反序列化解决方案
matrix-pickle是一个简单的二进制编码格式,主要用于Matrix生态系统中。它是libolm和vodozemac加密库中使用的编码格式。
如何使用
最简单的使用方式是使用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] |
先编码长度,然后编码每个元素 |
派生支持
只要内部类型实现了Encode
和Decode
,该库就支持为结构体和枚举派生Encode
和Decode
实现。
结构体
结构体的派生支持按定义顺序编码每个字段:
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;
// 编码第一个结构体字段
ret += self.first.encode(writer)?;
// 现在编码第二个结构体字段
ret += self.second.encode(writer)?;
Ok(ret)
}
}
枚举
枚举首先将变体编号编码为u8
,然后编码枚举值。仅支持包含单个关联数据值的枚举。
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) => {
// 这是第一个变体,先编码0u8
ret += 0u8.encode(writer)?;
// 现在编码关联值
ret += value.encode(writer)?;
},
Bar::Second(value) => {
// 这是第二个变体,先编码1u8
ret += 1u8.encode(writer)?;
// 现在编码关联值
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],
}
完整示例
use anyhow::Result;
use matrix_pickle::{Encode, Decode};
fn main() -> Result<()> {
// 定义包含机密数据和非机密数据的结构体
#[derive(Clone, Debug, Decode, Encode, PartialEq, Eq)]
struct SecureMessage {
#[secret]
secret_key: Box<[u8; 32]>,
public_key: [u8; 32],
payload: Vec<u8>,
message_type: MessageType,
}
// 定义枚举类型
#[derive(Clone, Debug, Decode, Encode, PartialEq, Eq)]
enum MessageType {
Text(usize),
Binary(usize),
Encrypted(usize),
}
// 创建实例
let message = SecureMessage {
secret_key: Box::new([0xAA; 32]),
public_key: [0xBB; 32],
payload: vec![1, 2, 3, 4, 5],
message_type: MessageType::Encrypted(256),
};
// 序列化
let encoded = message.encode_to_vec()?;
println!("Encoded data: {:?}", encoded);
// 反序列化
let decoded = SecureMessage::decode_from_slice(&encoded)?;
println!("Decoded data: {:?}", decoded);
// 验证
assert_eq!(message, decoded);
Ok(())
}
与bincode的比较
二进制格式与bincode crate提供的以下配置类似:
let config = bincode::config::standard()
.with_big_endian()
.with_fixed_int_encoding()
.skip_fixed_array_length();
两个主要区别是:
- bincode使用
u64
编码切片长度 - matrix-pickle使用
u32
编码切片长度
其他区别:
- 不支持配置编码格式,如果需要调整格式,请使用bincode
- 没有使用unsafe代码,优化了简单性而非纯粹性能
1 回复
Rust序列化库matrix-pickle使用指南
简介
matrix-pickle是一个高效的Rust二进制序列化库,专注于高性能的数据序列化与反序列化操作。它特别适合需要处理大量二进制数据的场景,如网络通信、游戏开发或高性能计算应用。
主要特性
- 零拷贝反序列化
- 紧凑的二进制格式
- 高性能序列化/反序列化
- 支持基本类型和自定义结构体
- 内存安全的Rust实现
安装
在Cargo.toml中添加依赖:
[dependencies]
matrix-pickle = "0.1"
基本使用
序列化基本类型
use matrix_pickle::{to_bytes, from_bytes};
fn main() {
// 序列化
let data = 42u32;
let serialized = to_bytes(&data).unwrap();
// 反序列化
let deserialized: u32 = from_bytes(&serialized).unwrap();
assert_eq!(data, deserialized);
}
序列化自定义结构体
use matrix_pickle::{Encode, Decode};
#[derive(Encode, Decode, PartialEq, Debug)]
struct Player {
id: u32,
name: String,
position: (f32, f32),
health: u8,
}
fn main() {
let player = Player {
id: 1,
name: "Alice".to_string(),
position: (10.5, 20.3),
health: 100,
};
let serialized = matrix_pickle::to_bytes(&player).unwrap();
let deserialized: Player = matrix_pickle::from_bytes(&serialized).unwrap();
assert_eq!(player, deserialized);
}
高级用法
处理字节缓冲区
use matrix_pickle::{Encoder, Decoder};
fn main() {
let mut buffer = vec![0u8; 1024]; // 预分配缓冲区
// 编码
let mut encoder = Encoder::new(&mut buffer);
encoder.encode(&42u32).unwrap();
encoder.encode(&"hello").unwrap();
let bytes_written = encoder.finish().unwrap();
// 解码
let mut decoder = Decoder::new(&buffer[..bytes_written]);
let num: u32 = decoder.decode().unwrap();
let text: String = decoder.decode().unwrap();
assert_eq!(num, 42);
assert_eq!(text, "hello");
}
处理数组和集合
use matrix_pickle::{to_bytes, from_bytes};
fn main() {
let data = vec![1u32, 2, 3, 4, 5];
let serialized = to_bytes(&data).unwrap();
let deserialized: Vec<u32> = from_bytes(&serialized).unwrap();
assert_eq!(data, deserialized);
}
性能提示
- 对于频繁序列化的场景,重用缓冲区而不是每次都创建新的
- 对于大型结构,考虑使用
#[repr(C)]
来优化内存布局 - 使用固定大小的数组而不是Vec当大小已知时
错误处理
use matrix_pickle::{from_bytes, Error};
fn try_deserialize(data: &[u8]) -> Result<u32, Error> {
let value: u32 = from_bytes(data)?;
Ok(value)
}
完整示例:游戏状态序列化
下面是一个完整的游戏状态序列化示例,展示了如何使用matrix-pickle处理复杂数据结构:
use matrix_pickle::{Encode, Decode, to_bytes, from_bytes};
// 定义游戏状态结构体
#[derive(Encode, Decode, PartialEq, Debug)]
struct GameState {
version: u32,
players: Vec<Player>,
map: Map,
timestamp: u64,
}
#[derive(Encode, Decode, PartialEq, Debug)]
struct Player {
id: u32,
name: String,
position: (f32, f32),
inventory: Vec<Item>,
stats: Stats,
}
#[derive(Encode, Decode, PartialEq, Debug)]
struct Item {
id: u16,
quantity: u8,
durability: f32,
}
#[derive(Encode, Decode, PartialEq, Debug)]
struct Stats {
health: u8,
stamina: u8,
experience: u32,
}
#[derive(Encode, Decode, PartialEq, Debug)]
struct Map {
tiles: Vec<u8>,
width: u32,
height: u32,
}
fn main() {
// 创建游戏状态
let game_state = GameState {
version: 1,
players: vec![
Player {
id: 1,
name: "Alice".to_string(),
position: (10.5, 20.3),
inventory: vec![
Item { id: 101, quantity: 1, durability: 100.0 },
Item { id: 205, quantity: 5, durability: 75.5 },
],
stats: Stats {
health: 100,
stamina: 80,
experience: 1500,
},
},
Player {
id: 2,
name: "Bob".to_string(),
position: (15.2, 8.7),
inventory: vec![
Item { id: 102, quantity: 1, durability: 90.0 },
],
stats: Stats {
health: 85,
stamina: 90,
experience: 2300,
},
},
],
map: Map {
tiles: vec![0, 1, 1, 0, 2, 0, 1, 3, 0],
width: 3,
height: 3,
},
timestamp: 1234567890,
};
// 序列化游戏状态
let serialized = to_bytes(&game_state).unwrap();
println!("Serialized size: {} bytes", serialized.len());
// 反序列化游戏状态
let deserialized: GameState = from_bytes(&serialized).unwrap();
// 验证数据完整性
assert_eq!(game_state, deserialized);
println!("Deserialization successful!");
}
这个完整示例展示了:
- 如何定义复杂嵌套结构体
- 使用derive宏自动实现Encode和Decode trait
- 处理包含Vec、元组和基本类型的复杂数据结构
- 验证序列化和反序列化的数据一致性
通过这个示例,您可以了解如何在实际项目中使用matrix-pickle来处理复杂的游戏状态或应用数据的序列化需求。