RFC-0048:显式联合基数 | |
---|---|
状态 | 已接受 |
区域 |
|
说明 | 为了更好地统一可扩展联合体(或简称联合体)的 ABI 影响,我们建议进行一些更改,使灵活联合体在语法上更接近表,并更正目前围绕重命名联合体或联合体成员的奇怪 [2] 且过于严格的 ABI 限制。 |
作者 | |
提交日期(年-月-日) | 2019-08-25 |
审核日期(年-月-日) | 2019-09-26 |
摘要
为了更好地统一可扩展联合体(或简称联合体)的 ABI 影响,我们建议:
- 更改了变体成员的语法,以要求使用显式序数(类似于表序数的使用方式)。
- 请使用此显式序数,而不是之前实现的经过哈希处理的序数。
- 最后,我们更改了线格格式,使联合基数为 64 位(而非 32 位)。
这些更改使灵活联合体在语法上更接近表,并更正了目前在重命名联合体或联合体成员时存在的奇怪1和过于严格的 ABI 限制。
动机和设计
除了联合体之外,对于类型,名称不会对 ABI 产生影响:枚举、位、结构体和表都可以重命名,或重命名其成员,而无需担心二进制兼容性。由于我们选择使用基于哈希的技术来分配变体序数,因此联合体有所不同(请参阅 RFC-0061:可扩展的联合体)。
我们已认识到这一缺点,并提出了解决方法:请参阅Intent to implement: xunion ordinals change,其中建议将哈希方案更改为仅包含成员名称。
相反,此提案更进一步,只使用显式序数,从而避免名称与 ABI 兼容性之间存在任何关联。我们认为,在联合体声明中编写序数的额外工作量非常小:到目前为止,表格中的显式序数一直没有问题,而且在其他流行 IDL 中为成员或变体编号的先例非常多。
此外,为了进一步与表格保持一致,我们要求从 1 开始顺序分配序数,并允许使用关键字 reserved
显式跳过联合变体。
仅对协议进行哈希处理
与类型不同,协议使用基于哈希的方法分配序数。这源于以下两个关键用例:
- 协议可以组合,因此我们需要一个全局序数分配方案,以避免远程问题导致中断。请参阅 [RFC-0063:OrdinalRange、RFC-0020:接口序列号哈希和 RFC-0029:递增方法序列号。
- 实际上,全局唯一标识符极大地简化并增强了监控和跟踪等需求,例如 fidlcat 依赖于能够唯一标识方法调用。
这些用例不会转换为类型。
此提案的效果是,哈希仅用于协议。只有一种哈希方案,即 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 位显式)序数语法之间进行区分。这可通过以下操作实现:
- 在 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() 语句的 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。
改进这方面的做法有两种方式:
- 我们想修改 xunion RFC (RFC-0061),以便将序数仅从字段名称派生出来,从序数哈希计算中移除 xunion 名称和库名称。
- 我们需要更改代码,遗憾的是,这意味着 xunion ABI 会发生变化,并可能导致构建失败。幸运的是,我们可以通过 Jeremy Manson 为实现方法的序数哈希而开创的技术来实现平滑过渡:让客户端同时检查旧哈希和新哈希,直到更改完全在树中传播。
(教训:今后,我们应仔细查看序数哈希中包含的内容,以及更改这些内容是否会更改 ABI。)
我们认为,由于可以进行软过渡,并且 Jeremy 已成功为方法序数执行此操作,因此这是一个低风险的方案。请提供您的意见,否则我们将尽快开始处理。
-
奇怪之处在于,除了联合体之外,名称对消息的二进制线格格式(即位、枚举、结构体、表)没有影响。因此,此支持请求与其他支持请求相比显得与众不同。 ↩