RFC-0048:显式联合序数

RFC-0048:显式联合基数
状态已接受
区域
  • FIDL
说明

为了更好地统一可扩展联合体(或简称联合体)的 ABI 影响,我们建议进行一些更改,使灵活联合体在语法上更接近表,并更正目前围绕重命名联合体或联合体成员的奇怪 [2] 且过于严格的 ABI 限制。

作者
提交日期(年-月-日)2019-08-25
审核日期(年-月-日)2019-09-26

摘要

为了更好地统一可扩展联合体(或简称联合体)的 ABI 影响,我们建议:

  1. 更改了变体成员的语法,以要求使用显式序数(类似于表序数的使用方式)。
  2. 请使用此显式序数,而不是之前实现的经过哈希处理的序数。
  3. 最后,我们更改了线格格式,使联合基数为 64 位(而非 32 位)。

这些更改使灵活联合体在语法上更接近表,并更正了目前在重命名联合体或联合体成员时存在的奇怪1和过于严格的 ABI 限制

动机和设计

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

我们已认识到这一缺点,并提出了解决方法:请参阅Intent to implement: xunion ordinals change,其中建议将哈希方案更改为仅包含成员名称。

相反,此提案更进一步,只使用显式序数,从而避免名称与 ABI 兼容性之间存在任何关联。我们认为,在联合体声明中编写序数的额外工作量非常小:到目前为止,表格中的显式序数一直没有问题,而且在其他流行 IDL 中为成员或变体编号的先例非常多。

此外,为了进一步与表格保持一致,我们要求从 1 开始顺序分配序数,并允许使用关键字 reserved 显式跳过联合变体。

仅对协议进行哈希处理

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

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

此提案的效果是,哈希仅用于协议。只有一种哈希方案,即 RFC-0029 中所述的方案。

64 位序数是标准

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

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

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

如需了解如何向 64 位序数进行软过渡,请参阅实现策略部分。

JSON 线格式

我们之前曾讨论过,在创建 JSON 线格格式时,重命名类型和成员会对该格式中的 ABI 产生影响。

按“线格格式”分隔 ABI 故障非常有用。我们可以从不同的媒体资源中获取不同的房源。极少数需要在支持的所有可能的线程格式上与 ABI 兼容的消息在演变方式上会受到很大限制。其他用户应尽可能受益于更灵活的规则。

后续内容:稀疏表

我们曾讨论过支持稀疏表,即除了结构体之外,为记录类数据提供第三种布局。如果我们决定引入第三种选项,基准语法将遵循此提案和当前表的语法:

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() 语句的 codegen 更高效,因此此方案比经过哈希处理的序数更高效。

安全

无影响。

测试

与往常一样,没什么大不了的。

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

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

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

经过进一步思考,我们认为上述做法还不够,因为语法优势(源代码中没有序数)无法弥补以下缺点:

  • 两种哈希方案,这使得理解 ABI 影响更难;
  • 在重命名声明或成员时,使联合与其同级枚举、位、结构体和表区分开来。

在先技术和参考文档

没有特别相关的。


Footnote1

本文档中的“联合体”是指可扩展联合体,而不是即将弃用的“静态”联合体。

Footnote3

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

FIDL 开发者,您好!在昨天编写测试时,FIDL 团队发现了当前 xunion 规范和实现的意外行为。如果声明如下:

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. 奇怪之处在于,除了联合体之外,名称对消息的二进制线格格式(即位、枚举、结构体、表)没有影响。因此,此支持请求与其他支持请求相比显得与众不同。