starwing / lua-protobuf

A Lua module to work with Google protobuf
MIT License
1.71k stars 388 forks source link

对于proto3,如果设置了encode_default_values选项,repeated字段默认设置为packed #227

Closed sundream closed 1 year ago

sundream commented 1 year ago

这样做的目的是通过配置: pb.option("no_default_values") pb.option("encode_default_values") pb.option("no_decode_default_array") 可以确保任何字段服务器发nil,客户端会得到nil,服务器发{},客户端会得到{},为改进前,上面配置服务器发{},客户端会得到nil 最终的目的是实现: 参与编码的数据和解码得到的数据完全相等,具体看test.lua#L495

starwing commented 1 year ago

这样改不是特别好。首先proto3的数组就是默认packed的,你改了这个设定容易出各种兼容性问题。目前,如果服务器发了{}(注意这不是标准行为),那么客户端一定是{},但是,服务器如果没发,客户端就需要处理no_default_values和no_decode_default_array了。我刚刚试了下,现在的行为就是满足要求的:

local pb = require "pb"
local protoc = require "protoc"

protoc:load [[
syntax = "proto3";
message Test {
   repeated int32 foo = 1;
}
]]

pb.option("no_default_values")
pb.option("encode_default_values")
pb.option "no_decode_default_array"
print(require "serpent".block(pb.decode("Test", pb.encode("Test", {}))))
print(require "serpent".block(pb.decode("Test", pb.encode("Test", {foo={}}))))

输出:

{} --[[table: 0x600000f06d40]]
{
  foo = {} --[[table: 0x600000f07280]]
} --[[table: 0x600000f07240]]
sundream commented 1 year ago

在如下配置模式下 pb.option("no_default_values") pb.option("encode_default_values") pb.option "no_decode_default_array" 主要是因为:pb.h#L1637行 if (f->type_id >= 9 && f->type_id <= 12) f->packed = 0; 会造成服务器发{},客户端会收到nil,你上面的测试示例改成如下即可重现

local pb = require "pb"
local protoc = require "protoc"
-- 注意放到protoc.load前配置!
pb.option("no_default_values")
pb.option("encode_default_values")
pb.option "no_decode_default_array"

protoc:load [[
syntax = "proto3";
message TestNestMessage {
    int32 num = 1;
}

message Test {
   repeated int32 foo = 1;
}

message Test2 {
   repeated TestNestMessage foo = 1;
}
]]

print(require "serpent".block(pb.decode("Test", pb.encode("Test", {}))))
print(require "serpent".block(pb.decode("Test", pb.encode("Test", {foo={}}))))
print(require "serpent".block(pb.decode("Test", pb.encode("Test2", {}))))
print(require "serpent".block(pb.decode("Test", pb.encode("Test2", {foo={}}))))

旧版代码会得到输出

{} --[[table: 0x1d82ec0]]
{
  foo = {} --[[table: 0x1d838a0]]
} --[[table: 0x1d83830]]
{} --[[table: 0x1d868f0]]
{} --[[table: 0x1d871e0]]

新版代码会得到输出

{} --[[table: 0x2119bc0]]
{
  foo = {} --[[table: 0x211a5a0]]
} --[[table: 0x211a530]]
{} --[[table: 0x211d650]]
{
  foo = {} --[[table: 0x211e090]]
} --[[table: 0x211e020]]
starwing commented 1 year ago

我懂了,但是不能这样,packed子消息是非法的,其他的protobuf解析器没办法解析这种数据。