protobuf编码格式解析

示例

假如定义一个如下的protobuf类型

message Person {
    required string user_name       = 1;
    optional int64  favorite_number = 2;
    repeated string interests       = 3;
}

将其赋值为:

user_name :   "Martin"
favorite_number :    1337
interests:"daydreaming", "hacking"

则生成的数据解析如下:
protobuf编码格式解析_第1张图片

数据格式

从上图可以看到整个数据的解析过程。从这个过程中可以看出,实际上。protobuf的数据格式是tag+length+value模式(TLV模式)。如下:
protobuf编码格式解析_第2张图片
protobuf数据就是这样一个长条形的序列。再继续细分的话,可以看到tag实际上是有2部分组成。

tag= tag<<3 | type

tag的高5位为tag,tag更容易理解的话就是数据的编号,上例中user_name 的tag=1,说明读到tag=1,后面跟的数据就是user_name。 favorite_number 和interests 都是同理,只不过其编号分别是2 和3,这个tag在定义.proto文件时就会固定。解析的话,就是根据这个tag来表明tag后面的数据含义。

继续看下type是干啥。在protobuf中,谷歌定义了几种数据类,type实际上是一个数据类型的集合。

ID Name Used For
0 VARINT int32, int64, uint32, uint64, sint32, sint64, bool, enum
1 I64 fixed64, sfixed64, double
2 LEN string, bytes, embedded messages, packed repeated fields
3 SGROUP group start (deprecated)
4 EGROUP group end (deprecated)
5 I32 fixed32, sfixed32, float

其中3和4已经废弃掉了实际上没有用到。
示例中第一个字节是0x0a,对应的tag=1,type=2。当type=2的时候,tag后面跟的数据就是length。当type为其他值的时候,tag后面的数据就是value。

解析tag=1数据

继续来看tag=1的数据,
0a 06 4d 61 72 74 69 6e
第二个字节就是长度为6字节,表示tag=1的数据有6字节长度。其value=4d 61 72 74 69 6e ,转换成ascII就是Martin。至此tag=1的数据解析完毕。

解析tag=2数据

继续来看tag=2的数据
10 b9 0a
0x10解析出来后tag=2 ,type=0。此时b9就是数据而不是数据长度。一直tag=2是favorite_number 数据,为1337,所以b9 0a 解析出来应该是1337(0x539)。而1337的十六进制表示0x539,可见这个编码并不是实际的16进制数。

对于数字类型的,采用的是Varints编码模式。这种编码模式采用的是小端模式。其每个字节实际上是拆分成了2个部分。
最高bit(bit7)表面下一个字节是否是数据的一部分。
bit7=1,下一个字节数据中还是包含的数据。
bit7=0,数据只有一个字节
bit6-0 是数据。

具体来看b9 0a这两个数据
b9二进制为1 0111001 ,其bit7=1说明下一个字节0a也是也是favorite_number 的一部分
0a二进制为0 0001010 ,其中bit7=0,说明favorite_number 的数据到此结束。
由于采用的是小端模式。所以最终的数据是0001010 0111001,对应的16进制是539,十进制为1337,这样就解析出来了favorite_number 的值。编码的过程就是这个逆过程。

参考:
https://www.jianshu.com/p/73c9ed3a4877
https://protobuf.dev/programming-guides/encoding/
https://gitee.com/ljango/ddia/blob/master/ch4.md#thrift%E4%B8%8Eprotocol-buffers

你可能感兴趣的:(STM32,nanopb,probotbuf,数据结构,编码,单片机)