通用字节序转换函数
在分布式系统的软件开发过程中,字节序转换总是一件令人头疼的麻烦事,尤其是参数众多,函数上下层次较多的场合,极易发生某个参数漏转序的事故……
如果有一个通用的字节序转换函数,一次函数调用,全部参数一个不漏的实现转序,那就帅呆啦。参考python的struct库的设计思想,实现了通用字节序转换函数,先来看用户使用体验:
样例:
/* 静态结构体字节序转换 */
extern VOS_BOOL SwitchEndian(VOS_VOID *pStruct, const VOS_CHAR *pcFmt);
typedef struct {
unsigned long nLong;
unsigned short nShort;
char acString[12];
char resv[2];
char cChar1;
char cChar2;
unsigned short usPad;
double dDouble;
} DEMO_STRU;
int main()
{
int aa = 100, bb=0;
DEMO_STRU stDemo = {0x11223344, 0x5566, "It's a demo", 0,0, 'a', 'b', 0, 3.1415926};
SwitchEndian(&stDemo, "LH12s2x2c2xQ"); // 传入格式化串,实现字节序转换
return 0;
}
格式化串中各symbol的说明(与python的语法格式相似,是其简化版)
Symbol Type Size Remark
Q U64,I64,double 8*N
L U32,I32,float 4*N
H U16,I16 2*N
c U8, S8 1*N Filling each separately
s string 1*N Filling all together
x padding 1*N Filling zero automacly
space Useless Seperator Python中没有space,为提高可读而加
"LH12s2x2c2xQ" == "L H 12s 2x 2c 2x Q" == DEMO_STRU
结构描述串也是一种领域特定语言,通过简单的格式映射,将结构体(或二进制)信息转换成程序可识别的格式,让程序来实现繁琐且冗长的转换。
拼接fmt串的建议:
对于静态结构,格式化字符串可以写死,缺点是结构调整就得更新这个fmt串,这是无法避免的
对于动态结构,格式化字符串可以通过sprintf拼接
更智能的应用是:将所有待转序的结构体定义放在同一个.h中,写一段python脚本扫描结构自动生成fmt串,这样即使是结构存在嵌套(被嵌套的结构一定定义在嵌套结构之前),也能准确拼处正确的fmt串
结构描述的更多应用场景:
结构体打包,类似Python的struct.pack(...)
实例:
DEMO_STRU stDemo;
PackStruct(&stDemo, "!LH12sxx2c2xQ", 0x55667788, 10000, "abcdef", 'a', 'b', -123456.78901);
一句函数调用实现多个入参的赋值,fmt串前的!表示赋值的同时还转序(对于x即填充字段,自动赋0,不用指定参数传入)。这种写法比较节省代码行数,在串口调试构造结构体参数也非常便利。
计算结构长度,类似Python的struct.calcsize(...)
实例:
printf("%d == %d\n", sizeof(DEMO_STRU), CalcSize("!LH12sxx2c2xQ"));
CalcSize是按一字节对齐计算结构尺寸,用来和sizeof结果比较,可以判断某结构是否紧压缩,调试用
结构成员打印,类似Python的print:
VOS_BOOL PrintStruct(VOS_VOID *pStruct, const VOS_CHAR *pcFmt);
把结构的每个成员按类型dump出来,用于调试定位,相比数内存看数据高效得多
/*** A specification for symbols in format string
Symbol Type Size Remark
Q U64,I64,double 8*N
L U32,I32,float 4*N
H U16,I16 2*N
c U8, S8 1*N Filling each separately
s string 1*N Filling all together
x padding 1*N Filling zero automacly
! Switch Endian Indicator
space Useless Seperator
***/
VOS_CHAR GetNextSymbol(const VOS_CHAR *pcFmt, VOS_INT *pnOffset, VOS_INT *pnEleCount)
{
VOS_CHAR *pcCurr = (VOS_CHAR *)pcFmt + *pnOffset;
VOS_INT nCount = 0;
while (*pcCurr)
{
if (VOS_IsDigit(*pcCurr))
nCount = nCount * 10 + (*pcCurr - '0');
else {
*pnOffset = pcCurr + 1 - pcFmt;
*pnEleCount = (0 == nCount) ? 1 : nCount;
return *pcCurr;
}
pcCurr++;
}
return *pcCurr; // end symbol
}
VOS_BOOL PrintStruct(VOS_VOID *pStruct, const VOS_CHAR *pcFmt)
{
VOS_INT nFmtIndex = 0, nRsltOffset = 0, nEleCount;
VOS_CHAR *pcRslt = (VOS_CHAR *)pStruct;
VOS_CHAR cSymbol;
#define PRINT_ELEMENT(tp, fmt, offset, cnt) for (VOS_INT _i=0;_i<(cnt);++_i,(offset)+=sizeof(tp)) printf(fmt" ", *(tp *)&pcRslt[offset])
while (!bEnd) {
cSymbol = GetNextSymbol(pcFmt, &nFmtIndex, &nEleCount);
switch (cSymbol) {
case 'L': // U32, I32, float
PRINT_ELEMENT(VOS_UINT32, "%#x", nRsltOffset, nEleCount);
break;
case 'H': // U16, I16
PRINT_ELEMENT(VOS_UINT16, "%#x", nRsltOffset, nEleCount);
break;
case 'c': // U8, S8
PRINT_ELEMENT(VOS_CHAR, "%c", nRsltOffset, nEleCount);
break;
case 's': // string
printf("%s ", &pcRslt[nRsltOffset]);
nRsltOffset += nEleCount;
break;
case 'x': // pad VOS_CHAR, filling zero
break;
case 'Q': // U64, I64, double
PRINT_ELEMENT(VOS_UINT64, "%lld", nRsltOffset, nEleCount);
break;
case '!': // host-network endian switch indicator
case ' ': // seperator remark
break;
case '\0': // normal end
bEnd = true;
break;
default: // unknown symbol
return false;
}
}
#undef SET_ELEMENT
}
/* 相当于sizeof(),用于校验 */
VOS_INT CalcSize(const VOS_CHAR *pcFmt)
{
VOS_UINT32 nTotal = 0;
VOS_INT nFmtIndex = 0, nEleCount = 0;
VOS_CHAR cSymbol;
while (true) {
cSymbol = GetNextSymbol(pcFmt, &nFmtIndex, &nEleCount);
switch (cSymbol) {
case 'L': // U32, I32, float
nTotal += nEleCount * sizeof(VOS_UINT32);
break;
case 'H': // U16, I16
nTotal += nEleCount * sizeof(VOS_UINT16);
break;
case 'c': // U8, S8
case 's': // string
case 'x': // padding VOS_CHAR
nTotal += nEleCount * sizeof(VOS_CHAR);
break;
case 'Q': // U64, I64, double
nTotal += nEleCount * sizeof(VOS_UINT64);
break;
case '!': // host-network endian switch indicator
case ' ': // seperator remark
break;
case '\0': // normal end
return nTotal;
default: // unknown symbol
return -1;
}
}
return false;
}
/* 静态结构体转序 */
VOS_BOOL SwitchEndian(VOS_VOID *pStruct, const VOS_CHAR *pcFmt)
{
VOS_INT nFmtIndex = 0, nRsltOffset = 0, nEleCount;
VOS_CHAR *pcRslt = (VOS_CHAR *)pStruct;
VOS_CHAR cSymbol;
#define SW_ENDIAN(tp, offset, cnt, sw) for (VOS_INT _i=0;_i<(cnt);++_i,(offset)+=sizeof(tp)) *(tp *)&pcRslt[offset] = sw(*(tp *)&pcRslt[offset])
while (true) {
cSymbol = GetNextSymbol(pcFmt, &nFmtIndex, &nEleCount);
switch (cSymbol) {
case 'L': // U32, I32, float
SW_ENDIAN(VOS_UINT32, nRsltOffset, nEleCount, VOS_HTONL);
break;
case 'H': // U16, I16
SW_ENDIAN(VOS_UINT16, nRsltOffset, nEleCount, VOS_HTONS);
break;
case 'c': // U8, S8
case 's': // string
case 'x': // padding VOS_CHAR
nRsltOffset += nEleCount;
break;
case 'Q': // U64, I64, double
SW_ENDIAN(VOS_UINT32, nRsltOffset, nEleCount * 2, VOS_HTONL);
break;
case '!': // host-network endian switch indicator
case ' ': // seperator remark
break;
case '\0': // normal end
return true;
default: // unknown symbol
return false;
}
}
#undef SW_ENDIAN
return false;
}
// 按指定格式填充结构体(紧压缩),支持转序
VOS_BOOL PackStruct(VOS_VOID *pStruct, const VOS_CHAR *pcFmt, ...)
{
VOS_INT nFmtIndex = 0, nRsltOffset = 0, nEleCount;
VOS_CHAR *pcRslt = (VOS_CHAR *)pStruct;
VOS_CHAR cSymbol;
VOS_BOOL bEnd = false, bSwEndian=false;
va_list pArgList;
va_start(pArgList, pcFmt);
#define SET_ELEMENT(tp, offset, cnt) for (VOS_INT _i=0;_i<(cnt);++_i,(offset)+=sizeof(tp)) *(tp *)&pcRslt[offset] = (tp)va_arg(pArgList, tp)
while (!bEnd) {
cSymbol = GetNextSymbol(pcFmt, &nFmtIndex, &nEleCount);
switch (cSymbol) {
case 'L': // U32, I32, float
SET_ELEMENT(VOS_UINT32, nRsltOffset, nEleCount);
break;
case 'H': // U16, I16
SET_ELEMENT(VOS_UINT16, nRsltOffset, nEleCount);
break;
case 'c': // U8, S8
SET_ELEMENT(VOS_CHAR, nRsltOffset, nEleCount);
break;
case 's': // string
VOS_StrNCpy(&pcRslt[nRsltOffset], (VOS_CHAR *)va_arg(pArgList, VOS_CHAR *), nEleCount);
nRsltOffset += nEleCount;
break;
case 'x': // pad VOS_CHAR, filling zero
VOS_MemSet(&pcRslt[nRsltOffset], 0, nEleCount);
nRsltOffset += nEleCount;
break;
case 'Q': // U64, I64, double
SET_ELEMENT(VOS_UINT32, nRsltOffset, nEleCount * 2);
break;
case '!': // host-network endian switch indicator
bSwEndian = true;
break;
case ' ': // seperator remark
break;
case '\0': // normal end
bEnd = true;
break;
default: // unknown symbol
return false;
}
}
#undef SET_ELEMENT
va_end(pArgList);
if (bSwEndian) SwitchEndian(pStruct, pcFmt);
return true;
}
/************************************************************************/