嵌入式中使用bincode进行结构体序列化和反序列化是很方便的一种操作。但是发现有个问题。比如定义一个结构体:
#[derive(Debug, Deserialize, Serialize)]
pub struct EposDispMsg {
pub msg_id: u32,
pub mod_id: u8,
pub oper_type: u8,
pub oper_num: u16,
pub color: u8,
pub disp_info: Vec<u8>,
}
这个结构体在32位和64位的机子上编译出来,序列化后,其他字段都没有问题,但disp_info 字段前的长度字段的字节数是不一样的,32为编译出来的长度是4字节,64位编译处理的长度是8字节。这对于接收端会造成影响,而且嵌入式中定义的协议中,经常这个字段连4个字节都不到。
这种方式怎么解决呢,首先想到的是bincode会不会提供一个length属性宏,限制这种情况下长度的字节数,发现没有,然后想着使用serder的serialize_with重新定义序列化函数
#[derive(Serialize, Deserialize, Debug)]
struct MyStruct {
#[serde(serialize_with = "serialize_disp_info", deserialize_with = "deserialize_disp_info")]
disp_info: Vec<u8>,
}
然后实现serialize_disp_info
fn serialize_disp_info<S>(disp_info: &Vec<u8>, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let length = disp_info.len() as u16;
let mut buffer = Vec::new();
buffer.extend_from_slice(&length.to_le_bytes());
buffer.extend_from_slice(disp_info);
serializer.serialize_bytes(&buffer)
}
发现还是不行,执行serializer.serialize_bytes(&buffer)时,仍然会在数据前面添加上4个或8个长度字段。
折腾好久,后来想到一种方法,干脆将长度和数据放在一个结构体中,直接实现这个结构体的序列化和反序列化。
#[derive(Debug)]
pub struct EposDispInfo {
pub len: u8,
pub data: [u8; 256],
}
impl Serialize for EposDispInfo {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let mut state = serializer.serialize_tuple((self.len + 1) as usize)?;
state.serialize_element(&self.len)?;
for i in 0..self.len as usize {
state.serialize_element(&self.data[i])?;
}
state.end()
}
}
struct EposDispInfoVisitor;
impl<'de> Visitor<'de> for EposDispInfoVisitor {
type Value = EposDispInfo;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("a sequence starting with length and followed by data")
}
fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
where
A: SeqAccess<'de>,
{
let len: u8 = seq
.next_element()?
.ok_or_else(|| serde::de::Error::invalid_length(0, &self))?;
let mut data = [0; 256];
for (i, item) in data.iter_mut().enumerate().take(len as usize) {
*item = seq
.next_element()?
.ok_or_else(|| serde::de::Error::invalid_length(i + 1, &self))?;
}
Ok(EposDispInfo { len, data })
}
}
impl<'de> Deserialize<'de> for EposDispInfo {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
deserializer.deserialize_tuple(256, EposDispInfoVisitor)
}
}
#[derive(Debug, Deserialize, Serialize)]
pub struct EposDispMsg {
pub msg_id: u32,
pub mod_id: u8,
pub oper_type: u8,
pub oper_num: u16,
pub color: u8,
pub disp_info: EposDispInfo,
}
这样就能实现预期的结果,但这种方式实在有点繁琐,不知道还有没有其他更简洁一点的实现方式。