cloudwu / sproto

Yet another protocol library like google protocol buffers , but simple and fast.
MIT License
942 stars 253 forks source link

sproto add map support #94

Closed t0350 closed 4 years ago

t0350 commented 4 years ago

sproto 目前对 map 的支持比较弱,比如形如 map<string, int> 则不能支持,而且对值也有要求。

增加对 map 的编码支持,实现上和原来的实现接近,是将 key / value 对编码为数组。通过在生成 sproto 的定义文件时,另外插入一个 entry 定义,并借助该定义编码。

比如定义:

.Person {
  id 0 : integer
}

.AddressBook {
  person 0: [string]Person
}

最终生成的定义文件相当于:

.Person {
  id 0 : integer
}

.AddressBook {
  .person {
    .entry {
      key 0: string
      value 1: Person
    }
  }
  person 0: *person.entry
}

并以这种形式编码。

麻烦云风看看,希望考虑下增强对 map 的支持!

cloudwu commented 4 years ago

暂不考虑合并,不过 pr 会保持打开。

t0350 commented 4 years ago

这个特性增强没有兼容性问题,想问下暂不考虑合并是出于稳定性的考虑吗 ?

cloudwu commented 4 years ago

因为如非必要,不增加新的特性。

cloudwu commented 4 years ago

另外一个想法:能否在原有的语法基础上扩展出来? 原来的方法是

.AddressBook {
    person 0 : *Person(id)
}

这样申明一个 key 为 id ,value 是 Person 结构。这样就是 id->Person 这样的 map 。 这里的需求其实是在 Person 足够简单的时候(只有 key value 两项),不用一个复杂结构。 是否可以用

.Person {
  id 0 : integer
  value 1 : string
}

.AddressBook {
    person 0 : *Person()
}

当原有的 () 里为空时,要求 Person 必须只有两项,第一项为 key 第二项为 value 。编码的时候还是按 *Person 编码;解码的时候变成 map ?

t0350 commented 4 years ago

我觉得在原有的语法上扩展出来的 *Person() 这样的形式,表达力上是比不上 [key]Person 的,前者 key 始终取决于 Person 这个结构。

原来的 *Person(id) 这样的形式我倾向于认为是比较 workaround 的形式,是可以解决业务中部分的问题(可能甚至是大部分),但始终有另一小部分问题绕不开。

t0350 commented 4 years ago

想了下,使用 *Person() 这种形式和原来对 map 的支持在定义上是更一致,从 sproto 整体考虑是更好,但对于业务上可能不一定更易用。如果云风仍然觉得选择 *Person() 这种形式更优我可以改过来。

cloudwu commented 4 years ago

我主要从编码角度看,本质上是对 Person list 的编码,然后解码成特殊的内存形式。

t0350 commented 4 years ago

我整理了一下,你看看这样 ok 不。

如果使用 *intstr() 描述的话, 那使用

{[3] = "a", [4] = "b"}

会以

.intstr {
  i 0 : integer
  s 1 : string
}

的数组形式编码,并解码到内存为输入的 map。

对比原有的 *intstr(i),则需要使用

{[3] = {i = 3, s= "a"}, [4] = {i = 4, s = "b"}}

并以

.intstr {
  i 0 : integer
  s 1 : string
}

的数组形式编码,并解码到内存为输入的 map。

cloudwu commented 4 years ago

差不多。我认为应该满足一点:

当不写 () 时,依旧是合法的。比如一端可以用没有 () 的 schema 编码(使用合适的内存结构),另一端有 () 也可以 decode 。 反之亦然。如果用新增加的特性 () 进行 map 编码,另一端即使不升级代码,只需要在 schema 中去掉 () 或作最小的修改,忽略掉 () ,就可以用旧形式解码。

从这一点来说写 () 和 (id) 都是对原有列表的扩展,影响的是 lua 中的数据呈现形式,并不影响编码的二进制结构;而编码方式都是以列表形式存在的。

t0350 commented 4 years ago

@cloudwu 修改好了,麻烦再看看。

增加了一个 testcompat.lua 文件测试新旧格式互相转化。()(id) 仅作为解码到内存中的格式表达,编码结构不受类型这两者定义影响。

cloudwu commented 4 years ago

大致看了一遍,明白了实现的逻辑。不过细节没有推敲,先默认没有问题 :)

cloudfreexiao commented 4 years ago

@t0350 麻烦 能否 补下 在 readme 里 补下 map 相关内容

t0350 commented 4 years ago

好的,我修改下 readme。