LmCast :: Stay tuned in

Base84 deserves a place in file names

Recorded: Sept. 13, 2026, 2:09 p.m.

Original Summarized

Base84 deserves a place in file names - Frank DENIS random thoughts.

Skip to main content

Frank DENIS random thoughts.

Archive
RSS

Base84 deserves a place in file names

September 9, 2026

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.
“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.
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.
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 ...
Here’s the alphabet, in encoding order:
ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!#$%&'()+,-;=@[]^_`{}~

Every character is acceptable in a filename on the usual Linux, macOS and Windows filesystems.
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.
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.
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.
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.
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.

Related Posts

Sep 09, 2026
Semi-public Git repositories

Aug 19, 2026
Why compiling Rust to WebAssembly is slow

Jul 30, 2026
An improved attack on 7-round AES

Jul 16, 2026
AES gets swizzled

Jul 08, 2026
The best WebAssembly runtime may still be no runtime at all

← Back to posts

GitHub
Twitter
DNSCrypt
Archive
RSS

Frank DENIS proposes that Base84 should be incorporated into file naming conventions to ensure portability and compatibility across different operating system filesystems. This necessity arose because the initial implementation of the TurboCrypt file encryption tool, which utilized Base91 for encoding encrypted filenames, was constrained by the specific requirements of Unix and macOS systems. When expanding compatibility to Windows, where certain characters found in the Unix-filesystem-safe alphabet are forbidden, a change in encoding scheme was required.

The rationale for adopting Base84 is rooted in the character set restrictions imposed by the Windows filesystem. While the standard printable ASCII set includes 94 characters excluding the space, Windows imposes specific restrictions that exclude nine characters, such as less-than signs, greater-than signs, colons, quotes, forward slashes, backslashes, pipes, question marks, asterisks, and the dot. Furthermore, dropping the dot is recommended for reliable interaction with the Windows shell and ordinary file APIs. This adjustment reduces the available character set to 84 potentially usable characters for a filename component.

The implementation details of Base84, as seen in the zig-base84 library, focus on efficient bit packing. The encoder groups the encoded data into sets of five characters, a choice determined by an optimization where 84 to the power of five is used, yielding a result close to 4.18 billion, which offers sufficient room for approximately 32 bits of information. For random input, the packing results in an average expansion rate of approximately 25.2% compared to the binary input, positioning it as slightly more efficient than Base64, with a worst-case expansion rate reaching about 29.0% when the input is entirely composed of the maximum value 0xff.

The practical application of Base84 also addresses filesystem size limits. Since most filesystems restrict names to a maximum of 255 bytes, and the Base84 alphabet size (84) is a divisor of 255, the encoding naturally fits perfectly into whole groups without losing information due to partial groups. This method guarantees room for approximately 197 bytes of input, which is an improvement over the 191 bytes suggested by unpadded Base64. This feature provides a robust method for handling filenames within established filesystem constraints.

When considering Unix-only filenames, a specialized variant of Base91 exists where the slash character is replaced by an apostrophe. This variant offers a packing efficiency of approximately 6.51 bits per character for random input, resulting in a roughly 23% expansion. Moreover, the structure imposed by the five-character packing has an important side effect concerning reserved device names. Because the encoding scheme constrains the possible characters at the end of the encoded string, it is impossible to spell reserved device names like CON, NUL, COM1, or LPT1 through LPT9, regardless of case, by restricting the final characters to a specific range. This inherent property eliminates the need for additional special handling or padding to avoid these reserved names.