【发布时间】:2021-09-13 12:35:12
【问题描述】:
我正在尝试将 serde 与 bincode 一起使用来反序列化任意 bitcoin network message。鉴于有效负载普遍作为字节数组处理,如果在编译时长度未知,我该如何反序列化它? bincode 默认处理Vec<u8>,假设它的长度在向量元素之前被编码为u64。但是,此假设在这里不成立,因为 校验和 位于有效负载的 length 之后。
我有以下工作解决方案
Cargo.toml
[package]
name = "serde-test"
version = "0.1.0"
edition = "2018"
[dependencies]
serde = { version = "1.0", features = ["derive"] }
serde_bytes = "0.11"
bincode = "1.3.3"
main.rs
use bincode::Options;
use serde::{Deserialize, Deserializer, de::{SeqAccess, Visitor}};
#[derive(Debug)]
struct Message {
// https://en.bitcoin.it/wiki/Protocol_documentation#Message_structure
magic: u32,
command: [u8; 12],
length: u32,
checksum: u32,
payload: Vec<u8>,
}
struct MessageVisitor;
impl<'de> Visitor<'de> for MessageVisitor {
type Value = Message;
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
formatter.write_str("Message")
}
fn visit_seq<V>(self, mut seq: V) -> Result<Self::Value, V::Error> where V: SeqAccess<'de>,
{
let magic = seq.next_element()?.unwrap();
let command = seq.next_element()?.unwrap();
let length: u32 = seq.next_element()?.unwrap();
let checksum = seq.next_element()?.unwrap();
let payload = (0..length).map(|_| seq.next_element::<u8>().unwrap().unwrap()).collect();
// verify payload checksum (omitted for brevity)
Ok(Message {magic, command, length, checksum, payload})
}
}
impl<'de> Deserialize<'de> for Message {
fn deserialize<D>(deserializer: D) -> Result<Message, D::Error> where D: Deserializer<'de>,
{
deserializer.deserialize_tuple(5000, MessageVisitor) // <-- overallocation
}
}
fn main() {
let bytes = b"\xf9\xbe\xb4\xd9version\x00\x00\x00\x00\x00e\x00\x00\x00_\x1ai\xd2r\x11\x01\x00\x01\x00\x00\x00\x00\x00\x00\x00\xbc\x8f^T\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xc6\x1bd\t \x8d\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xcb\x00q\xc0 \x8d\x12\x805\xcb\xc9yS\xf8\x0f/Satoshi:0.9.3/\xcf\x05\x05\x00\x01";
let msg: Message = bincode::DefaultOptions::new().with_fixint_encoding().deserialize(bytes).unwrap();
println!("{:?}", msg);
}
输出:
Message { magic: 3652501241, command: [118, 101, 114, 115, 105, 111, 110, 0, 0, 0, 0, 0], length: 101, checksum: 3530103391, payload: [114, 17, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 188, 143, 94, 84, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 198, 27, 100, 9, 32, 141, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 203, 0, 113, 192, 32, 141, 18, 128, 53, 203, 201, 121, 83, 248, 15, 47, 83, 97, 116, 111, 115, 104, 105, 58, 48, 46, 57, 46, 51, 47, 207, 5, 5, 0, 1] }
我不喜欢这个解决方案,因为 payload 的处理方式。它需要我分配一些“足够大”的缓冲区来考虑payload 的动态大小,在代码sn-p 中5000 以上就足够了。我宁愿将payload 反序列化为单个元素,而改用deserializer.deserialize_tuple(5, MessageVisitor)。
有没有办法以简洁的方式处理这种反序列化?
我可以找到类似的问题:Can I deserialize vectors with variable length prefix with Bincode?
【问题讨论】:
-
也许尝试使用
nomcrate?或者尝试逐字节解析并分组为数字,例如let length = u32::from_le_bytes([...])? -
@Coder-256 直到现在我还没有听说过
nomcrate,它是serde的替代品吗?我不确定逐字节解析是什么意思,length编码并不是真正的问题,而是它没有立即跟随payload中的元素。 -
你不能使用 bincode 并称之为一天 LUL bincode 仅用于 bincode 编码。使用名词
-
@Kevin nom 不是 serde 的替代品,它只是一种不同的方法。您的问题是您的消息未编码为 bincode,因此您正在做一些奇怪的事情来处理非 bincode 数据。 Serde 用于为通用格式创建序列化器和反序列化器,但您的消息采用非常特定的格式,只能以一种方式解释。像 nom 这样的库更合适。您也可以直接查看字节,因为它是一种简单的格式。