protobuf现在用的也越来越广了,出于好奇研究了一下他的编码格式,和大家一起分享
上一篇博客已经介绍了如何安装和使用protobuf
先介绍 数字1 是怎么存的
Person.proto
syntax = "proto2";
package dong;
message Test1 {
required int32 a = 1;
}
message Test2 {
required uint32 age = 1;
required string name = 2;
}
这里主要是创建了一个Test1这样一个结构体,里面存的是int32类型
现在就看看1这样一个int32类型 protobuf是如何进行编码的吧
首先科普一下:
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, embedded messages, packed repeated fields |
3 | Start group | groups (deprecated) |
4 | End group | groups (deprecated) |
5 | 32-bit | fixed32, sfixed32, float |
【08 01 00】
先解释 08:
08 -> 0000 1000
右移3 -> 00001 000 前面的就是 tag 后面就是 type
tag 就是 required int32 a = 1; 中的编号1
type就是上面表格中的Type 用于辨别是什么类型
所以 08 就代表了 tag = 1,type = int32
再解释 01 00
01 00 -> 0000 0000 0000 0001(注意是小端字节序)
先说Varints:
Varints是使用一个或多个字节序列化整数的方法,varint中的每个字节都具有最高有效位(msb)
也就是说最高位如果被置1那么说明还没有结束,直到有一个字节的最高位为0,说明这个数结束了
所以 000 0000 + 0000 0001 的值就是1
再介绍正数和string的编码
age 设置为 150
name 设置为 shuai dong
编码后变成了 【08 96 01 12 0a 73 68 5 61 69 20 64 6f 6e 67】
现在我就手动来解码
- 第一个字节是Varints键 08 -> 由上面可知代表了 tag = 1 , type = 0
- 96 -> 1001 0110 msb位不为0
- 01 -> 0000 0001 msb位为0
- 解析tag1:
- 去掉msb: 001 0110 0000 0001
- 还原:0000 0001 + 001 0110 = 1001 0110 = 150 (再一次提示是小端字节序)
- 第一个字节是Varints键 12 -> 0001 0010 由上面可知 tag = 2 , type = 2
- 0a:代表后面后10个字节
- 解析tag2:
- 看一下上面的截图吧后面刚好10个字节 对应的字符串是 shuai dong【帅 东】
可见整数1和整数150占不同的字节
Varints是使用一个或多个字节序列化整数的方法。较小的数字占用较少的字节数。
再说一下uint32的负数形式吧
可见负数占了10个字节
带符号的int类型(sint32和sint64)和“标准”类型(int32和int64)之间存在重要的区别。如果使用int32或int64作为负数的类型,则生成的varint 始终为十个字节长, 它被有效地视为非常大的无符号整数。
sint的编码格式:
Signed Original | 编码为 |
---|---|
0 | 0 |
-1 | 1 |
1 | 2 |
-2 | 3 |
2147483647 | 4294967294 |
-2147483648 | 4294967295 |
每个值n都使用编码:
sint32(n << 1 )^ (n >> 31 )
sint64(n << 1 )^ (n >> 63 )
Repeated Fields 今天不想写了 如果明天心情好就在写一篇