protobuf编解码

基本原理:

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 :见示例一分析过程

你可能感兴趣的:(protobuf编解码)