,Varints对于小于128的数值都可以用一个字节表示,大于128的数值会用更多的字节来表示,对于很大的数据则需要用5个字节来表示。
Varints算法描述: 每一个字节的最高位都是有特殊含义的,如果是1,则表示后续的字节也是该数字的一部分;如果是0,则结束
b、demo生成的的二进制文件反序列化。
第1个字节 (0A)
字段索引(index): 0A = 0001010 0A>>3 = 001 = 1
数据类型(type): 0A = 0001010&111 = 2 (String);
第2个字节 (0C)
字符串长度(length): 0E = 12;
字符串: 0A 05 69 64 6F 6C 33 10 01 18 BD 0F
第3个字节 (0A)
因为字符串是来自phoneInfo属于嵌套类型
字段索引(index): 0A = 0001010 0A>>3 = 001 = 1
数据类型(type): 0A = 0001010&111 = 2 (String);
第4-9个字节(69 64 6F 6C 33)
字符串长度(length): 05 = 5
字符串: 69 64 6F 6C 33 = idol3
第10个字节 (10)
字段索引(index): 10 = 00010000 10A>>3 = 0010 = 2
数据类型(type): 10 = 00010000&111 = 0 (Varints);
第11个字节 (01)
Varints: 01 = 00001字节的最高位为0 整数结束
Value: 1;
第12个字节(18)
字段索引(index): 18 = 00011000 18>> 00011 = 3
数据类型(type): 18 = 00011000&111 = 0 (Varints);
第13个字节(D0)
最高位为1,整数计算到下一个字节
第14个字节(0F)
最高位为0,整数计算结束
Value:为11111010000 =2000
C、反序列化结果
phoneinfo为:
phoneName = “idol3”
top = 1
price = 2000;
同样的方法watchInfo为:
watchName = “tcl name”
top = 1
price=2000
3、时间效率
通过protobuf序列化/反序列化的过程可以得出:protobuf是通过算法生成二进制流,序列化与反序列化不需要解析相应的节点属性和多余的描述信息,所以序列化和反序列化时间效率较高。
4、空间效率
xml、json是用字段名称来确定类实例中字段之间的独立性,所以序列化后的数据多了很多描述信息,增加了序列化后的字节序列的容量。
Protobuf的序列化/反序列化过程可以得出:
protobuf是由字段索引(fieldIndex)与数据类型(type)计算(fieldIndex<<3|type)得出的key维护字段之间的映射且只占一个字节,所以相比json与xml文件,protobuf的序列化字节没有过多的key与描述符信息,所以占用空间要小很多。
七、Protobuf的源码分析
1、protobuf在java使用的序列化流程
java程序调用parserFrom(byte[] data)开始字节序列的反序列,Java程序通过调用编译生类GenerateMessage中的wirteTo()方法开始将序列化后的字节写入输出流中
GenerateMessage 继承AbstractMessage类,序列化最终在AbstractMesssage中完成,序列化的实现过程:
a、遍历对象中Message结构()
调用AbstractMessage类中的writeTo()方法
b、 序列化Message中每一个字段
调用CodeOutputStream类中的writeMessageSetExtension()方法
c、 对于Varints Tag 的序列化流程:
调用CodeOutputStream类中的writeUInt32()方法
调用CodeOutputStream类中的WriteRawVarint32()方法
d、 对于非Varints Tag的序列化
调用CodeOutputStream类中的WriteTag()方法
具体的序列化实现都在CodedOutputStream中完成
2、java使用protobuf 的反序列化流程分析
java程序通过调用parserFrom(byte[] data)开始反序列化
具体在com.google.protobuf. AbstractParser类中实现
最后在com.google.protobuf.CodedInputStream类中完成反序列化
3、动态编译
以windows下用protoc.exe工具实现proto文件编译为例,protoc.exe是用C++实现。在控制台执行命令:
编译的流程:
检查proto的语法规则
将proto的文件中的message结构转换为GenerateMessage类的子类,并实现Builder接口。