ProtoBuffer将bool类型Varint改为全位掩码枚举是否具备前向兼容性?
问题背景
我希望对如下ProtoBuffer消息进行改造,保证其前向兼容性。
当前Storage消息的state字段定义为bool类型:
message Storage { bool state = 1; }
ProtoBuffer编码中,bool、enum这类Varint类型的编码格式如下:
|1-bit sequence number|4-bit serial number|3-bit data type|n-bit payload|
Varint类型对应的类型标识位为000,因此编码格式为:
|X|XXXX|000|XXXX...|
由于Storage消息仅包含1个字段号为1的字段,序列号未解析到最后一个字节时值为0,因此最终编码格式为:
|0|0001|000|XXXX...|
当Storage.state设为0时,0值不会被编码,最终Storage消息的ProtoBuffer编码值为0x8;设为1时编码值为0x8 0x1。
现在计划将Storage.state的类型从bool改为如下枚举类型:
// BIT7 | BIT6 | BIT5 | BIT4 | BIT3 | BIT2 | BIT1 | BIT0 | //------------------------------------------------------- // 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | = STATE0 (0) // 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | = STATE1 (1) // 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | = STATE2 (2) // 0 | 0 | 0 | 0 | 0 | 0 | 1 | 1 | = STATE2 (3) //... so go on // 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | = STATE2 (254) // 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | = STATE2 (255) enum State { STATE0 = 0; STATE1 = 1; STATE2 = 2; STATE3 = 3; //... so go on STATE254 = 254; STATE255 = 255; } message Storage { State state = 1; }
改造后在Protobuf编码规则下:
Storage.state设为State.STATE0时编码为0x8;设为State.STATE1时编码为0x8 0x1;设为State.STATE2时编码为0x8 0x2;设为State.STATE255时编码为0x8 0xFF。
兼容性结论
该改造在编码层面完全兼容,仅在特定场景下存在语义差异,分场景说明如下:
按Proto标准划分
- proto2标准
- 新版本接收老版本消息:老版本发送的false(值0)、true(值1)完全匹配枚举定义的STATE0、STATE1,可正常解析,无任何异常
- 老版本接收新版本消息:若新版本发送STATE0/STATE1,老版本可正常解析为false/true;若新版本发送≥2的枚举值,proto2会将超出bool范围的Varint值统一识别为true,不会触发解析错误,但会丢失具体枚举值信息
- 注意:如果老版本中state字段定义为
required,也不会影响解析,因为字段号和编码类型完全匹配
- proto3标准
- 新版本接收老版本消息:和proto2一致,0、1可完美匹配枚举值
- 老版本接收新版本消息:和proto2表现一致,≥2的枚举值会被识别为true
- 额外特性:proto3默认支持开放枚举,若后续枚举扩展超出255,新版本也可以正常接收未知整数值,不会当作未知字段丢弃
按语言环境划分
- C语言(protobuf-c/官方protobuf库)
两种标准下都不会抛出解析异常:- 老版本C程序解析≥2的Varint值时,会直接将bool变量赋值为1(true),无崩溃风险
- 新版本C程序解析老版本的0、1值,可直接映射为对应枚举值,无异常
- Java语言(官方protobuf库)
- 老版本Java程序解析≥2的Varint值时,会自动转换为boolean类型的true,不会抛出异常
- 新版本Java程序解析老版本的0、1值,可直接映射为对应枚举值;若后续收到枚举定义外的整数值,proto3会自动封装为
UNRECOGNIZED枚举值,proto2会将其存入未知字段列表,均不会报错
注意事项
唯一的兼容风险是语义层面:如果业务逻辑中依赖老版本返回的true/false二值判断,新版本发送≥2的枚举值时,老版本会全部识别为true,如果业务没有兼容这种情况会出现逻辑错误,这个不属于编码层面的兼容性问题,需要业务层面提前适配。
内容的提问来源于stack exchange,提问作者ecle
相关产品推荐
相关产品推荐

