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:可扩展的联合)。

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

相反,此提案更进一步,仅使用显式序号,从而避免名称与 ABI 兼容性之间存在任何关联。我们认为在联合声明中编写序数的额外工作量很小:迄今为止,表中的显式序数尚未成为问题,并且有大量先例表明可以从其他热门 IDL 中对成员或变体进行编号。

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

仅针对协议进行哈希处理

与类型不同,协议使用基于哈希的方法来分配序号。这主要是出于以下两个关键应用场景的考虑:

  • 协议可以组合,因此我们需要一个全局序号分配方案,以避免出现距离较远的问题。请参阅 [RFC-0063: OrdinalRangeRFC-0020:接口序号哈希RFC-0029:增加方法序号
  • 实际上,全局唯一标识符可极大地简化并增强监控和跟踪等需求,例如 fidlcat 依赖于唯一标识方法调用的能力。

这些使用情形不会转换为类型。

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

64 位序号是标准

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

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

相反,我们希望以格式显式调用所有字节,因此将序号更改为 64 位。一般来说,我们更喜欢无填充结构,因为它们更高效(例如,不需要显式内存设置或额外的编码表)。

如需了解如何软过渡到 64 位序号,请参阅实现策略部分。

JSON Wire Format

过去曾讨论过,当我们创建 JSON 有线格式时,重命名类型和成员会给该格式带来 ABI 影响。

按“有线格式”分别列出 ABI 破坏性变更非常有用。我们可以从不同的配置中获取不同的属性。需要在所有可能支持的线格式上实现 ABI 兼容的极少数消息在发展方面会受到很大限制。其他用户应尽可能受益于更灵活的规则。

接下来:稀疏表

有人讨论过支持稀疏表,即除了 structtable 之外,还支持第三种记录类数据布局。如果我们决定引入第三个选项,则草案语法将遵循此提案和当前表格的语法:

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

实施策略

为了实现平稳过渡,我们需要区分经典(32 位哈希)序号语法和提议的(64 位显式)序号语法。您可以通过以下方式实现此目的:

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

工效学设计

以极低的语法成本(即使用显式序号)大幅简化了 ABI 人体工程学。

文档和示例

至少:

向后兼容性

未采用显式序号语法的联合将继续使用现有的 32 位哈希序号方案。因此,当前存在的联合将继续保持 API 和 ABI 兼容性。

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

性能

极小的改进:由于 switch() 语句的代码生成效果更好,此方案比哈希序号更高效。

安全

无影响。

测试

与往常一样,微不足道。

缺点、替代方案和未知因素

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

请参阅实现意向:xunion 序号更改

经过进一步考虑,我们认为上述方法不够,因为语法优势(来源中没有序数)无法弥补以下不足:

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

在先技术和参考资料

没有特别相关的。


Footnote1

本文档中的“联合”是指可扩展的联合,而不是即将结束生命周期的“静态”联合。

Footnote3

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

Hi FIDLers!昨天编写测试时,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. 之所以说奇怪,是因为名称对消息(即位、枚举、结构、表)的二进制有线格式没有影响,联合除外。因此,这是一个与众不同的案例。