RFC-0061:可扩展联合

RFC-0061:可扩展联合
状态已接受
领域
  • FIDL
说明

为了提供更多方式来表达形状可能需要随时间演变的载荷,我们提议用可扩展的并集取代当今存在的并集。

作者
提交日期(年-月-日)2018-09-26
审核日期(年-月-日)2018-10-11

“夏威夷和阿拉斯加餐饮”

摘要

提供更多方式来表达形状可能需要演变的载荷 我们提议用取代目前存在的工会 可扩展联合的设计

设计初衷

如今,工会无法随着时间的推移而发展,我们甚至警告过 “一般来说,更改并集的定义将破坏二进制 兼容性。"

目前定义的很多联合体的可扩展性 例如 fuchsia.modular/TriggerCondition, 其中字段已废弃而不被移除,或 fuchsia.modular/Interaction.

所述, 还有许多联合,其当前表示法是 因为它们不太可能在近期发展。不过, 同时保留 static unionsextensible unions 引入了 不必要的复杂度,请参阅 优缺点

设计

如需引入可扩展联合,我们需要修改 FIDL 的多个部分: 语言和 fidlc、JSON IR、传输格式以及所有语言 绑定。 我们还需要在不同的地方对此新功能进行记录。 我们将逐一讨论各项更改。

语言

从语法上讲,可扩展并集与静态并集完全相同:

union MyExtensibleUnion {
    Type1 field1;
    Type2 field2;
     ...
    TypeN fieldN;
}

在后台,系统会为每个字段分配一个序数: 对的 序号,以及如何 方法'序数词 会自动获得分配

具体而言:

  • 序数的计算算法与方法序数相同详细信息), 我们将库名称串联起来, “.”、可扩展的联盟名称“/”以及成员名称 然后采用 SHA256 算法,并使用 0x7fffffff 进行遮盖。
  • 序数为 uint32没有两个字段可以声明相同的序数, 并禁止 0。 在发生顺序冲突的情况下,[Selector] 属性 用于提供备用名称(或重命名后的成员)。
  • 序数可以是稀疏的,也就是说,与表的工作方式不同,表需要 密集序数。
  • 在可扩展联合中,不允许使用可为 null 的字段
  • 可扩展联合必须至少有一个成员

可扩展并集可用于当前可使用并集的任何位置 语言。 尤其是:

  • 结构体、表和可扩展联合可以包含可扩展联合体;
  • 可扩展联合可以包含结构体、表和可扩展联合体;
  • 接口参数或返回值可以是可扩展的并集;
  • 可扩展联合可以为 null。

JSON IR

在下表中,我们将在每个联合字段声明中添加一个键 “ordinal”。

电汇格式

在电线上,可扩展并集由 区分各个选项(填充到 8 个字节),后跟 制作人熟知的各种成员的信封。 具体而言,即:

  • uint32 标记,其中包含要编码的成员的序号;
  • uint32 内边距,用于对齐 8 个字节;
  • 一个 uint32 num_bytes,用于存储信封中的字节数; 始终是 8 的倍数,如果信封为 null,则必须为 0;
  • uint32 num_handles,用于存储信封中的句柄数量, 如果信封为 null,则必须为 0;
  • uint64 data 指针,用于指示是否存在 离线数据:
    • 当信封为 null 时,值为 0
    • FIDL_ALLOC_PRESENT(或 UINTPTR_MAX) 和下一个行外对象;
  • 在解码以供使用时,此 data 指针要么是 nullptr 若信封为 null,则为指向信封的有效指针。
  • 信封会为 内容。

可为 null 的可扩展联合tag 为 0num_bytes 设置为 0num_handles 设置为 0,并且数据指针为 FIDL_ALLOC_ABSENT, 即0。 从本质上讲,null 可扩展联合是 24 个字节,即 0。

语言绑定

可扩展并集与并集类似,不同之处在于前者需要 处理“未知”错误,读取 union 时的情况。 理想情况下,大多数语言绑定会将

union Name { Type1 field1; ...; TypeN fieldN; };

就像它们是一个可扩展的并集一样,可以轻松地 对未知情况进行模数支持, 只有在可扩展并集时才有意义。

首先,我们建议不要通过任何语言绑定公开预留成员: 它们存在于 JSON IR 中以保持完整性,我们不希望 在语言绑定中公开这些标识符很有用。

实施策略

植入过程将分两步完成。

首先,我们将构建对可扩展联合体的支持:

  1. 通过使用不同语言(fidlc)来介绍该功能, 关键字 (xunion),用于区分静态联合体和可扩展式 并集。
  2. 实现各种核心语言绑定(C、C++、Rust、Go、Dart)。 相应地扩展兼容性测试和其他测试。

其次,我们将所有静态联合体迁移到可扩展联合体:

  1. 为静态联合生成序数,并将其放在 JSON IR 中。 后端最初应忽略这些内容。

  2. 在读取路径上,同时具有两种读取联合模式,就像它们是 静态并集,就好像它们是可扩展的并集(需要序数 )。 根据事务中的标志,在一种与另一种方法之间进行选择 消息标头。

  3. 更新写入路径以将联合编码为可扩展并集,并指明 通过在交易消息标头中设置该标志来尽可能多地处理。

  4. 当所有写入者都完成更新、部署和传播后, 静态联合处理和软转换的基架代码。

文档和示例

这至少需要在以下位置提供文件:

向后兼容性

可扩展联合与“静态”明确不兼容 并集。

性能

不使用时对性能没有影响。 在构建期间对性能的影响微乎其微。

安全

不影响安全性。

测试

编译器中的单元测试、在各种环境中编码/解码的单元测试 语言绑定和兼容性测试,用于检查各种语言 绑定在一起。

缺点、替代方案和未知问题

可扩展联合的效率低于不可扩展联合。 此外,不可扩展的并集无法通过其他方式表达 。 因此,我们提议同时使用这两种功能。

不过,我们可以决定只存在可扩展的并集, 而不再是当前定义的联合体。 这会违反紫红色各地区的规定,这些地方代表工会 对性能至关重要的消息, 期望值,例如fuchsia.io/NodeInfofuchsia.net/IpAddress

保持静态联合的优缺点

优点

  • 与并集相比,可扩展联合会产生 8 个字节的费用(对于 信封大小和手柄数量)。 此外,可扩展联合的数据始终离线存储 (即,数据指针需要额外的 8 个字节),而只有 nullable unions'数据离线存储
  • 由于联合的编码,无法使用 FIDL 中的其他基元。 因此,如果从语言、某些类别的邮件中 我们无法再以紧凑而高效的方式表示。
  • 在某些情况下,联合可表示为 但方式不同;但这属于例外,并非常态。 一个可以在不使用并集的情况下进行重写的例子是 fuchsia.net.stack/InterfaceAddressChangeEvent 仅用于 fuchsia.net.stack/InterfaceAddressChange 其中,InterfaceAddress 可以直接写入,并且具有 enum 以指明添加还是移除。

缺点

  • 同时保留静态并集和可扩展联合体会增加 编译器、JSON IR、所有后端以及编码/解码。 收益微乎其微:在 FIDL 的世界,应用大小差异是微乎其微的 编码最初并不是特别适合大小。 此外,可根据需要对可扩展联合进行解码。
  • 下面以收益微乎其微的方式为例, fuchsia.io/NodeInfo: <ph type="x-smartling-placeholder">
      </ph>
    • 目前,NodeInfo 有 6 个选项:服务(大小 1)、文件(大小 4)、 目录(大小 1)、竖线(大小 4)、vmofile(大小 24)、设备(大小 4)。
    • 因此,NodeInfo 的总大小始终为 32 个字节,即 代码 + max(选项大小)= 8 + 24 = 32。
    • 使用可扩展联合时,NodeInfo 的大小取决于选项 正在编码。 始终有 16 个字节的“tax”(对比 8),因此相应的尺寸 be:服务 = 24,文件 = 24,目录 = 24,竖线 = 24,vmofile = 40, 设备 = 24。
    • 因此,在任何情况下,我们都削减了 8 个字节, vmofile,其中要再添加一个 8 个字节。
  • 同时具有静态并集和可扩展的语言的复杂性 也是一个担忧 我们希望库作者在使用两种方法之间左右摇摆, 选择可扩展联合是一种更安全的长期选择,而且成本很低。

总而言之,我们决定用可扩展的联合体取代静态联合体。

标记与序数

我们使用 ordinal 表示分配给字段的内部数值, 即,通过哈希处理计算得出的值。 我们使用标记来表示绑定中变体的表示形式: 在 Go 中,可能是类型为 alias 的常量;在 Dart 中,这可能是 enum

fidlc 编译器只处理序数。 开发者很可能只处理代码。 绑定可提供从高级标记到低级标记的转换 内部序数。

无空的可扩展联合体

在设计阶段,我们考虑过让可扩展联合体为空。 不过,我们最终选择禁止这种行为:选择一个可为 null 的 明显具有单个变体(例如空结构体)的可扩展并集 对意图进行建模。 这样也避免了两个“单位”例如, null 值和一个空值。

先验技术和参考资料

  • 协议缓冲区具有 oneof
  • 除非在特殊情况下,否则 FlatBuffer 的联合不可扩展。