#pointers #bits #tagged-pointers #intrusive #tagged #bit

无 std 指针

你把高比特位偷走后的指针叫什么?指针!

2 个稳定版本

4.0.1 2023年8月18日
4.0.0 2023年2月16日
3.0.1 2021年7月3日
2.0.0 2021年6月21日
1.0.0 2021年6月20日

#426 in Rust 模式

Apache-2.0 WITH LLVM-exception

27KB
210

License Package Documentation

指针

你把高比特位偷走后的指针叫什么?指针!

指针是一个库,用于表示某些比特位被偷走的指针,以便程序员可以将它们用于其他目的。实际上,它是一小部分免费存储空间。

完全支持无 std,无依赖,<100loc。

比特源

指针支持一些比特源。何时安全使用它们由你决定。

对齐比特(常量参数 A)

如果我们知道指针的地址将始终对齐到 A 字节,其中 A > 1,我们可以偷走 log2(A-1) 字节。对于一个 8 字节对齐的值,这提供了适度的 3 比特。

如果你有更大宽度对齐的值,你可以得到更多。在并行编程中,通过增加对齐要求来填充数据到缓存行以消除假共享是很常见的。由于预取,amd64 或 aarch64 上的缓存行实际上相当于 128 字节,因此你可以回收令人尊敬的 7 个额外比特。

如果你的数据对齐得更宽,那么天空就是极限,但如果你有 16MB 对齐的数据,你可以得到惊人的 24 比特!请记住,Rust 所知的最对齐方式是类型声明的对齐,因此你必须创建一个新的类型包装器,以充分利用大对齐大小。

比特 最小对齐
1 2b
2 4b
3 8b
4 16b
5 32b
6 64b
7 128b
8 256b
9 512b
10 1k
11 2k
12 4k
13 8k
14 16k
15 32k
16 64k
17 128k
18 256k
19 512k
20 1m
21 2m
22 4m
23 8m
24 16m

从对齐中偷比特相对无害,但我们只能通过今天的 Rust 开发模式让编译器为你检查。

符号位(参数 S)

最常用的操作系统将内存排列得高半部分为内核保留,低半部分分配给用户空间。

将其视为有符号整数,这使得地址空间的高半部分为负,低半部分为正。

大多数程序不处理内核地址,这为我们提供了额外的位来操作。

如果我们知道我们不会处理用户空间地址,我们也可以在内核模式下获取这个额外的位。我们通过取栈上一个值的指针并偷取它的符号位来实现这一点。

如果您知道您将在内核空间处理用户空间地址或在用户空间处理内核空间地址,或者您使用或实现了一个不遵循此约定的内核,则必须将S设置为false

S位目前仅在用户空间的用户空间指针上进行了测试。虽然我们认为它应该更通用,但我们目前还没有其他场景的测试平台,因此我们不能保证它确实如此。

未使用的虚拟地址空间(V)

在64位模式下,地址空间很丰富:没有人有64位的RAM,即使他们有,他们的处理器也无法全部访问。除非为64位目标编译,否则V必须为0。

可以通过这种方法安全窃取的位数取决于所涉及的微架构和页表深度。

对于x86-64和aarch64,以下为适用大小

比特 页表深度 支持
25 3 aarch64仅支持,不常见,需选择加入
16 4 最常见默认
7 5 一些英特尔产品,不常见,需选择加入

如果您有钱并且需要超过128TB的虚拟地址空间,您应该将V限制为7位。同样,如果您知道您将在3层页表中,您可以窃取惊人的25位。但您可能通常限于16位。

黑客技术

欢迎贡献,请友善。

测试套件目前需要std(因为rand的ThreadRng)。在我的Ryzen 3900X上大约需要36秒。如果您在较慢的机器上,您可能想减少循环迭代次数。实际上,只需要一次就可以排除大多数错误,而 handful次就可以排除剩余的错误。一百万次迭代只是为了确保rayon的不可思议的易用性可以方便地启用,这肯定有些过度。

测试很全面。然而,我目前测试的配置数量有限

  • x86-64,Linux带有4PT

我们希望支持更多。这些应该是容易的

  • x86,Linux带有4PT应通过github actions实现
  • aarch64,Linux带有4PT不应太难找到访问权限。
  • 32位ARM(3PT)也应该可行。

这些可能更难

  • aarch64,Linux带有3PT可能不太广泛部署
  • 英特尔的新5PT具有极窄的兴趣范围 - 如果您希望我们测试它,您将不得不赞助它,因为我没有访问硬件。

变更日志

v4.0.1

  • 使用sptr功能提供可选的指针来源支持(感谢@GoldsteinE!)

v4.0.0

安全(请尽快升级,我已经下架了旧版本)

  • 修复使用Ox/NotNull窃取对齐位时的可能UB。

功能

  • 为所有类型添加方法new_stealing,允许在窃取数据的同时创建。

v3.0.1

  • 添加i_know_what_im_doing功能,在不是为64位目标构建时启用从V中窃取。

v3.0.0

  • 即使T不是,也使Ointer<T>NotNull<T>成为Clone + Copy

v2.0.0

新API

  • pack() - 将指针打包到usize的低位。
  • unpack() - 打包的反向操作。
  • asv_mask() - 计算一个掩码,其中被窃取的位由a, s, v设置。
  • mask() - 计算一个掩码,其中被窃取的位由总位设置。
  • Ointer::raw() - 返回ointer(窃取 + ptr)中的原始数据作为usize。
  • NotNull::raw() - 同上,但适用于NotNull

变更

  • Ointer现在内部使用usize。

版权(c)2021 James Laver,ointers贡献者。

许可根据Apache许可证第2版(《https://apache.ac.cn/licenses/LICENSE-2.0》)以及LLVM异常(《https://spdx.org/licenses/LLVM-exception.html》)。

除非您明确声明,否则您根据Apache-2.0许可证定义的贡献,将按照上述方式许可,不附加任何额外条款或条件。

依赖项