基本原理:
1.可变长度编码 & 跳过可选字段
2.作用在网络传输过程
一、存储方式
TAG [LENGTH] VALUE
TAG: filedId(前五位bit)+ WIRE_TYPE(低三位bit) 1 byte
LENGTH: WIRE_TYPE = 2 时 存在 1byte
VALUE:WIRE_TYPE = varint 时 采用小端存储模式,其他正常读取
二、WIRE_TYPE
0: varint变长编码,主要就是依靠这个来减小存储体积
1:定长 8byte
2: 指定长度
3、4 :已废弃
5:定长 4 byte
三、Varint 原理
int32 类型的数字,一般需要 4 个 byte 来表示。但是采用 Varint,对于很小的 int32 类型的数字,则可以用 1 个 byte 来表示。
采用 Varint 表示法,大的数字则需要 5 个 byte 来表示。从统计的角度来说,一般不会所有的消息中的数字都是大数,因此大多数情况下,采用 Varint 后,可以用更少的字节数来表示数字信息
小端存储模式
示例1:
对于数字1 对应的二进制是 :00000000 00000000 00000000 00000001
PB 只用一个字节就可以存储该值:即 0 00000001
第一位0 表示该字节就是结束字节,后七位 0000001 即表示十进制的数字 1
示例2:
对于数字 500,对应的二进制:00000000 00000000 00000001 11110100
从最低位开始 七位分割 即:1110100 0000011
PB编码用两个字节表示 :1 1110100 0 0000011
解码:高位是 1 表示 还要读后面一个字节
去掉最高位:1110100 0000011
由于是小端模式:组合后是 00000111110100 十进制即是500
三、zigTag 编码(解决负数占用多字节问题)
原码:最高位为符号位,剩余位表示绝对值;
反码:除符号位外,对原码剩余位依次取反;
补码:对于正数,补码为其自身;对于负数,除符号位外对原码剩余位依次取反然后+1
原码缺陷:
1、 0 有两种表现形式 :00000000 和 10000000
2、计算错误:1 + (-1) = 00000001 + 10000001 = 10000010 = -2
补码解决的问题:1+(-1) = 00000001+ 11111111 = 00000000 = 0
zizag会对负数进行一轮哈希映射,拿到hash值后,编码策略:直接去掉hash值的前导0之后的byte作为压缩编码
四、一些示例
示例1:
message Test1 {
optional int32 a = 1; //表明是 fildId 是 1
}
创建 Test1消息并把a设置为150
编码后:08 96 01
编码后对应二进制:00001000 10010110 00000001
解码:
(1)000001000 后三位:000 表示WIRE_TYPE = 0,即 Varint;00001 = 1 表示对应第一个 fieldId;
(2)10010110 00000001:
10010110 最高位 1 表示还需要读取下一个字节,剩余 0010110;
00000001 最高位表示无需读下一个字节,剩余 0000001;
按照小端模式进行拼接:00000010010110 转化为10进制 即 150
示例2:嵌套消息(嵌套的消息会作为WIRE_TYPE = 2 来对待)
Message Test2{
optional Test1 c = 3;
}
如示例1 给Test1 a 赋值 150
则得到的编码:1a 03 08 96 01
1a 对应 2进制:00011010 后三位表示 有线类型 2,前五位表示 3 对应的 是fieldId
03 表示长度:及3个字节的长度
08 96 01 :见示例一分析过程