RFC-0048:显式联合序数

RFC-0048:显式并集序数
状态已接受
领域
  • FIDL
说明

为了更好地统一对可扩展联合(或简单联合)的 ABI 影响,我们提议做出一些更改,使灵活联合在语法上更接近表,并更正目前有关重命名联合或联合成员的奇数 [2] 和过分严格的 ABI 限制。

作者
  • pascallouis@google.com
提交日期(年-月-日)2019-08-25
审核日期(年-月-日)2019-09-26

总结

为了更好地协调可伸缩联合(或简单联合)的 ABI 影响,我们提议:

  1. 将变体成员的语法更改为需要显式序数(与需要表序数的方式类似)。
  2. 使用此显式序数,而不是之前实现的哈希序数。
  3. 最后,我们更改传输格式,使联合序数为 64 位(而不是 32 位)。

这些更改使灵活联合在语法上更接近表,并针对重命名联合或联合成员纠正了奇数1 和过分严格的 ABI 限制

动机和设计

在涉及类型时,除了联合之外,名称对 ABI 没有影响:枚举、位、结构体和表都可以重命名,或者重命名其成员,而无需担心二进制文件兼容性。联合会有所不同,因为我们选择使用基于哈希的技术分配变体序数(请参阅 RFC-0061:可扩展联合)。

我们已经认识到这一缺点,并提出了解决此问题的方法:请参阅实现意图:xunion 序数更改,其中建议将哈希方案更改为仅包含成员名称。

相反,该方案更进一步,仅使用显式序数,从而避免名称与 ABI 兼容性之间的任何关联。我们认为,在联合声明中编写序数所付出的额外工作量极少:到目前为止,表中的显式序数不是问题,对来自其他常用 IDL 的成员或变体进行编号之前就有广泛存在。

此外,为了进一步与表保持一致,我们要求从 1 依序分配序数,并允许使用关键字 reserved 明确跳过并集变体。

仅针对协议进行哈希处理

与类型不同,协议使用基于哈希的方法分配序数。这由两个关键用例推动:

  • 协议可以组合,因此我们需要一个全局序数分配方案,以避免在距离出现损坏时。请参阅 [RFC-0063:OrdinalRangeRFC-0020:接口序数哈希RFC-0029:增加方法序数
  • 实际上全局唯一标识符可大大简化和加强监控和跟踪等需求,例如,Fielcat 可对方法调用进行唯一标识的能力。

这些用例不会转换为类型。

这项方案的影响是,哈希技术仅用于协议。只有一种哈希方案,即 RFC-0029 中所述的方案。

64 位序数是标准格式

目前,联合内联内容中有 4 个字节的填充:

  • 序数 (uint32)
  • 内边距 (uint32)
  • 信封(16 个字节)

相反,我们希望所有字节都以显式方式调用,因此将序数更改为 64b。通常,我们更倾向于无填充结构,因为它们效率更高(例如,不需要显式模元集或额外的编码表)。

如需了解如何软转换为 64 位序数,请参阅实现策略部分。

JSON 有线格式

过去讨论过,当我们创建 JSON 传输格式时,重命名类型和成员会对该格式造成 ABI 影响。

按“有线格式”分隔 ABI 合规性破坏问题很有用。我们可以从不同的属性获取不同的属性。需要在支持的所有可能的传输格式上实现 ABI 兼容性的罕见消息在发展方式上非常有限。其他合作伙伴应尽可能受益于更为灵活的规则。

展望未来:稀疏表

曾经讨论过如何支持稀疏表,即除了结构体之外,第三种布局可用于类似记录的数据。如果我们决定引入第三个选项,则 strawman 语法将遵循该方案,以及当前表的语法:

sparse_table Example {
    1: T1 field1;
    2: reserved; // deprecated
    3: T3 field3;
};

实施策略

为了实现软转换,我们需要消除经典(32 位哈希)序数语法与所提议的(64 位显式)序数语法之间的歧义。这可以通过以下操作来实现:

  1. 在 fidlc 中添加一项检查,以确保 32 位哈希序数的值始终不小于 N。例如,如果 N 为 512,则经过哈希处理的序数的十六进制值必须至少为 0x200。
    • 小于 0x200 的哈希序数将导致编译错误,并且必须使用 [Selector=] 属性手动重命名字段名称。先将 [Selector] 添加到相应字段中,然后再将此项更改提交到 fidlc。
    • 鉴于现有哈希方案的随机性,我们预计哈希错误会发生接近零的次数,因此可能不需要手动解析。
    • 添加此检查后,系统会有效地为显式序数分配 [0..N] 序数空间,并确保其不会与经过哈希处理的序数发生冲突。
  2. 当语言绑定解释序数值时:
    • 如果序数介于 [0, N) 之间,则序数为 64 位显式值。
    • 如果序数介于 [N, UINT32_MAX] 之间,则序数为 32 位并经过哈希处理。
    • 如果序数介于 [UINT32_MAX, UINT64_MAX) 之间,绑定必须引发错误,并使用 epitaph 来关闭通道。

工效学设计

大大简化了 ABI 的人体工程学,但以极低的语法开销,因为拥有显式序数。

文档和示例

至少:

向后兼容性

未使用显式序数语法的联合将继续使用现有的 32 位哈希序数架构。因此,现在存在的联合体将继续与 API 和 ABI 兼容。

通过显式序数语法定义的联合将使用此 RFC 中描述的 64 位序数方案。如需了解如何同时支持 32 位和 64 位序数方案,请参阅实现策略部分。

性能

只有细微的改进:由于 switch() 语句的代码生成功能更好,此方案比哈希序数更有效。

安全性

无影响。

测试

和往常一样,微不足道。

缺点、替代方案和未知情况

替代方案:仅对成员名称进行序数哈希处理

请参阅实现意图:xunion 序数更改

经过进一步的考虑,我们并未充分考虑上述问题,因为语法优势(源语言中没有序数)无法弥补:

  • 两种哈希处理方案,加大了理解 ABI 影响的难度;
  • 在重命名声明或成员时,确保联合与其同级项枚举、位、结构体和表不同。

早期技术和参考资料

都不是特别相关的。


Footnote1

本文档中的“Union”是指可扩展联合体,而不是即将终止服务期的“静态”联合体。

Footnote3

发件人:apang@google.com
收件人:fidl 用户列表
日期:2019 年 5 月 23 日

FIDL 用户,您好!在昨天编写测试时,FIDL 团队发现,在使用当前的 xunion 规范和 impl 时,会出现令人惊讶的行为。如果声明如下:

xunion MyXUnion {
    int32 i;  // ordinal might be 0x11111111
}
````

and renames the name of the xunion (not the field), the ordinal of the field
changes:

```fidl
// rename from MyXUnion to MyXUnion2
xunion MyXUnion2 {
    int32 i;  // ordinal now changes to 0x22222222 since the xunion was renamed. d'oh!
}

可以说是一种意外行为:更改 xunion 的名称并不会更改 ABI。

改进网站意味着两点:

  1. 我们即将修改 xunion RFC (RFC-0061),以便仅从字段名称中派生序数,并从序数哈希计算中移除 xunion 名称和库名称。
  2. 我们需要更改代码,遗憾的是,这意味着 xunion ABI 会发生更改,并且可能会导致构建错误。幸运的是,我们可以通过 Jeremy Manson 开创的用于为方法实现序数哈希的技术进行软转换:让客户端检查新旧哈希,直到更改完全遍历树。

(经验教训:将来,我们应该仔细查看序数哈希中包含的内容,以及更改这些内容是否应该更改 ABI。)

我们认为这是一个低风险计划,因为可以进行软转换,而 Jeremy 已成功完成方法序数。请发表评论,否则我们将很快开始开展这项工作。


  1. 奇怪,因为名称对消息(即位、枚举、结构体、表)的二进制传输格式没有影响,联合除外。因此,此情形与其他情形截然不同。