农场主的黑科技.

ProtoBuffer的数据格式

字数统计: 657阅读时长: 4 min
2019/04/05 Share

ProtoBuffer的数据格式

1
2
3
4
5
message Person {
int32 id = 1;//24
string name = 2;//wujingchao
string email = 3;//wujingchao92@gmail.com
}

上面这条简单的protobuf消息编码后的结果如下

1
08 18 12 0a 77 75 6a 69 6e 67 63 68 61 6f 1a 16 77 75 6a 69 6e 67 63 68 61 6f 39 32 40 67 6d 61 69 6c 2e 63 6f 6d

它的结构是下面这样

1
2
3
4
5
6
7
8
9
08	// tag (field number 1, wire type 0)
18 // 值 = 24,它的varint值 = 18
12 // tag (field number 2, wire type 2)
0a // 表示后面的数据长度为10个字节, varint值 = 0a
77 75 6a 69 6e 67 63 68 61 6f // 值 = wujingchao,写入utf-8编码的varint值
1a // tag (field number 3, wire type 2)
16 // 表示后面的数据长度为22个字节, varint值 = 16
77 75 6a 69 6e 67 63 68 61 6f 39 32 40 67 6d 61 69 6c 2e 63 6f 6d
// 值 = wujingchao92@gmail.com,也是写入varint值

上面出现的tag和varint是什么,如何得出的?

tag

表示一个属性时首先要求他的tag值:

1
tag的计算方式: (field_number << 3) | wire_type

在上面的例子中id的field_number就是1。

每种数据类型都有对应的wire_type:

Wire 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

所以属性id的tag值是

1
1 <<< 3 | 0  = 0x08

需要注意的是 Length-delimited,如string类型,后面需要跟着的Varint类型表示数据的字节数。

varints

一般情况下int类型都是固定4个字节,protobuf定义了一种变长的int,每个字节最高位表示后面还有没有字节,低7位就为实际的值,并且使用小端的表示方法。因此可以用更少的字节来表示一个int值

例如300,4字节表示为:10 0101100,varint表示为:

1
10101100 00000010

它的转换过程如下:

图 6. Varint 编码

ZigZag 编码

负数的最高位为1,如果负数也使用这种方式表示就会出现一个问题,int32总是需要5个字节,int64总是需要10个字节。所以定义了另外一种类型:sint32,sint64。采用ZigZag编码,所有的负数都使用正数表示。

图 8. ZigZag 编码

参考:

https://www.ibm.com/developerworks/cn/linux/l-cn-gpb/index.html

https://blog.csdn.net/mine_song/article/details/76691817

CATALOG
  1. 1. ProtoBuffer的数据格式
    1. 1.1. tag
    2. 1.2. varints
    3. 1.3. ZigZag 编码