rust中bincode库关于不定长数据长度字段字节数问题的一种解决方法

嵌入式中使用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,
}

这样就能实现预期的结果,但这种方式实在有点繁琐,不知道还有没有其他更简洁一点的实现方式。

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值