前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >protocol buffers的编码原理

protocol buffers的编码原理

作者头像
charlieroro
发布2020-03-24 12:09:37
9160
发布2020-03-24 12:09:37
举报
文章被收录于专栏:charlieroro

protocol buffers使用二进制传输格式传递消息,因此相比于xml,json来说要轻便很多。

示例:假设定义了一个Message

代码语言:javascript
复制
message Test1 {
  required int32 a = 1;
}

实际使用的时候将a设置为150,然后将其序列化到输出流,查看编码后的message,可以看到如下3个byte

代码语言:javascript
复制
08 96 01

解析:

上述三个字节实际分为两部分: 08 96 01。第一部分(08)包含了message成员变量的field number(a=1)和变量类型(Varint),第二部分(96 01)为a的实际值150。

这里面涉及几个概念:

  Varint:这个可以理解为可变长的int类型,数值越小使用的byte越少;

  field number和type:protocol buffer消息为一系列的key-value对。二进制版本的消息使用field number作为key。

当接收到一个message时,解析器可以忽略无法识别的字段,通过这样的方式,也可以在不影响老功能的前提下添加新的字段。 通信格式下的key实际包含2个值:.proto文件中的field number,和通信类型。通信类型如下

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

message流中的key类型为varint,计算方式为:(field_number << 3) | wire_type ,即后三位保存了通信类型

上述第一个字节为08,转化为二进制为0000 1000,没个varint的第一个比特位为MSB位,置位表示后续还有字节。去掉MSB位后为

代码语言:javascript
复制
000 1000

后三位表示类型,值为0,表示类型为Varint;右移三位获取tag值为1(即message中设置的a = 1)

下面获取消息值150,注意:字节顺序为大端序

代码语言:javascript
复制
96 01 = 1001 0110  0000 0001
       → 000 0001  ++  001 0110 (drop the msb and reverse the groups of 7 bits)
       → 10010110
       → 128 + 16 + 4 + 2 = 150

以上介绍的时varint的编码方式,下面介绍一下其他类型的编码

Signed integer

int32和int64的实际类型都是varint,当它表示负数的时候,为10个固定字节长度的值,效率比较低。可以使用sint32和sint64来表示有符号的数值,它采用ZigZag编码,编码对应关系如下,实际就是把负数从0开始做了扩展。

Signed Original

Encoded As

0

0

-1

1

1

2

-2

3

2147483647

4294967294

-2147483648

4294967295

Non-Varint Numbers

非varint的值比较简单,double和fixed64的类型为1,表示64位固定长度的值;类似地,float和fixed32类型为5,表示固定32为长度的值,这两种情况下以小端序存储

Strings

类型为2,假设创建message如下,

代码语言:javascript
复制
message Test2 {
  required string b = 2;
}

实际消息b=“testing”

代码语言:javascript
复制
12 07 74 65 73 74 69 6e 67

首字节为特殊字节:0001 0010,去除msb位:001 0010,后三位->10表示类型2,右移三位->10表示tag 2;07表示长度为7,74 65 73 74 69 6e 67为"testing"的值。

Embedded Messages

假设定义嵌入的message如下:

代码语言:javascript
复制
message Test3 {
  required Test1 c = 3;
}

设置Test1.c=150,获得的结果如下,可以看到后三个字节跟上述的一致

代码语言:javascript
复制
1a 03 08 96 01

Packed Repeated Fields

proto2中使用repeated field需要启用特殊选项[packed=true],在proto3中,默认启用packed。如果packed repeated field中包含0个元素,则它不会出现在被解析的message中。

代码语言:javascript
复制
message Test4 {
  repeated int32 d = 4 [packed=true];
}

编码如下:

代码语言:javascript
复制
22        // tag (field number 4, wire type 2) ->0010 0,010
06        // payload size (6 bytes)
03        // first element (varint 3)
8E 02     // second element (varint 270)
9E A7 05  // third element (varint 86942)

只有使用了原始数据类型(如32-bit或64-bit的varint)的repeated fields才能称之为"packed"

可以看到string,message,repeated field是有长度字段的,而varint由每个字节的msb位表示一个varint是否有后续字节

proto的类型定义如下:

.proto

说明

C++

Java

Python

Go

Ruby

C#

PHP

double

double

double

float

float64

Float

double

float

float

float

float

float

float32

Float

float

float

int32

使用变长编码,对负数编码效率低,如果你的变量可能是负数,可以使用sint32

int32

int

int

int32

Fixnum or Bignum (as required)

int

integer

int64

使用变长编码,对负数编码效率低,如果你的变量可能是负数,可以使用sint64

int64

long

int/long

int64

Bignum

long

integer/string

uint32

使用变长编码

uint32

int

int/long

uint32

Fixnum or Bignum (as required)

uint

integer

uint64

使用变长编码

uint64

long

int/long

uint64

Bignum

ulong

integer/string

sint32

使用变长编码,带符号的int类型,对负数编码比int32高效

int32

int

int

int32

Fixnum or Bignum (as required)

int

integer

sint64

使用变长编码,带符号的int类型,对负数编码比int64高效

int64

long

int/long

int64

Bignum

long

integer/string

fixed32

4字节编码, 如果变量经常大于228228 的话,会比uint32高效

uint32

int

int

int32

Fixnum or Bignum (as required)

uint

integer

fixed64

8字节编码, 如果变量经常大于256256 的话,会比uint64高效

uint64

long

int/long

uint64

Bignum

ulong

integer/string

sfixed32

4字节编码

int32

int

int

int32

Fixnum or Bignum (as required)

int

integer

sfixed64

8字节编码

int64

long

int/long

int64

Bignum

long

integer/string

bool

bool

boolean

bool

bool

TrueClass/FalseClass

bool

boolean

string

必须包含utf-8编码或者7-bit ASCII text

string

String

str/unicode

string

String (UTF-8)

string

string

bytes

任意的字节序列

string

ByteString

str

[]byte

String (ASCII-8BIT)

ByteString

string

228

228 的话,会比uint32高效 uint32 int int int32 Fixnum or Bignum (as required) uint integer fixed64 8字节编码, 如果变量经常大于

256

256 的话,会比uint64高效 uint64 long int/long uint64 Bignum ulong integer/string sfixed32 4字节编码 int32 int int int32 Fixnum or Bignum (as required) int integer sfixed64 8字节编码 int64 long int/long int64 Bignum long integer/string bool bool boolean bool bool TrueClass/FalseClass bool boolean string 必须包含utf-8编码或者7-bit ASCII text string String str/unicode string String (UTF-8) string string bytes 任意的字节序列 string ByteString str []byte String (ASCII-8BIT) ByteString string

参考:Encoding

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2018-05-07 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档