Files
Tools/base64-rs/README.md
T
LyAhn 665f846dc9 test(base64-rs): add GNU differential parity harness
Runs both binaries over 154 cases and compares stdout, stderr and exit
code: encoding at every wrap width, decode round-trips, canonical-tail
rules, padding placement, concatenated streams, every prefix of a known
encoding, usage errors and file operands.

Locates a genuine GNU base64 rather than trusting /usr/bin/base64, since
distributions increasingly ship uutils there and it disagrees with GNU on
most malformed input — running against it fails 51 of the 154 cases, which
doubles as a negative control for the harness itself.

The three known differences are asserted as expected rather than ignored.
2026-07-27 21:53:26 +01:00

82 lines
4.4 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# base64 for Windows
Windows has no `base64` command. `certutil -encode` is the usual substitute, but it only works on files, wraps output in `-----BEGIN CERTIFICATE-----` markers, and can't be piped. This is a real `base64.exe` that behaves like the one on a Linux box, so the commands and scripts you already know carry over.
```powershell
base64 image.png > image.b64
base64 -d image.b64 > image.png
type secrets.json | base64 -w0
base64 -di messy.txt > payload.bin
```
## Build and install
```powershell
cargo build --release
```
Copy `target\release\base64.exe` somewhere on your PATH — e.g. `%LOCALAPPDATA%\Microsoft\WindowsApps`, or a `C:\tools\bin` folder you've added yourself:
```powershell
mkdir C:\tools\bin
copy target\release\base64.exe C:\tools\bin\
[Environment]::SetEnvironmentVariable(
'Path',
[Environment]::GetEnvironmentVariable('Path','User') + ';C:\tools\bin',
'User')
```
Open a new terminal afterwards for the PATH change to take effect. No runtime dependencies — it's a single static binary, and the crate itself has zero dependencies, so the build works offline.
## Usage
```
Usage: base64 [OPTION]... [FILE]
Base64 encode or decode FILE, or standard input, to standard output.
With no FILE, or when FILE is -, read standard input.
-d, --decode decode data
-i, --ignore-garbage when decoding, ignore non-alphabet characters
-w, --wrap=COLS wrap encoded lines after COLS character (default 76).
Use 0 to disable line wrapping
--help display this help and exit
--version output version information and exit
```
Long options accept unambiguous abbreviations (`--dec`), short options cluster (`-di`), `-w` takes its value joined or separate (`-w0`, `-w 0`, `--wrap=0`), and `--` ends option parsing.
## Behaviour
Verified against GNU coreutils 9.7 with a 154-case differential test ([tests/parity.py](tests/parity.py)) covering encoding at every wrap width, decode round-trips, malformed padding, truncated input, garbage handling, all the usage errors and file operands. Output, exit codes and error messages match, including the fiddly parts:
- **Partial output on failure.** Bytes are emitted as soon as enough bits are available, so a stream that goes bad halfway still writes the valid prefix before the error — same as coreutils.
- **Unpadded input is accepted** when the encoding is canonical, i.e. the leftover bits are zero. `aGVsbG8` decodes to `hello`; `aGV` is rejected because `V` carries stray bits.
- **Concatenated streams** decode as one (`aGVsbG8=aGVsbG8=``hellohello`).
- **`-w0` emits no trailing newline**; wrapped output always ends with one. Empty input produces no output at all.
- **Exit codes**: `0` success, `1` on any error.
### Deliberate differences
| | Why |
|---|---|
| `\r` is skipped when decoding, as `\n` already is | GNU rejects CRLF input as invalid. On Windows a base64 file will routinely have CRLF line endings, and erroring on those would make the tool useless for its main job. Everything else is still rejected unless you pass `-i`. |
| `--help` omits GNU's support URLs | This isn't GNU coreutils; pointing at their bug tracker would be wrong. |
| Error messages quote with `'ASCII'` | GNU uses `'…'` in a UTF-8 locale. Windows consoles are frequently cp437/cp1252, where those bytes render as mojibake. |
## Running the tests
```bash
cargo build --release
python3 tests/parity.py
```
Linux-only — it diffs against a real GNU `base64`, so it can't run on the Windows box this tool targets. Note that many distributions now ship **uutils** coreutils as `/usr/bin/base64`; it disagrees with GNU on most malformed-input cases, so the script hunts for a genuine GNU build and refuses to run against anything else. On Ubuntu with `coreutils-from-uutils` installed, the GNU one is `gnubase64`.
## Notes
- Roughly 2× slower than GNU on large inputs (100 MB encodes in ~0.35s vs ~0.18s). GNU uses hand-tuned routines; this is plain portable Rust and fast enough that the difference is invisible for normal use.
- Input is streamed, not buffered whole, so encoding a multi-gigabyte file doesn't blow up memory.
- Piping to a program that exits early (`base64 big.bin | head`) is handled quietly rather than reporting a broken pipe.
- Writing raw binary to a Windows console fails with a clear message telling you to redirect, because consoles take UTF-16 text and would corrupt the bytes. Redirecting to a file or piping is unaffected.