手撕代码: C++实现按位序列化和反序列化

目录

1.需求

2.流程分析

3.实现过程

4.总结


1.需求

        在我们正在开发的项目,有这样一种需求,实现固定格式自由格式比特流无线传输。解释一下,固定格式形如下面表格:

每个字段都有位宽、类型等属性,这种固定格式一般总位宽都是固定的,比如72比特(9个字节)。它的序列化过程就是把比特数据转换成字节流,反序列化过程即是把字节流转换成比特数据。

自由格式形如下面表格:

它的最大位宽是固定的,但是总位宽是不固定的,用户可以自定义每个字段;自定义格式的序列化和反序列化跟固定格式的流程是差不多的。

2.流程分析

以下面自定义格式为例来说明实现的过程:

自定义格式的组包过程如下:

1)字段1为2个比特,值为1,二进制为0b00000001,即把0b01移到第1个字节的低两位。

2)字段2为5个比特,值为5,二进制为0b00000101,即把0b00101移到第1个字节的第2位到第6位。

3)字段3为3个比特,值为2,二进制为0b00000010,即把0b010的最低比特0移到第1个字节的第7位,高两个比特01移到第2字节的低两位上。

4)字段4、字段5、字段6依次做类似处理。

5)这个自定义格式总共36个比特,比5个字节(40个比特)还少4个比特,把最后一个字节高4个比特填充0,于是得到字节流如上图的底部所示。

解包过程即是上面的流程反过来,就不在这里赘述了。

3.实现过程

单个字段组包实现过程代码如下:

int CJField::getData(char* pData, int len, int& currIndex, int& reserveBit) const {
	constexpr quint8 nShift[] = { 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F, 0xFF };
	int nBitWidth = m_bitWidth;
	qint64 value = m_value;
	if (reserveBit <= 0) {
		currIndex++;
		reserveBit = 8;
	}
	while (nBitWidth > 0) {
		if (nBitWidth > reserveBit) {
			pData[currIndex] |= ((value & nShift[reserveBit - 1]) << (8 - reserveBit));
			value >>= reserveBit;
			currIndex++;
			nBitWidth -= reserveBit;
			reserveBit = 8;
		}
		else {
			pData[currIndex] |= ((value & nShift[nBitWidth - 1]) << (8 - reserveBit));
			reserveBit -= nBitWidth;
			nBitWidth = 0;
		}
	}
	return 1;
}

单个字段解包实现过程代码如下:

int  CJField::setData(const char* pData, int len, int& currIndex, int& reserveBit) {
	constexpr quint8 nShift[] = { 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F, 0xFF };
	int nBitWidth = m_bitWidth;
	qint64 value = 0;
	if (reserveBit <= 0) {
		currIndex++;
		reserveBit = 8;
	}
	while (nBitWidth > 0) {
		if (nBitWidth > reserveBit) {
			value |= ((qint64)((pData[currIndex] >> (8 - reserveBit)) & nShift[reserveBit - 1]) << (m_bitWidth - nBitWidth));
			nBitWidth -= reserveBit;
			reserveBit = 8;
			currIndex++;
		}
		else {
			value |= ((qint64)((pData[currIndex] >> (8 - reserveBit)) & nShift[nBitWidth - 1]) << (m_bitWidth - nBitWidth));
			reserveBit -= nBitWidth;
			nBitWidth = 0;
		}
	}
	m_value = value;
	calcPhyFromValue();
	return 1;
}

自定义格式整体组包过程代码如下:

    /// <summary>
	/// //组包
	/// </summary>
	/// <param name="pData"></param>
	/// <param name="nLen"></param>
	/// <returns></returns>
	int getData(char* pData, int& nLen) {
		int  nCurrIndex = 0;
		int  nReserveBit = 8;
		//[1]
		if (m_vecMsgFields.size() <= 0)
			return 0;

		//[2]
		for (auto& it : m_vecMsgFields) {
			it->getData(pData, nLen, nCurrIndex, nReserveBit);
		}
		//[3]填充数据,长度为9的倍数
		nLen = nCurrIndex + 1;
		while (nLen % 9 != 0) {
			nLen++;
		}
		return 1;
	}

自定义格式整体解包过程代码如下:

    /// <summary>
	/// 解包
	/// </summary>
	/// <param name="pData"></param>
	/// <param name="nLen"></param>
	/// <returns></returns>
	int setData(const char* pData, int nLen) {
		int  nCurrIndex = 0;
		int  nReserveBit = 8;
		//[1]
		if (m_vecMsgFields.size() <= 0)
			return 0;
		//[2] 根据模板,解析内容
		for (auto& it : m_vecMsgFields) {
			it->setData(pData, nLen, nCurrIndex, nReserveBit);
		}
		return 1;
	}

固定格式和自由格式的实现差不多,就不在这里赘述了。

4.总结

        上述实现的按位序列化和反序列化的过程并不是很复杂;看着上面的过程,可以自己动手写写其实现过程,就会真正理解它。

        你要相信一句话:纸上得来终觉浅,绝知此事要躬行。

        如果需要此实现的详细源代码,可以后台@我。

推荐阅读

手撕代码: C++实现数据的序列化和反序列化_c++序列化和反序列化实例-优快云博客

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值