Base84 deserves a place in file names
Base84 deserves a place in file names
Base84 值得在文件名中占有一席之地
The TurboCrypt file encryption tool was originally designed for Unix systems. And it used to encrypt file names and encode the resulting ciphertext using Base91. Why Base91? Because it’s a perfect fit for encrypted file names, producing strings that can be stored as valid files on Unix and macOS. TurboCrypt 文件加密工具最初是为 Unix 系统设计的。它过去常对文件名进行加密,并使用 Base91 对生成的密文进行编码。为什么要用 Base91?因为它非常适合加密文件名,生成的字符串可以作为有效文件存储在 Unix 和 macOS 系统中。
“But my filesystem can store arbitrary file names”! That may be true for some filesystems, but this is without taking libraries and applications into consideration. For example, the macOS Finder would not like this at all. So, Base91 worked fine for encrypted file and directory names. “但我的文件系统可以存储任意文件名!”对于某些文件系统来说这可能是真的,但这没有考虑到库和应用程序的限制。例如,macOS Finder 就完全无法处理这种情况。因此,Base91 在处理加密的文件名和目录名时表现良好。
Then people asked for Windows support, where several characters in the Unix filesystem-safe alphabet are forbidden. So, TurboCrypt is switching to Base84. Something surprisingly not defined nor (apparently) used anywhere, even though it’s a perfect fit for anything that should be encoded as portable filesystem-safe names. 后来人们要求提供 Windows 支持,而 Unix 文件系统安全字符集中的几个字符在 Windows 中是被禁止的。因此,TurboCrypt 转向了 Base84。令人惊讶的是,尽管 Base84 非常适合作为可移植的文件系统安全名称进行编码,但它似乎在任何地方都没有被定义或使用过。
Why Base84? There are 94 printable ASCII characters excluding the space. But Windows rules exclude nine of them: < > : ” / \ | ? * That leaves 85. But a name ending in a dot doesn’t work reliably through the Windows shell and ordinary file APIs. Remove the dot as well, and we have 84 characters that can appear anywhere in a filename component. Microsoft documents these restrictions. However, Windows allows a leading dot: .gitignore is fine. But dropping dots also avoids hidden names on Unix and the special names . and … 为什么要用 Base84?除去空格,共有 94 个可打印的 ASCII 字符。但 Windows 规则排除了其中的 9 个:< > : ” / \ | ? *,这剩下 85 个。然而,以点号结尾的文件名在 Windows Shell 和普通文件 API 中无法可靠工作。去掉点号后,我们剩下 84 个可以出现在文件名任何位置的字符。微软记录了这些限制。不过,Windows 允许以点号开头:.gitignore 是合法的。但去掉点号也避免了 Unix 上的隐藏文件名以及 . 和 .. 等特殊名称。
Here’s the alphabet, in encoding order: ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!#$%&’()+,-;=@[]^_{}~ Every character is acceptable in a filename on the usual Linux, macOS and Windows filesystems. 以下是按编码顺序排列的字符集:ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!#$%&'()+,-;=@[]^_{}~ 每个字符在常见的 Linux、macOS 和 Windows 文件系统中作为文件名都是合法的。
Packing the bits zig-base84 is an implementation of Base84. It emits groups of five characters. Five is the sweet spot: 84⁵ = 4,182,119,424, only 2.6% short of 2³². That leaves enough room for a group to hold 32 bits about 95% of the time on uniformly random input, and 31 bits otherwise. 位打包(Packing the bits):zig-base84 是 Base84 的一种实现。它以 5 个字符为一组进行输出。5 是一个最佳平衡点:84⁵ = 4,182,119,424,仅比 2³² 少 2.6%。这留出了足够的空间,使得一组字符在均匀随机输入的情况下约 95% 的时间能容纳 32 位数据,其余情况则容纳 31 位。
The encoder looks at the next 31 bits. If their value is below 84⁵ - 2³¹, there’s room for a 32nd bit. Otherwise, it consumes just those 31 bits. Either way, the value fits in five base-84 digits. On random input, that’s about 31.95 bits per group, or 6.39 bits per character. The output is about 25.2% larger than the binary input. Almost Base85. 编码器查看接下来的 31 位。如果其值小于 84⁵ - 2³¹,则有空间容纳第 32 位。否则,它只消耗这 31 位。无论哪种方式,该值都能放入 5 个 Base84 位中。在随机输入下,每组约为 31.95 位,即每个字符 6.39 位。输出比二进制输入大约 25.2%。几乎等同于 Base85。
These expansion rates ignore the final partial group; the averages assume random input: 这些扩展率忽略了最后的部分组;平均值假设为随机输入:
| Encoding | Average expansion | Worst-case expansion |
|---|---|---|
| Base64 | 33.3% | 33.3% |
| Base84 | 25.2% | 29.0% |
An input filled with 0xff forces every full group to consume only 31 bits. That’s the worst case: about 29% expansion. Most filesystems cap a name at 255 bytes. Since the alphabet is ASCII, that’s 255 characters. Five divides 255 exactly, so even a maximum-length name holds only complete groups, with no bits lost to a partial one. Base84 guarantees room for 197 bytes of input, compared with 191 for unpadded Base64. 充满 0xff 的输入会强制每个完整组仅消耗 31 位。这是最坏的情况:约 29% 的扩展。大多数文件系统将文件名限制在 255 字节。由于字符集是 ASCII,即 255 个字符。5 可以整除 255,因此即使是最大长度的文件名也只包含完整组,不会因部分组而丢失位。Base84 保证可容纳 197 字节的输入,而未填充的 Base64 仅为 191 字节。
Unix-only names: Unix filenames can contain most of the punctuation Windows rejects. NUL and / are forbidden inside a filename; the Linux pathname documentation lists the rules and filesystem-specific limits. The filesystem variant in zig-base91 replaces the standard Base91 alphabet’s slash with an apostrophe. It packs about 6.51 bits per character on random input, giving roughly 23% expansion. For Unix-only names, use that variant. Standard Base91 still contains /, and both alphabets contain characters Windows rejects. 仅限 Unix 的名称:Unix 文件名可以包含大多数 Windows 禁止的标点符号。文件名中禁止使用 NUL 和 /;Linux 路径名文档列出了规则和特定于文件系统的限制。zig-base91 中的文件系统变体将标准 Base91 字符集中的斜杠替换为撇号。在随机输入下,它每个字符打包约 6.51 位,扩展率约为 23%。对于仅限 Unix 的名称,请使用该变体。标准的 Base91 仍然包含 /,且两种字符集都包含 Windows 禁止的字符。
Reserved names and case: Windows reserves device names such as CON, NUL and COM1, regardless of case. The five-character packing has a useful side effect: with the standard alphabet, the encoder can’t spell a reserved device name, even for short inputs. A three-character output always ends with A through J. That rules out CON, PRN, AUX and NUL, regardless of case. A four-character output always ends with an uppercase letter or a, b, c. It can’t end with a digit, so COM1 through COM9 and LPT1 through LPT9 are impossible too. The superscript digits Windows also reserves aren’t in the alphabet. And the alphabet has no dots, so a reserved name followed by an extension is also impossible. No padding or special handling is needed to avoid these names. 保留名称与大小写:Windows 保留了诸如 CON、NUL 和 COM1 等设备名称,不区分大小写。五字符打包有一个有用的副作用:使用标准字符集时,编码器无法拼写出保留的设备名称,即使是短输入也是如此。三字符输出总是以 A 到 J 结尾。这排除了 CON、PRN、AUX 和 NUL,无论大小写如何。四字符输出总是以大写字母或 a、b、c 结尾。它不能以数字结尾,因此 COM1 到 COM9 以及 LPT1 到 LPT9 也不可能出现。Windows 保留的上标数字也不在字符集中。此外,该字符集没有点号,因此保留名称后跟扩展名也是不可能的。无需任何填充或特殊处理即可避免这些名称。