1个不稳定版本
0.1.0 | 2024年3月22日 |
---|
#2130 在 编码
36KB
377 行
💬 Bunk.
快速高效的可读数据编码!
Bunk 将二进制数据编码成可发音的乱码,类似于拉丁文。当需要将加密密钥等二进制数据手动传递给最终用户时,这很有用。
使用默认设置,32字节的字符串将被编码为
atemorni telphocom neideu gepypi forzamar oasal cevanal butthepo aujoate turviy menkais
可选地,Bunk 可以在编码字符串中添加逗号、句点和句子大小写,以提高可读性
Atemorni telphocom. Neideu gepypi forzamar oasal cevanal butthepo aujoate turviy, menkais.
📚 文档
阅读文档 这里。
📋 概述
- 它很快!在我的机器上,使用默认设置编码和随后解码一个随机的32字节数组平均需要 ~0.8µs — 包括分配和所有;没有隐藏费用。
- 它很小!Bunk 只存储一个包含256个音节的表,每个音节由1-4个字母(平均2.47个字母)组成,以及一些用于快速查找所需的数据结构。
- 可以在编码消息中添加可变长度的校验和,以便在解码时验证数据完整性,从而防止输入错误。
- 最大单词长度(以音节计)可以自定义。
⚖️ 与英语词典编码的比较
一种流行的方案是将二进制数据编码为实际的英语单词,这会产生更易读、更容易记忆的结果。例如,请参阅 bip39。然而,为了高效地编码单词字符串中的数据量,必须包括一个巨大的(有时相当长的)单词表 — bip39 使用2048个单词。除此之外,还需要某种用于查找的数据结构,并且可能需要在运行时构建。如果这不符合您的应用程序,请使用类似 bip39 的方案!
Bunk 采用不同的方法,只需要一个包含256个1-4个字母音节的表,每个音节携带一个字节数据。这使得 Bunk 能够
- 总体占用更少的内存。
- 将用于快速查找所需的数据结构存储在静态内存中,而不是在运行时构建。
🛠️ 工作原理
为了解释算法,我们将逐步构建它并解决过程中出现的问题。
基本思想是将一个字节编码为一个音节,通过将其用作索引来查询一个包含256个唯一音节的表,然后将结果追加到编码字符串中——正如人们所期望的那样。解码器可以使用字典树来找到字符串开头的最长音节索引,这对应于编码的字节。
这本身就会导致解析歧义问题,当一个有效的音节是另一个音节的子串时。以一个基本示例来说,编码字符串 "ous"。这是单个音节 "ous",还是音节 "o" 后面跟着 "us" 呢?除非有一些笨拙的机制,否则解码器无法知道!因此,编码器必须检测到这种歧义是否可能,方法是检查第二个音节的首字母是否是第一个音节的合法后续。如果是这样,它将在它们之间插入一个单词分隔符。(技术上,这比打破歧义所必需的更严格,但易于检查,并允许编写贪婪的解码器。)
为了支持这两个必需的操作——在字符串前找到最长的音节,以及检查一个字母是否是音节的合法后续——Bunk 使用字典树。因此出现了两个问题
- 字典树的构建速度很慢。
- 没有有效的 Rust 字典树库允许在它们的 API 中执行这些操作。
作为这两个问题的解决方案,一个预先计算的字典树(如由crawdad创建)存储在静态内存中,Bunk 在其上实现了一个基本的遍历,这是执行两个操作所需的唯一 API。总的来说,字典树 API 只有大约 60 行代码——远少于需要添加crawdad(或类似)作为依赖项。
到目前为止,我们描述的算法是一个功能齐全的编码器。然而,为了更易于使用,我们理想上还希望所有的输入都产生易于发音的文本。如果没有采取任何进一步措施,像[0, 0, 0, 0]
这样的输入会产生重复的音节,在这种情况下是 "uuu u"。为了避免这种情况,Bunk 通过首先将它们与一个依赖于它们索引的值进行异或操作来人为地增加编码字节的外观熵。由于异或会相互抵消,解码器可以执行完全相同的操作来检索原始字节。有了这个,[0, 0, 0, 0]
就可以被很好地编码为 "trirori mulry"。
依赖关系
~0.3–0.8MB
~20K SLoC