该表显示了在.proto
文件中指定的类型,以及自动生成的类中的相应类型:
(相关资料图)
.proto Type | Notes | C++ Type | Java/Kotlin Type[1] Java/Kotlin 类型 [1] | Python Type[3] | Go Type | Ruby Type | C# Type | PHP Type | Dart Type |
---|---|---|---|---|---|---|---|---|---|
double | double | double | float | float64 | Float | double | float | double | |
float | float | float | float | float32 | Float | float | float | double | |
int32 | varint编码。对于负数编码效率低下——如果字段可能有负值,建议改用 sint32。 | int32 | int | int | int32 | Fixnum or Bignum (as required) | int | integer | int |
int64 | varint编码。对于负数编码效率低下——如果字段可能有负值,建议改用 sint64。 | int64 | long | int/long | int64 | Bignum | long | integer/string | Int64 |
uint32 | varint编码。 | uint32 | int | int/long | uint32 | Fixnum or Bignum (as required) | uint | integer | int |
uint64 | varint编码。 | uint64 | long | int/long | uint64 | Bignum | ulong | integer/string | Int64 |
sint32 | zigzag和varint编码。有符号的 int 值。比常规的 int32 能更高效地编码负数。 | int32 | int | int | int32 | Fixnum or Bignum (as required) ) | int | integer | int |
sint64 | zigzag和varint编码。有符号的 int 值。比常规的 int64 能更高效地编码负数。 | int64 | long | int/long | int64 | Bignum | long | integer/string | Int64 |
fixed32 | 总是四个字节。如果值通常大于 2\(^{28}\) ,则比 uint32 更有效。 | uint32 | int | int/long | uint32 | Fixnum or Bignum (as required) | uint | integer | int |
fixed64 | 总是八个字节。如果值通常大于 2\({^56}\) ,则比 uint64 更有效。 | uint64 | long | int/long | uint64 | Bignum | ulong | integer/string | Int64 |
sfixed32 | 总是四个字节。 | int32 | int | int | int32 | Fixnum or Bignum (as required) | int | integer | int |
sfixed64 | 总是八个字节。 | int64 | long | int/long | int64 | Bignum | long | integer/string | Int64 |
bool | bool | boolean | bool | bool | TrueClass/FalseClass | bool | boolean | bool | |
string | 字符串必须始终包含 UTF-8 编码或 7 位 ASCII 文本,并且不能长于 2\(^32\) 。 | string | String | str/unicode | string | String (UTF-8) | string | string | String |
bytes | 可以包含任何不超过 2\(^{32}\) 的任意字节序列。 | string | ByteString | str (Python 2) bytes (Python 3) | []byte | String (ASCII-8BIT) | ByteString | string | List |
对于传统的 xml 或者 json 等方式的序列化中,编码时直接将 key 本身加进去,例如:
{ "foo": 1, "bar": 2}
这样最大的好处就是可读性强,但是缺点也很明显,传输效率低,每次都需要传输重复的字段名。Protobuf 使用了另一种方式,将每一个字段进行编号,这个编号被称为 field number
。通过 field_number
的方式解决 json 等方式重复传输字段名导致的效率低下问题,例如:
message { int32 foo = 1; string bar = 2;}
field_number
的类型被称为wire types,目前有六种类型:VARINT
,I64
,LEN
,SGROUP
,EGROUP
, andI32
(注:类型3和4已废弃),因此需要至少3位来区分:
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 |
当 message 被编码时,每一个 key-value 包含
,其结构如下:
+--------------+-----------+---------+| field_number | wire_type | payload |+--------------+-----------+---------+ | | | | | | +---------------+ +---------------+ +--------->| (length) data | | tag | +---------------+ +---------------+
field_number 和 wire_type 被称为 tag,使用一个字节来表示(这里指编码前的一个字节,通过Varint编码后可能并非一个字节)。其值为 (field_number << 3) | wire_type
,换句话说低3位解释了wire_type,剩余的位则解释了field_number。payload 则为 value 具体值,根据 wire_type 的类型决定是否是采用 Length-Delimited 记录额外一提的是由于 tag 结构如上所述,因此对于使用 Varint 编码的 1个字节来说去除最高位标志位和低三位保留给 wire_type使用,剩下四位能够表示[0, 15] 的字段标识,超过则需要使用多于一个字节来存储 tag 信息,因此尽可能将频繁使用的字段的字段标识定义在 [0, 15] 直接。
编码规则Protobuf 使用一种紧凑的二进制格式来编码消息。编码规则包括以下几个方面:
每个字段都有一个唯一的标识符和一个类型,标识符和类型信息一起构成了字段的 tag。字段的 tag 采用 Varint 编码方式进行编码,可以节省空间。字符串类型的字段采用长度前缀方式进行编码,先编码字符串的长度,再编码字符串本身。重复的字段可以使用 repeated 关键字进行定义,编码时将重复的值按照顺序编码成一个列表。Varint 编码Varint 是一种可变长度的编码方式,可以将一个整数编码成一个字节序列。值越小的数字,使用越少的字节数表示。它的原理是通过减少表示数字的字节数从而实现数据体积压缩。Varint 编码的规则如下:
对于值小于 128 的整数,直接编码为一个字节;对于值大于等于 128 的整数,将低 7 位编码到第一个字节中,将高位编码到后续的字节中,并在最高位添加一个标志位(1 表示后续还有字节,0 表示当前字节是最后一个字节)。每个字节的最高位也称 MSB(most significant bit)。在解码的时候,如果读到的字节的 MSB 是 1 话,则表示还有后序字节,一直读到 MSB 为 0 的字节为止。例如,int32类型、field_number为1、值位 300 的 Varint 编码为:// 300 的二进制00000001 00101100// 按7位切割00 0000010 0101100// 高位全0省略0000010 0101100// 逆序,使用的小端字节序0101100 0000010// 每一组加上msb,除了最后一组是msb是0,其他的都为110101100 00000010// 十六进制指ac 02// 按照 protobuf 的消息结构,其完整位08 ac 02| |__|__ payload| |----------- tag (field-number << 3 | wire-type) = (1 << 3 | 0) = 0x08
ZigZag编码对于 int32/int64
的 proto type,值大于 0 时直接使用 Varint 编码,而值为负数时做了符号拓展,转换为 int64
的类型,再做 Varint 编码。负数高位为1,因此对于负数固定需要十个字节( ceil(64 / 7) = 10
)。(这里有个值得思考的问题是对于 int32 类型的负数为什么要转换为 int64
来处理?不转换的话使用5个字节就能够完成编码了。网上的一个说法是为了转换为 int64 类型时没有兼容性问题,此处由于还未阅读过源码,不知道内部是怎么处理的,因此暂时也没想通为什么因为兼容性问题需要做符号拓展。因为按照 Varint 编码规则解码的话,直接读取出来的值赋值给 int64 的类型也没有问题。int32 negative numbers)
很明显,这样对于负数的编码是非常低效的。因此 protobuf 引入 sint32
和 sint64
,在编码时先将数字使用 ZigZag
编码,然后再使用 Varint
编码。ZigZag 编码将有符号数映射为无符号数,对应的编解码规则如下:
static uint32_t ZigZagEncode32(int32_t v) { // Note: the right-shift must be arithmetic // Note: left shift must be unsigned because of overflow return (static_cast(v) << 1) ^ static_cast(v >> 31); }static uint64_t ZigZagEncode64(int64_t v) { // Note: the right-shift must be arithmetic // Note: left shift must be unsigned because of overflow return (static_cast(v) << 1) ^ static_cast(v >> 63); }int32_t ZigZagDecode32(uint32_t n) { // Note: Using unsigned types prevent undefined behavior return static_cast((n >> 1) ^ (~(n & 1) + 1));} static int64_t ZigZagDecode64(uint64_t n) { // Note: Using unsigned types prevent undefined behavior return static_cast((n >> 1) ^ (~(n & 1) + 1));}
因此如果传输的数据中可能包含有负数,那么应该使用 sint32/sint64
类型。因为 protobuf 中只定义了为这两种数据类型进行 ZigZag
编码再使用 Varint
编码。
wire_type
为 LEN
,由于其具有动态长度,因此其由一个 Length 值保存长度大小,这个 Length 同样通过 Varint 编码,最后是其内容。参照以下例子:
message Test2 { optional string b = 2;}b = "testing"12 07 [74 65 73 74 69 6e 67]| | t e s t i n g| | |__|__|__|__|__|__ body 的 ASCII 码| || |__ length = 6 = 0x06| |__ Tag (field-number << 3 | wire-type) = (2 << 3 | 2) = 18 = 0x12
标签:
-
环球动态:Protobuf编码规则
支持类型该表显示了在& 160; proto& 160;文件中指定的类型,以及自动生成的类中的相应类型:| protoType|No
-
克洛普:裁判觉得我质疑了他们的诚信,但我所说的话没有撒谎-当前热门
北京时间周四凌晨的英超第28轮补赛,利物浦将坐镇安菲尔德迎战富勒姆。在本场比赛之前的新闻发布会上,利物
-
大非农和小非农是什么意思 大非农是什么意思 世界快消息
今天来聊聊关于大非农和小非农是什么意思,大非农是什么意思的文章,现在就为大家来简单介绍下大非农和小非
-
【走访】钢花饰世界 匠心铸有元|秘书处走进湖南有元不锈钢制品公司 环球要闻
4月25日上午,长沙市平江商会顾问余攀霞,常务副秘书长魏一平,办公室主任余欢礼、法工委律师王昌江,会员
-
4月岚图汽车交付3339辆 环比增长10%
易车讯5月1日,岚图汽车官方发布了其4月的交付数据,4月岚图汽车交付3339辆。同比增长210%,环比增长10%。
-
日语怎么说日常用语中文版_日语怎么说日常用语_全球聚焦
1、常用日语こんばんは。2、konbanwa晚上好。3、おはようございます。4、ohayougozaimasu
-
全球热点评!良信股份:集团近期取得16项实用新型专利证书、3项外观设计专利证书
良信股份4月13日公告,公司及其子公司上海良信智能电工有限公司、良信电器(海盐)有限公司于近日取得国家知
-
开局首季各地经济“成绩单”出炉 怎么看?
从拉动经济增长的动力来看,一季度,上海、北京、浙江、天津、广东、江苏、福建、重庆、湖南等9省份居民人
-
未来十年热门行业排行榜_未来十年热门行业
1、2007中国大学十大最佳专业:最近看到很多网友评价中国大学的专业问题,忍不住发表一下自己的见解,如果
-
火影同人:鸣人成第七班导师,卡卡西儿子被针对,报复时刻到了 焦点关注
从图片上看,此时的鸣人,拿着卡卡西曾经让他们抢的铃铛,看着眼前矮自己一截的学生们的模样,仿佛又回到
-
全国游泳冠军赛首日:潘展乐破亚洲纪录 徐嘉余摘金
原标题:全国游泳冠军赛首日:潘展乐破亚洲纪录徐嘉余摘金中新社杭州5月1日电(钱晨菲)5月1日,2023年“韵味
-
漩涡鸣人六道仙人模式是什么_漩涡鸣人六道仙人模式
1、鸣人可能是通过母亲而得到的六道的体质,想想吧,当年是六道将十尾查克拉分成1~9尾,分散封印的,对
-
铁路等部门助力“五一”小长假旅客平安有序出行 当前热点
新华社北京5月2日电(记者樊曦、周圆)今年“五一”小长假是春节后的第一个小长假,旅客出行需求旺盛。铁路
-
371.1万人次!长三角铁路再创单日客发量历史新高 全球动态
图说:旅客在上海虹桥站检票上车来源 过云松摄新民晚报讯(记者金志刚)记者从中国铁路上海局集团有限公司
-
世界头条:八方游客畅享“皖美”旅程
22176人、29986人、29412人,这是黄山风景区“五一”假期前三天接待游客的“成绩单”,合计恢复至2019年“
-
韩国学者:韩美所谓“延伸威慑”刺激地区局势紧张
央视新闻客户端消息连日来韩美首脑会晤、韩美加强对朝遏制等消息引发韩国和国际舆论的担忧和批评。首尔传媒
-
全球速读:奥地利两列货运列车相撞 致少量危险品泄漏
据奥地利媒体消息,当地时间4月30日晚,奥地利基特湖地区两列货运列车相撞,造成列车和轨道系统受损,没有
-
今天!头条留给他们!
他们是一群平凡的人在平凡岗位磨砺非凡韧劲他们都有一颗奉献的心一直用匠心守望初心他们用平凡铸就非凡平凡
-
刚刚 破10亿元了! 世界新视野
统计数据显示,截至5月1日20时11分,2023年“五一档”电影总票房(含预售实时)已突破10亿元。《长空之王》
-
海参怎么吃的做法 海参怎么做好吃
今天来聊聊关于海参怎么吃的做法,海参怎么做好吃的文章,现在就为大家来简单介绍下海参怎么吃的做法,海参
-
直男总动员全文免费阅读_直男总动员
1、具体一点的?是穿越还是现代?我看过的有《大龙门客栈》还不错,《清空万里》这个很搞笑。2、讲八爷与穿
-
花面母灵猫怎么画_花面母灵猫
1、《花面母灵猫之花面母灵猫》读后感 男孩土娃去射击花面母灵猫时发现它抛弃了一只亲生猫仔,为了让它
-
“五一档”电影票房突破10亿元
统计数据显示,截至5月1日20时11分,2023年“五一档”电影总票房(含预售实时)已突破10亿元。《长空之王》
-
米体:曼联巴黎热刺有意亚伯拉罕,切尔西有8000万欧优先回购条款-世界观点
直播吧5月1日讯据《米兰体育报》报道,曼联、巴黎圣日耳曼和热刺等队均有意引进罗马前锋亚伯拉罕。报道称,
-
打新来了!5月8日至5月12日1只可转债开启申购 天天即时
打新来了!5月8日至5月12日1只可转债开启申购,5月8日至5月12日,共有1只可转债可申购,为光力科技可转债光
-
为了方便游客打卡,重庆让出一座跨江大桥
新华社重庆5月1日电(记者李晓婷吴燕霞)“亮灯了!”4月29日19时30分,随着重庆洪崖洞层层叠叠的灯带倏地
-
未能延续连涨势头!市场情绪抑制下比特币五月首日跌超2%-头条
比特币如今在3万美元附近停滞不前,交易员则在等待进一步的催化剂。加密货币交易所IndependentReserve负责
-
万人说新疆丨小小画家 今日热闻
夏提古丽是新疆尉犁县幼儿园的小朋友,小小年纪却对绘画有着极大的兴趣。和许多孩子一样,夏提古丽刚进幼儿
-
全球快看点丨津巴布韦议员:美国监听联合国秘书长不可接受
据美国媒体近日披露,美国政府监听联合国秘书长古特雷斯同各国领导人及联合国高官通话情况。津巴布韦议员在
-
英超综合:利物浦绝杀热刺 曼城力克富勒姆_天天信息
新华社伦敦4月30日电(记者张薇)30日的英超比赛,开场15分钟即三球领先的利物浦虽然眼见就要落得平局收场