5

ProtoBuffer的数据格式

 3 years ago
source link: https://nnkwrik.github.io/2019/04/05/20190405/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

ProtoBuffer的数据格式

message Person {
int32 id = 1;//24
string name = 2;//wujingchao
string email = 3;//[email protected]
}

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

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

它的结构是下面这样

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
// 值 = [email protected],也是写入varint值

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

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

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 <<< 3 | 0  = 0x08

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

varints

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

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

它的转换过程如下:

ZigZag 编码

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

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

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


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK