转载时请注明出处和作者联系方式
作者联系方式:李先静 <xianjimli at hotmail dot com>
我们决定用XML来打包数据,借以避免二进制的晦涩,以及字节顺序和字节对齐的问题。这会引出一个小小的麻烦,有些字符和字符序列是不允许出现在XML文档中的,因为XML用它们来表示自己的语法,比如大/小于号和引号,XML也不允许二进制数据出现在里面,它只能表示文本内容,而且按统一的标准编码。
要传输二进制数据,我们需要对数据做些转换。其中空间效率比较高的转换可能是base64编码了,它能用64个基本字符编码任何二进制数据,它被发明的目的好像是为了在邮件中传输二进制数据,它以8位传输数据表示6位有效数据(每6位数据编码为一个字符)。但我不想使用它,原因是它不够直观,当你看到一串base64编码的字符串,很难直接推出它原本的字符串(如果你认为它能加密,那也错了,一个简单的程序就能”解密”它们)。最后我决定选择十六进制编码,它能以8位数据表示4位有效数据,虽然比base64差了点,不过要直观一些,可以根据这些十六进制数据看出原本的数据,比如30表示原来的数字0,而且实现也更简单。
很多情况下我们传输的是字符串,它们用十六进制编码也可以,只是有些浪费空间,也不够字符串本身直观。因此我们可以采用另一个更有效的编码方式,也就是实体(entity)编码,比如小于号可以编码为<或者&#x 3c;,这种方法看起来效率低得出奇,你需要4个或者6个字符才能表示一个字符,所幸的是我们只需要编码那些与XML文档冲突的字符,这样的字符在普通字符串中出现的概率不高,所以这种编码的整体表现是很优秀的。
我们有两种编码方法了,也不排除最后我们因为效率原因退回来使用base64,更甚者我们还会引入加密或压缩的编码方法进来,所以我们有必要为数据编码定义一个接口,针对接口编码,把变化的部分隔离开来:
- typedef MeRet (*MobileExplorerTransformerTransformFunc)
- (MobileExplorerTransformer* thiz, MobileExplorerBuffer* input, MobileExplorerBuffer* output);
- typedef MeRet (*MobileExplorerTransformerGetTypeFunc)
- (MobileExplorerTransformer* thiz, MeTransformType* type);
- typedef MeRet (*MobileExplorerTransformerDestroyFunc)(MobileExplorerTransformer* thiz);
- struct _MobileExplorerTransformer
- {
- MobileExplorerTransformerTransformFunc transform;
- MobileExplorerTransformerGetTypeFunc get_type;
- MobileExplorerTransformerDestroyFunc destroy;
- char priv[0];
- };
C语言中的接口定义很简单,它是一个包括一组函数指针的结构,可以认为它们是一组纯虚函数。函数指针是一个抽象的东西,它们指到不同的函数上,对象就具备不同的行为,也就是传说中的多态。上面的transfom是这个接口的核心,它负责数据的转换。C语言定义接口是要注意两个问题:一是接口中不会出现创建函数,因为对象的创建一定与接口的具体实现相关,创建函数是虚函数,就会造成鸡生蛋蛋生鸡死循环。其次是一定要有销毁函数,如果调用者最后不知道销毁对象的方式,那意味着调用者和实现者会有更多的耦合。
~~end~~