RFC-0020:接口序哈希 | |
---|---|
状态 | 已接受 |
领域 |
|
说明 | 我们提议让程序员不再为接口方法手动指定序数。编译器会根据完全限定方法名称(即库名称、接口名称和方法名称)的哈希值生成序数。 |
作者 | |
提交日期(年-月-日) | 2018-10-26 |
审核日期(年-月-日) | 2018-11-29 |
“60% 的情况回答了面试问题”
摘要
我们提议取消程序员手动修改
为接口方法 1 指定序数。
相反,编译器会根据
完全限定的方法名称,即库名称、接口名称和方法
名称。方法重命名将通过新的 Selector
属性与 ABI 兼容
(请参阅下文)。
我们明确限制此 FTP,以建议接口进行序数哈希处理 ;非枚举、表和可扩展联合体。 我们认为,这些结构的应用场景大相径庭, 需要进一步调查,并使用其他 FTP。
示例
目前,FIDL 作者会写:
library foo;
interface Science {
1: Hypothesize();
2: Investigate();
3: Explode();
4: Reproduce();
};
此 FTP 会删除序数索引:
interface Science {
Hypothesize(); // look, no ordinals!
Investigate();
Explode();
Reproduce();
};
在后台,编译器会有效地生成类似于 :
interface Science {
// ordinal = SHA-256 of the fully-qualified method name,
// i.e. "foo.Science/MethodName", truncated to 32 bits
0xf0b6ede8: Hypothesize();
0x1c50e6df: Investigate();
0xff408f25: Explode();
0x0c2a400e: Reproduce();
};
设计初衷
- 手动指定序数在很大程度上是一项机械操作。 如果您完全不需要考虑编写接口, 。
- 如果使用合适的哈希,则极不可能产生 序数冲突,这是相对于人类手动书写的 序数(尤其是在使用接口继承的情况下)。请参阅 下方的“序数冲突”部分 。
- 目前,程序员必须确保不同方法的序数
避免冲突这对包含很少方法的接口来说很容易,
接口有许多方法,这可能非常重要。
对于序数,有不同的编码风格和思路
这会导致编码风格不一致。
- 大多数接口从 1 开始,并一直向上。
- 不过,有些开发者更喜欢将不同的接口方法分组到一起, 范围内(例如,1-10、100-110 等)。
- 删除手动编号的序数也会消除这种不一致问题 样式,因此作者无需再决定 要使用的样式。
- 接口继承可能会导致顺序发生意外冲突。
到目前为止,我们已尝试了两次解决此问题:
<ph type="x-smartling-placeholder">
- </ph>
- FTP-010(已拒绝)提议了
OrdinalRange
属性, 界面 继承的可预测性更高;已被拒绝。 FragileBase
2 是当前的临时方案, 但无法解决核心问题,那就是确保序数 。- 如果序数经过哈希处理,且使用了接口和库名称 计算哈希值,对序数进行哈希处理不会导致冲突 序数,从而解决接口继承问题(在外部 极其罕见的哈希冲突)。
- FTP-010(已拒绝)提议了
设计
哈希
经过哈希处理的序数由 SHA-256 哈希:
library name (encoded as UTF-8; no trailing \0)
".", ASCII 0x2e
interface name (encoded as UTF-8; no trailing \0)
"/", ASCII 0x2f
method name (encoded as UTF-8; no trailing \0)
例如,以下 FIDL 声明:
library foo;
interface Science {
Hypothesize();
Investigate();
Explode();
Reproduce();
};
将具有以下字节模式,用于计算序数 哈希:
foo.Science/Hypothesize
foo.Science/Investigate
foo.Science/Explode
foo.Science/Reproduce
使用 .
和 /
分隔符,因为 fidlc
已输出
完全限定的方法名称,采用此格式 (c.f.
fidlc
的 NameName() 方法)。
计算 SHA-256 哈希值后:
- 提取 SHA-256 哈希的高 32 位(例如,
echo -n foo.Science.Hypothesize | shasum -a 256 | head -c8
) - 最高位设为 0,从而得到有效的 31 位哈希 从零填充到 32 位的值。(自 FIDL 有线格式预留了 32 位 ordinal.)
在伪代码中:
full_hash = sha256(library_name + "." + interface_name + "/" + method_name)
ordinal = full_hash[0] |
full_hash[1] << 8 |
full_hash[2] << 16 |
full_hash[3] << 24;
ordinal &= 0x7fffffff;
选择器属性和方法重命名
我们定义编译器要使用的 Selector
属性
计算经过哈希处理的序数,而不是使用方法名称。
如果方法名称没有 Selector
属性,则该方法
名称将用作 Selector
。(接口和库)
名称在哈希计算中仍然使用。)
Selector
可用于在不破坏 ABI 的情况下重命名方法
这也是手动指定的
序数。例如,如果要重命名 Investigate
方法添加到 Science
接口中的 Experiment
,我们可以编写:
interface Science {
[Selector="Investigate"] Experiment();
};
我们只允许在方法中使用 Selector
属性。正在重命名
库被视为罕见库,并且保留 ABI 兼容性
在这种情况下不是高优先级。对于
对接口进行重命名此外,
具有 Discoverable
属性的重命名的接口如下所示:
令人困惑:可发现的名称是什么?
序数冲突和冲突解决
如果经过哈希处理的序数导致与其他序数发生冲突或冲突,
经过哈希处理的序数,则编译器会发出一个
并依靠人工指定
Selector
属性
以解决冲突 3。
例如,如果方法名称 Hypothesize
与
方法名称为 Investigate
时,我们可以将 Selector
添加到
Hypothesize
,以避免冲突:
interface Science {
[Selector="Hypothesize_"] Hypothesize();
Investigate(); // should no longer conflict with Hypothesize_
};
我们将更新
FIDL API 评分准则
建议在后面附加“_”添加到 Selector
的方法名称
来解决冲突。fidlc
也会提供此修正建议。
请注意,序数只需在每个接口上是唯一的, 类似于手动指定的序数。如果我们要让序数 在所有接口中是唯一的,应该在另一个 FTP。
根据信包后方的计算结果显示,31 位和 100 个方法,发生碰撞的可能性为 0 .0003%, 因此我们预计哈希冲突极为罕见。
选择器自行车架
还有其他关于“Selector
”的建议:
WireName
(abarth
)OriginalName
(ctiller
)Salt
(abarth
;略有不同,因为它建议添加编译器指定的 而不是别名)OrdinalName
我们之所以选择Selector
,是因为我们认为它能更贴切地反映
intent 属性。
我们选择让程序员指定序数名称, 比序索引多,原因有多种:
- 要求索引就会更繁琐(如复制和粘贴) 原始 SHA-256 哈希值的序列),
- 指定序号名称可启用与 ABI 兼容的方法重命名, 和
- 而指定名称而不是索引 与编程人员编写代码时一样 而不是降低一个抽象层 需要考虑序数。
零序数
零是无效序数。如果某种方法
名称哈希值为零,则编译器会将其视为哈希冲突
并要求用户指定一个不进行哈希处理的 Selector
零。
我们考虑让 fidlc
按照
确定性地对其进行转换,但感觉:
- 任何此类算法都不明显;
- 零大小写的情况极其罕见,
因此这种方法并不意味着 工效学设计和编译器实现。
事件
此 FTP 还包含一些事件,这些事件被视为 方法(详见 FIDL 语言文档 4)。
编译器和绑定变更
我们认为只需修改 fidlc
即可支持
序数哈希;代码生成后端不需要
。这是因为,fidlc
会计算序数,
将其在 JSON IR 中发送到后端。
绑定无需更改。
实施策略
我们打算分阶段实现此目标:
- 向 fidlc 添加代码以计算哈希值。
- 添加了对库属性的支持。
- 将意向转变为紫红色 eng,让他们意识到潜力 问题。 a.提议在某个特定日期弃用手动序数, 则表示下一步已完成。
- 在同一 CL 中:
a.修改 FIDL 语法的接口方法规则,将序数指定为可选项;
详见下文。
b.忽略手动指定的序数,并将经过哈希处理的序数用于
传递给代码生成后端的序号名称。
c.通过添加
Selector
来手动修复任何现有的哈希冲突 属性。 - 请在两周内测试更改,以确保不存在生产问题。 a.在这段时间内编写的新 FIDL 接口不应使用序数。 b.手动序数被视为已弃用,但 fidlc 不会发出 关于此问题的警告 c.与团队合作,确保 中 没有 手动指定的序号保留 界面。 d.两周后,更新 FIDL 格式设置工具, 序数,并将其批量应用到整个紫红色的树。
- 取消了对手动指定的序数的支持。
以上是
软过渡;
将 fidlc
更改为使用经过哈希处理的序数(第 4b 步)应该不会破坏
Rollers,因为 Roller 是基于整个树的单个版本构建的。
在 jeremymanson@google.com 实施这个 FTP 的步骤中, 他选择使用手动指定的序数而非哈希序数, 这与上述第 4b 步有所不同。这样所有现有接口 使用手动指定的序数 ABI 兼容,并且仅使用 未指定序数时经过哈希处理的序数。
工效学设计
优点:
- 编写接口应该更简单。
缺点:
- 程序员需要了解一个新属性
Selector
,它应该 有两个用途:重命名和解决冲突。 - 更改方法名称可能并不明显会破坏 ABI
兼容性,而程序员指定的序数则不同。
用户指导(例如改进文档)可以解决此问题。
- 请注意,其他组件系统(如 COM 和 Objective-C) 使用接口方法时,通常会破坏 ABI 兼容性 已重命名。 使用了类似的系统。
- 无法手动控制序数可能会导致 异常情况下的可调试性,例如有多个 FIDL 接口 用于同一个 Zircon 频道上。
请注意,作者创建此 FTP 主要是出于工效学方面的考虑。
文档和示例
我们预计会更改 FIDL 属性、语法、语言和
线上格式文档。API 可读性评分准则文档也应该更新
如 Selector
部分中所述。
向后兼容性
- 经过哈希处理的序数与手动指定的序数不兼容 ABI。
我们认为这不是问题,因为
<ph type="x-smartling-placeholder">
- </ph>
fidlc
更改是二进制的(经过哈希处理的 x 或手动序数会 使用),以及fidlc
用于构建整个树,因此- 树的所有部分将始终使用所选的序数方案。
- 经过哈希处理的序数与 API(源)兼容。 现有的源文件将保持兼容;手动序数将被 已弃用(请参阅“实现策略”)。
- 如果两个不同的编译版本(即两个不同的软件版本, 使用平台源代码树的 build),而 FIDL 接口 用于在机器间通信作者目前没有任何用途 所以这应该不是问题
性能
我们预计 fidlc
的运行速度可以忽略不计,因为它现在必须对所有方法进行哈希处理
用于计算它们。
我们预计不会对运行时性能产生显著影响。 编译器可能已为手动指定的序数生成跳转表 以前是较小的连续搜索,现在变成二元搜索 通过稀疏序数空间进行微调。 同一机制也可能会以微不足道的方式影响二进制文件的大小。 (表驱动的调度很可能会改善大小和速度方面的问题。)
安全
我们预计不会出现运行时安全问题,因为序数哈希没有运行时 除了更改通过网络发送的序数值以外。
使用加密哈希 (SHA-256) 可能会导致一些人认为该哈希需要 安全系数高;我们认为不存在安全问题,原因如下:
- FIDL 编译器将在编译时检查是否存在哈希冲突,并要求 人工输入的内容来解决这类问题;
- 我们不将 SHA-256 用于加密目的 这种可能性极小可能会导致碰撞 CRC-32(甚至 strlen())也可以使用,但可能会导致 这样会带来极大的不便
SHA-256 哈希的截断可能也会引起一些问题,但同样,我们并不 认为存在安全问题,因为 FIDL 编译器会静态检查 哈希冲突5。
测试
ianloic@google.com 在分析了现有的 FIDL 接口后,确定 以保证没有哈希冲突。
我们会仔细考虑如何测试实际哈希冲突的情况, 利用有效哈希人工生成哈希冲突是一项非常困难(从设计上来讲)。
否则,您需要使用单元测试、CQ 测试、 兼容性测试 和手动测试应该足以确保顺序哈希的可靠性。
缺点、替代方案和未知问题
此 FTP 有意仅处理接口的序数哈希。 它不会提议更改枚举的手动枚举序数, 或可扩展的联合。
完美哈希技术由 jeffbrown@google.com 提出,并被采纳。 FTP 作者不太熟悉完美的哈希方案,但认为 随着时间的推移,添加额外的方法会改变现有方法的哈希值, 方法,从而破坏 ABI 兼容性,导致不适合进行完美的哈希处理。 虽然或许也能进行动态完美哈希,但也会引发 而且不太为人熟知,也比标准 因此无需进一步调查。
移除手动序数的另一种方法是发送完整的方法名称 这在许多(大多数?)其他 RPC 系统(请参阅 参考文档)。这会对运行时性能产生影响 可能会与 FIDL 的预期应用场景相冲突。
我们考虑过能指定所使用的哈希值,以便日后进行更改,
(如果 SHA-256 最终出现另一个哈希可以解决的问题)。
这种设计在安全应用中很常见,其中广泛使用的加密技术
之后会发现哈希存在漏洞。
不过,指定哈希值可能需要更改传输格式,
并要求所有语言绑定实现代码,以便选择哈希算法,
使编译器和绑定代码大幅复杂化。
我们认为这种取舍不值得。
我们认识到,
git
也对 SHA-1 持这种态度,
现在在一定程度上回溯决策过程,但我们认为我们的用例
差异足以证明对哈希算法进行硬编码的合理性。
探索
- 利用节省空间的方法识别方法可以提高效率
方法的一级表示法,使方法成为第一类。
- 例如,这可允许方法在 FIDL 调用中用作参数,或者 FIDL 方法会返回另一个方法。 可以说,目前已经存在此方法的用例,其中方法会返回 一个接口,其中包含一个方法作为返回实际方法的代理。
- 所提议的 31 位哈希可以扩展为,例如64/128/53 位; SHA-256 提供了许多位。
- 将
ordinal
重命名为selector
,这是一个现有概念,提供 在其他语言和组件系统中的用途。 - 可能有必要区分方法名称和接口名称, 拥有两个不同的数据 这样,可以唯一地引用接口名称和方法名称。 为此,我们可能需要超过 32 位。
- 如上所述,枚举、表和可扩展联合并不在范围内。
尽管如此,我们确实认为此 FTP 适用于他们。
初步想法:
<ph type="x-smartling-placeholder">
- </ph>
- 我们不确定枚举是否需要此功能。 使用更简单、标准化的连续整数编号似乎就足够了。
- 这可以按原样应用于可扩展联合体。
- 表需要不同的传输格式才能采用序数哈希,因为 由于采用打包表示法,序数目前需要保持连续。
- FIDL 目前保留了序数最高位,并明确指出
一定范围的最高位用于控制流等。
作者认为造成这一现象的其中一个原因也可能与
和序数冲突。
我们要再回顾一下这个问题吗?
- 将序数空间扩展至 64 位(如上所述)可在很大程度上解决 这个。
abarth@google.com
在 Fuchsia IPC 聊天室中建议仅限预订0xFFFFxxxx
。
- 我们可以将方法的参数类型包含在计算哈希值中,
我们日后可能会支持方法重载。
- jeffbrown@google.com 提到,对完整的方法签名进行哈希处理可能会限制 接口扩展的机会,并且地图过载会导致 扩展到许多编程语言。
- 由于序数哈希应解决接口继承时序数冲突的问题
FragileBase
属性,也可以移除。- 代码搜索会显示
FragileBase
的大约 9 种用法。
- 代码搜索会显示
- 作者们担心,一个已经大幅演变的界面
如果许多方法具有
Selector
属性,则可能难以读取。- 解决此问题的一种方法是采用 Objective-C 类别 或 C# 部分类, 其中已存在的已声明接口可以“扩展”具有属性 添加到单独的声明中
先验技术和参考资料
有趣的是,我们不知道有没有其他任何方法调度或 RPC 系统 使用方法名称的哈希值来标识要调用的方法。
大多数 RPC 系统按名称(例如 gRPC/Protobuf 服务、Thrift、D-Bus)调用方法。 对于进程内方法调用,Objective-C 使用保证唯一的 char* 指针 值(称为选择器)来确定应对类调用的方法。 Objective-C 运行时可以将选择器映射到字符串化的方法名称,反之亦然。 对于进程外方法调用,Objective-C 分布式对象会使用方法 为调用指定名称。 COM 直接使用 C++ vtable 进行进程内调用,因此依赖于 介绍了 ABI 和编译器支持,以支持方法调度。 apang@google.com 建议对 ctiller@google.com 中的表使用序数哈希 Phickle 提案。ianloic@google.com 和 apang@google.com 在 2018 年 10 月 4 日(星期四)会面 写在白板上
-
Mojo/FIDL1 也不要求程序员指定序数; 而是按顺序生成(类似于 FlatBuffers 的 表字段的隐式标记编号)。 ↩
-
以前,您可以创建一个继承自 其他任何 FIDL 接口。 不过,接口和父接口具有相同的序数空间, 也就是说,如果您向接口添加方法, 子接口。 我们正围绕 FIDL-land 提出几项提案,以解决 但直到我们弄清楚 我们已经将接口的默认设置改为 禁止继承。 接口仍然可以选择允许使用
[FragileBase]
属性。 如果您遇到此问题,编译器应该会输出错误消息, 并附上简要说明 我 (abarth@google.com) 添加了[FragileBase]
属性 在平台源代码树中,所有使用 FIDL 接口继承的地方 (但愿如此!)。 如果您有任何疑问或遇到任何问题,请与我联系。 --abarth@google.com ↩ -
我们相信不存在足够的顺序冲突 自动化技术额外增加的实施和认知复杂性 冲突解决。我们可以重新考虑此决定,而不中断 如果数据表明顺序冲突变得 问题。 ↩
-
如果只声明结果,该方法就称为事件。 然后定义来自服务器的垃圾消息。 ↩
-
jln@google.com 写道:“是的,你可以截断 SHA-2,也可以。 在哪里截断也无关紧要。” ↩