protobuf序列化反序列化原理剖析
protobuf的数据类型大致可以分为以下几种:
- 变长编码类型Varints
- 固定32bits类型
- 固定64bits类型
- 有长度标识的变量
根据这些类型,编码方式也就分成四种。下面我来一一介绍一下不同数据类型的编码方式。
Varints编码
Varints编码用于处理变长编码类型。
核心思想其实就一句话:数字小的占用空间小,数字大的占用空间大。那要如何实现这个结果呢?Varints编码使用每个字节的最高有效位来储存标识位,这个标志位的作用就是表示这个字节是不是表示数据的最后一个字。字节编码采用7位补码的格式,头位表示标志位,后7位用于存储数据。那么是如何编码的呢?这里介绍Base64形式的编码,先用二进制补码的形式表示待编码的数字,比如300的补码是100101100。首先从高位取7位,放在第一个字节,如果还有字节,就将头位置为1,继续取7位,直到取完为止,不满7位的在高位补0即可。所以300的Base64格式的编码就是10101100 000000010。所以300需要使用2个字节来存储。反序列化就按照上面的步骤的相反步骤执行即可。
这样就对于一个int64的数据类型就不需要8个字节进行存储,实现数据的压缩,提高传输效率。
Zigzag编码
负数的补码表示很大,因为最高位是1,所以直接使用Varint编码不会起到压缩的效果。这里protobuf就提供Zigzag编码格式来处理负数的编码;Zigzag的处理比较简单,就是把最高位的1移到最后一位,其他的位左移一位。
介绍完这两种编码格式后,就要进入今天的主题:Protobuf 序列化;
Protobuf序列化
在讲protobuf之前,需要了解两个知识点:
- Wire Type类型
- TLV 存储方式 / Tag-Length-Value
Wire Type
wire type 的类型决定使用的编码方式;常见的wire type类型大致分为3种:Variant、64-bit(定长)、Length-delimi(string、 bytes、 embeddedmessage);不同的wire type也决定了不同的存储方式。
TLV储存方式
Tag标签: 字段标识号 + wire type的varint编码格式。
Length: Value的长度
Value: 序列化后的数据