目录
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依次做类似处理,最后得到一个字节流如上图的底部所示。
解包过程即是上面的流程反过来,就不在这里赘述了。
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.总结
上述实现的按位序列化和反序列化的过程并不是很复杂;看着上面的过程,可以自己动手写写其实现过程,就会真正理解它。
你要相信一句话:纸上得来终觉浅,绝知此事要躬行。
如果需要此实现的详细源代码,可以后台@我。