byAlex Jan2013 GNU GCC编译测试
lexcode数据包编码解码程序,用于编/解码简短的数据或指令,方便通信。最多携带32KB数据,可以划分为127个参数。lexcode数据包总大小不超过32773字节。
这已足以实现对远程设备的监视、控制。
lexcode数据包定义:
大小 定义
3 0x4C43BB:lexcode数据包标识
2 长度:数据包的总大小
1 定义字符(>0且<=127):区分两种数据定义,表示参数索引的个数
2 参数索引0
2 参数索引1
2 参数索引2
...
参数数据集
或
3 0x4C43BB:lexcode数据包标识
2 长度:数据包的总大小
1 定义字符(<=0size_t且>-128):区分两种数据定义,表示参数的个数(含附加信息)的相反数
4 参数0
4 参数1
4 参数2
...
[附加信息字符串]
format字符串规定(参考了Python2.7中的相关说明)
Format | C Type | Python type | Size |
---|---|---|---|
c | char | string | 1 |
b | signed char | integer | 1 |
B | unsigned char | integer | 1 |
? | _Bool | bool | 1 |
h | short | integer | 2 |
H | unsigned short | integer | 2 |
i | int | integer | 4 |
I | unsigned int | integer | 4 |
l | long | integer | 4 |
L | unsigned long | integer | 4 |
f | float | float | 4 |
d | double | float | 8 |
s | char[] | string | depends |
其中这些格式符被排除(Python却包含的)
x,p,P,q,Q
编码分析工具函数
void lcPrtMem(char *m){ //用来检查编码的m内存区
int i;short sz=*((short*)(m+3)); //可以替换为足够大的常数
for(i=0;i<sz;i++)printf("%5d:%4d %X\n",i,m[i],m[i]);
//偏移:十进制数据 十六进制数据
}
C++测试例程
#include <iostream>
#include <cstdio>
extern "C"{
#include "lexcode.h"
}
using namespace std;
int main(void)
{
char m1[200],m2[200]; //申请储存空间
//下面两句语句定义了x1、x2作为状态变量(lcStatus类型),并指定了格式字符
LcStatus(x1,LcDefA);
LcStatus(x2,LcDefB);
int a=12; //待装包的数据组
int b=-142857;
int c=31337;
char *s=(char*)"happy";
float q=0.618;
//编码:储存区,状态,格式字符串,数据组
lcCode(m1,&x1,"3i8sf",a,b,c,s,q); //根据x1状态变量给出的定义字符所表示的数据包定义,编码数据组到m1,格式字符串指定了每个变量的格式。
lcCode(m2,&x2,"2i8s",b,c,s);
//对每一个参数解码
cout<<"[m1]"<<endl;
cout<<lcArgi(m1,0)<<endl; //用整型获取索引位置0的参数
cout<<lcArgi(m1,1)<<endl;
cout<<lcArgi(m1,2)<<endl;
cout<<lcArgs(m1,3)<<endl; //获取指向索引位置3的参数的字符指针
cout<<lcArgf(m1,4)<<endl; //用浮点型获取索引位置4的参数
cout<<endl<<"[m2]"<<endl;
cout<<lcArgi(m2,0)<<endl; //用整型获取索引位置0的参数
cout<<lcArgi(m2,1)<<endl;
cout<<lcArgs(m2,2)<<endl; //获取指向索引位置2的参数的字符指针
cout<<"tag:"<<lcTag(m2)<<endl; //获取指向m2附加信息的字符指针
return 0;
}
FAQ
lexcode具有两种数据包定义,有什么区别?
A定义中含索引,可以编码不同类型元素的数据组;
B定义中没有索引,这使整个数据包更简短(由于数据类型都属于基本数据类型,所
节省的索引的数据量也是很可观的),但换来的代价是它只提供int类型数据(不要
尝试在B定义的数据包中传递float类型值,由于可变参数的类型提升规则影响了参
数栈,在A定义中特对此编程解决了这个问题),再附带一个可选的附加信息(一个
字符串)。
为什么A定义中采用索引表?
这使其可以编码不同类型元素的数据组
考虑到解码时寻址的方便,所以采用了索引表的方式,而非链表。
为什么编码函数是这种形式?
起初受到printf的启发,它可以格式化输出各种形式的数据。然而这里并不是一个
格式化输出,准确地讲,其作用是序列化(“解码”,相对地,就是逆序列化),所
以printf的格式字符串显得过于强大(冗长),所以采用了Python中pack函数
所规定的格式化字符串。状态参数可以反馈编码过程与结果的关键信息,通过指针
返回。
解码简单性从何体现?
解码的时候通过调用合适的解码函数(根据这个参数的类型),再告诉它这是从零数
起的第几个参数就可以了。这特别适合解释器的实现——几乎不需要再分析了。
解码过程不考虑它是采用了什么样的定义(解码的寻址表达式使这点对用户透明了)
状态变量和定义字符是如何使用的?
建议使用LcStatus宏来定义(并且初始化)一个状态变量。
一旦它被编码函数引用(此指在正确的参数位置被传入),其值就会改变。
编码函数根据已有的定义字符表示的定义来编码数据,然后用被编码数据的定义字符取而代之,显然这个函数对状态变量中定义字符的作用是对两种定义分别封闭的。也就是说,除非人为改变,连续引用同一个状态变量来编码的所有数据包符合同一个定义。
其他成员变量说明见头文件。
参考
使用方法参考上面的例程;格式字符串的用法可以参考Python的相关资料(pack函数)
状态变量提供了数据包的总大小,定义字符等,参考头文件中tlcStatus结构。
lexcode.h
#ifndef __LEXCODE_H_
#define __LEXCODE_H_
#ifndef byAlex
#define byAlex(___) ___
#endif
#include <stdlib.h>
#include <string.h>
#define LCHSZ 6 //数据头大小
#define LcDefA 1 //第一种数据包定义,AKA. A定义
#define LcDefB -1 //第二种数据包定义,AKA. B定义
#define LcStatus(__ls,__def)\
lcStatus __ls;__ls.fchar=__def //声明数据包状态变量
typedef struct tlcStatus{
char fchar; //定义字符
short tsz; //数据包总大小
short vdbas; //参数数据集起始地址
}lcStatus;
//编码函数
int lcCode(char *p,lcStatus *ls,const char *format,...);
//解码函数
char lcArgb(char *p,int i);
unsigned char lcArgB(char *p,int i);
short lcArgh(char *p,int i);
unsigned short lcArgH(char *p,int i);
int lcArgi(char *p,int i);
unsigned int lcArgI(char *p,int i);
long lcArgl(char *p,int i);
unsigned long lcArgL(char *p,int i);
float lcArgf(char *p,int i);
double lcArgd(char *p,int i);
char* lcArgs(char *p,int i);
char* lcTag(char *p);
#endif
lexcode.c
#include "lexcode.h"
size_t lcSizeof(char c){
if(c>='A'&&c<='Z')c+='a'-'A';
switch(c){
case 's':case 'c':case 'b':
case '?':return sizeof(char);
case 'h':return sizeof(short);
case 'i':return sizeof(int);
case 'f':return sizeof(float);
case 'l':return sizeof(long);
case 'd':return sizeof(double);
default:return 0;
}
}
int lcCount(const char *format,int i){
char n[12];int s=i,j;
for(s=i;s-1>=0&&format[s-1]>='0'&&format[s-1]<='9';s--);
if(s==i)return 1;
else{
for(j=s;j<i;j++)n[j-s]=format[j];
n[j-s]='\0';
return atoi(n);
}
}
int lcCode(char *p,lcStatus *ls,const char *format,byAlex (...)){
int i,j,ct,sz;short dx;
if(p!=NULL){
{ //计算数据包大小
int tct=0,tsz=0;
for(i=0;format[i]!='\0';i++){
if((sz=lcSizeof(format[i]))!=0){
ct=lcCount(format,i);
if(format[i]=='s'){ //s格式字符前的数字表示字符串最大长度(包括\0)而非个数
sz=ct;
ct=1;
}
tct+=ct;
tsz+=ct*sz;
} }
ls->vdbas=ls->fchar>0?2*tct+LCHSZ:LCHSZ;
ls->fchar=ls->fchar>0?tct:-tct;
ls->tsz=tsz+ls->vdbas;
}
{ //数据头初始化
*((int*)p)=0x00BB434C; //数据包标识符
*((short*)(p+3))=ls->tsz; //数据包总大小
*(p+5)=ls->fchar; //数据包定义字符
}
{ //编码数据
//char* pArg=(char*)(&format+sizeof(format)); //有文献记载使用这个算式计算可变参数首地址,实验却未成功
void *pArg=&format+1;
dx=ls->vdbas;
if(ls->fchar>0){
short *ix=(short*)(p+LCHSZ);size_t _sz;
for(i=0;format[i]!='\0';i++){
if((sz=lcSizeof(format[i]))!=0){
ct=lcCount(format,i);
for(j=0;j<ct;j++){
if(format[i]=='s'){ //编码值类型数据
char *str=*((char**)pArg);
*ix=dx;
strcpy(p+dx,str);
dx+=sz*ct;
pArg+=sizeof(char*);
ix++;
break;
}
else if(format[i]=='f'){ //类型提升规则中规定:float被提升为double
float __fdt;double __ddt;
*ix=dx; //添加索引记录
memcpy(&__ddt,pArg,sizeof(double));
__fdt=(float)__ddt; //执行转换
memcpy(p+dx,&__fdt,sz);
dx+=sz;
pArg+=sizeof(double);
ix++;
}
else{ //将字符指针指向的字符串编码进来
*ix=dx; //添加索引记录
_sz=sz>sizeof(int)?sz:sizeof(int); //注意可变参数的类型提升
memcpy(p+dx,pArg,sz);
dx+=sz;
pArg+=_sz;
ix++;
} } } } }
else{
for(i=0;format[i]!='\0';i++){ //跟上面的代码基本一致,B定义中没有索引记录
if((sz=lcSizeof(format[i]))!=0){
ct=lcCount(format,i);
for(j=0;j<ct;j++){
if(format[i]!='s'){
memcpy(p+dx,pArg,sizeof(int));
dx+=sz;
pArg+=sizeof(int);
}
else{
char *str=*((char**)pArg);
strcpy(p+dx,str);
dx+=sz*ct;
pArg+=sizeof(char*);
break;
} } } } }
return 0;
} }
return -1;
}
#define LcGet(__t,__val)\
byAlex(__t) __val;\
short dx=p[5]>0?*((short byAlex(*))(p+byAlex(LCHSZ))+i):LCHSZ+4*i;\
memcpy(&__val,p byAlex(+dx),sizeof(byAlex(__t)))
#define LcArgImp(__func,__t) __t __func(char *p,int i){LcGet(__t,val);return val;}
//利用LcArgImp宏实现参数解码函数族
LcArgImp(lcArgb,char) LcArgImp(lcArgB,unsigned char)
LcArgImp(lcArgh,short) LcArgImp(lcArgH,unsigned short)
LcArgImp(lcArgi,int) LcArgImp(lcArgI,unsigned int)
LcArgImp(lcArgl,long) LcArgImp(lcArgL,unsigned long)
LcArgImp(lcArgf,float) LcArgImp(lcArgd,double)
char* lcArgs(char *p,int i){ //根据索引构造指向目标字符串的指针
return p+(p[5]>0?*((short*)(p+LCHSZ)+i):LCHSZ+4*i);
}
char* lcTag(char *p){
if(p[5]<=0&&*((short*)(p+3))!=LCHSZ+4*(-p[5]))return p+LCHSZ+4*(-p[5]-1);
else return NULL;
}