Protocol-buffers序列化规则
Type | Meaning | Used For |
---|---|---|
0 | Varint | int32, int64, uint32, uint64, sint32, sint64, bool, enum |
1 | 64-bit | fixed64, sfixed64, double |
2 | Length-delimited | string, bytes, embeded, messages, packed repteated fields |
5 | 32-bit | fixed32, sfixed32, float |
一、基本结构
第一个字节中存储序列化的数据编号和序列化方式,字节的后3位的值代表了序列化方式,取值范围为[0, 5],其中3和4为已弃用的序列化类型。字节的前5位代表的序列化的字段编号。比如如下的类型
message Test1
{
optional int32 a = 1;
}
如果a被赋值为150,那么被序列化后的值为
08 96 01
第一个字节08的二进制值为0000 1000,前5位数值为1,代表设置给a的编号,后三位值为0,代表以Varint的形式序列化,后两个字节的值就是150以Varint方式序列化后的值。
二、序列化规则
1、Varint
这种方式用于序列化整型数据,会根据数值的大小动态改变占用的字节长度,越小的数值占用的更少的字节数。该序列化方式以一个字节为最小单位,以每个字节的第一位的值判断是否到了数据末尾,值为0代表无后续数值,值为1代表还有后续数据。后7位用于存储原数据,存储顺序以整数的低位优先。例如
0000 0001 代表 1
1010 1100 0000 0010 代表300
0000 0001
第一位值为0,代表已是数据的最后一个字节,数据值为就为1
1010 1100 0000 0010
第一个字节为1010 1100的第一位为1,代表下一个字节仍存储数据信息,而这个字节存储的信息为010 1100
第二个字节为0000 0010的第一位为0,代表该字节为存储的数据的最后一个字节,存储的信息为0000 0010
按照整数低位优先的顺序,010 1100代表低位,0000 0010代表高位,因此最后的代表的数据应该为
0000 0010 010 1100 --> 300
对于符号整数,如果值为负数,那么存储数值中的第一位永远为1,若按上面的规则来执行,将永远要用一个很长的字节来存储该数值,即使是-1这么看起来很简单的负数。为达到序列化后的长度与值的绝对值正相关,会对负数进行一定处理,再存储。主要的方式就是将符号整数映射为无符号整数,再按以上规则操作。映射规则为
32位:
(n << 1)^(n >> 31)
64位:
(n <<1 )^(n >> 63)
n位原符号整数的数值,计算效果等同于
if(n<0)
{
-n*2 -1;
}else
{
n*2;
}
而该操作只对sint32和sint64类型的数据进行,int32和int64仍是直接序列化。
2、64-bit, 32-bit
这两种比较简单,直接将头部字节后的64/32字节的数据作为原数据,未做任何其他处理
3、length-delimited
对于string或者packed repteated fields等长度未知的数据类型,将以这种方式序列化。
该方式使用第二个字节的值代表存储的长度,例如:
message Test2
{
optional string b = 2;
}
当b的值为"testing"时候,序列化后的数值为
12 07 [74 65 73 74 69 6e 67]
07 代表后面7个字节为该存储的该类型的数值,即方括号中的值(以UTF8编码)
该序列化方式也用于处理类型嵌套,如:
Message Test3
{
optional Test1 c = 3;
}
message Test1
{
optional int32 a = 1;
}
如果c.a的取值为150,那么Test3序列化结果为
1a 03 08 96 01
保存的3个字节的数据正是Test1的序列化结果。
三、参考资料
https://developers.google.com/protocol-buffers/docs/encoding#varints Protocol Buffers官方参考文档