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:增加方法序数。
- 实际上全局唯一标识符可大大简化和加强监控和跟踪等需求,例如,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 位显式)序数语法之间的歧义。这可以通过以下操作来实现:
- 在 fidlc 中添加一项检查,以确保 32 位哈希序数的值始终不小于 N。例如,如果 N 为 512,则经过哈希处理的序数的十六进制值必须至少为 0x200。
- 小于 0x200 的哈希序数将导致编译错误,并且必须使用
[Selector=]
属性手动重命名字段名称。先将[Selector]
添加到相应字段中,然后再将此项更改提交到 fidlc。 - 鉴于现有哈希方案的随机性,我们预计哈希错误会发生接近零的次数,因此可能不需要手动解析。
- 添加此检查后,系统会有效地为显式序数分配
[0..N]
序数空间,并确保其不会与经过哈希处理的序数发生冲突。
- 小于 0x200 的哈希序数将导致编译错误,并且必须使用
- 当语言绑定解释序数值时:
- 如果序数介于
[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。
改进网站意味着两点:
- 我们即将修改 xunion RFC (RFC-0061),以便仅从字段名称中派生序数,并从序数哈希计算中移除 xunion 名称和库名称。
- 我们需要更改代码,遗憾的是,这意味着 xunion ABI 会发生更改,并且可能会导致构建错误。幸运的是,我们可以通过 Jeremy Manson 开创的用于为方法实现序数哈希的技术进行软转换:让客户端检查新旧哈希,直到更改完全遍历树。
(经验教训:将来,我们应该仔细查看序数哈希中包含的内容,以及更改这些内容是否应该更改 ABI。)
我们认为这是一个低风险计划,因为可以进行软转换,而 Jeremy 已成功完成方法序数。请发表评论,否则我们将很快开始开展这项工作。
-
奇怪,因为名称对消息(即位、枚举、结构体、表)的二进制传输格式没有影响,联合除外。因此,此情形与其他情形截然不同。 ↩