| RFC-0048:显式联合序号 | |
|---|---|
| 状态 | 已接受 |
| 区域 |
|
| 说明 | 为了更好地协调可扩展联合(或简称联合)的 ABI 影响,我们建议进行一些更改,使灵活联合在语法上更接近表,并修正当前围绕重命名联合或联合成员的奇怪 [2] 和过于严格的 ABI 限制。 |
| 作者 | |
| 提交日期(年-月-日) | 2019-08-25 |
| 审核日期(年-月-日) | 2019-09-26 |
摘要
为了更好地使 ABI 含义与可扩展联合(或简称为联合)保持一致,我们建议:
- 更改了变体成员的语法,要求使用显式序号(类似于表序号的要求)。
- 使用此显式序号,而不是之前实现的哈希序号。
- 最后,我们更改了有线格式,使联合序号为 64 位(而非 32 位)。
这些更改使灵活的联合在语法上更接近于表,并修正了当前在重命名联合或联合成员时出现的奇怪1 和过于严格的 ABI 限制。
动机和设计
除了联合之外,名称在类型方面不会对 ABI 产生影响:枚举、位、结构和表都可以重命名,或者其成员可以重命名,而无需担心二进制兼容性。由于我们选择使用基于哈希的技术来分配变体序号,因此联合与枚举不同(请参阅 RFC-0061:可扩展的联合)。
我们已意识到此缺点,并提出了解决此问题的方法:请参阅意向实现:xunion 序数更改,其中建议更改哈希处理方案,使其仅包含成员名称。
相反,此提案更进一步,仅使用显式序号,从而避免名称与 ABI 兼容性之间存在任何关联。我们认为在联合声明中编写序数的额外工作量很小:迄今为止,表中的显式序数尚未成为问题,并且有大量先例表明可以从其他热门 IDL 中对成员或变体进行编号。
此外,为了进一步与表格保持一致,我们要求序号从 1 开始按顺序分配,并允许使用关键字 reserved 来明确跳过联合变体。
仅针对协议进行哈希处理
与类型不同,协议使用基于哈希的方法来分配序号。这主要是出于以下两个关键应用场景的考虑:
- 协议可以组合,因此我们需要一个全局序号分配方案,以避免出现距离较远的问题。请参阅 [RFC-0063: OrdinalRange、RFC-0020:接口序号哈希和 RFC-0029:增加方法序号。
- 实际上,全局唯一标识符可极大地简化并增强监控和跟踪等需求,例如 fidlcat 依赖于唯一标识方法调用的能力。
这些使用情形不会转换为类型。
此提案的效果是,仅对协议使用哈希处理。只有一种哈希处理方案,即 RFC-0029 中所述的方案。
64 位序号是标准
目前,联合内联内容中有 4 个字节的填充:
- 序数 (
uint32) - 内边距 (
uint32) - 信封(16 字节)
相反,我们希望以格式显式调用所有字节,因此将序号更改为 64 位。一般来说,我们更喜欢无填充结构,因为它们更高效(例如,不需要显式内存设置或额外的编码表)。
如需了解如何软过渡到 64 位序号,请参阅实现策略部分。
JSON Wire Format
过去曾讨论过,当我们创建 JSON 有线格式时,重命名类型和成员会给该格式带来 ABI 影响。
按“有线格式”分别列出 ABI 破坏性变更非常有用。我们可以从不同的配置中获取不同的属性。需要在所有可能支持的线格式上实现 ABI 兼容的极少数消息在发展方面会受到很大限制。其他用户应尽可能受益于更灵活的规则。
接下来:稀疏表
有人讨论过支持稀疏表,即除了 struct 和 table 之外,还支持第三种记录类数据布局。如果我们决定引入第三个选项,则草案语法将遵循此提案和当前表格的语法:
sparse_table Example {
1: T1 field1;
2: reserved; // deprecated
3: T3 field3;
};
实施策略
为了实现平稳过渡,我们需要区分经典(32 位哈希)序号语法和提议的(64 位显式)序号语法。您可以通过以下方式实现此目的:
- 在 fidlc 中添加一项检查,以确保 32 位哈希序号的值永远不低于 N。例如,如果 N 为 512,则哈希序号的十六进制值必须至少为 0x200。
- 如果哈希序号小于 0x200,则会导致编译错误,并且必须使用
[Selector=]属性手动重命名字段名称。在将此更改落实到 fidlc 之前,我们会向相应字段添加[Selector]。 - 鉴于现有哈希方案的随机性,我们预计哈希错误发生的次数接近于零,因此可能无需手动解决。
- 添加此检查可有效地为显式序号分配
[0..N]序号空间,并确保它不会与哈希序号冲突。
- 如果哈希序号小于 0x200,则会导致编译错误,并且必须使用
- 当语言绑定解释序数值时:
- 如果序号介于
[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。
为了改进这一点,我们需要做到以下两点:
- 我们希望修改 xunion RFC (RFC-0061),以便仅从字段名称派生序号,并从序号哈希计算中移除 xunion 名称和库名称。
- 我们需要更改代码,但遗憾的是,这意味着 xunion ABI 会发生变化,并可能导致构建失败。幸运的是,我们可以通过 Jeremy Manson 首创的技术来实现软过渡,该技术可为方法实现序数哈希:让客户端同时检查旧哈希和新哈希,直到更改完全在树中生效。
(我们从中吸取了一个教训:未来,我们应该仔细查看序号哈希中包含的内容,以及更改这些内容是否会更改 ABI。)
我们认为,鉴于可以进行软过渡,并且 Jeremy 已成功为方法序号执行了此操作,因此该计划的风险较低。请发表评论,否则我们很快就会开始处理此问题。
-
之所以说奇怪,是因为名称对消息(即位、枚举、结构、表)的二进制有线格式没有影响,联合除外。因此,这是一个与众不同的案例。 ↩