Skip to main content

Crate safe_decode

Crate safe_decode 

Source
Expand description

Panic-free, allocating byte→value transforms with no format knowledge.

The companion to safe-read: that crate is no_std with no allocator and reads fixed-width integers; this one needs alloc because its outputs are Strings and Vecs. That allocator boundary is the whole reason the two are separate crates.

Membership is decidable — every function here is panic-free, allocating, format-agnostic, and free of domain knowledge. A transform that needs to know what the bytes mean belongs in the crate that owns that knowledge, not here.

§The UTF-16 family names its NUL policy

A fleet audit found fourteen hand-rolled UTF-16 decoders disagreeing about NUL in four different ways. The disagreement was invisible because every one of them was called decode_utf16le. Here each policy is a separately named function, so picking the wrong one is a deliberate act rather than an accident:

use safe_decode::{
    decode_utf16le_keep_nuls, decode_utf16le_trim_end_nuls, decode_utf16le_until_nul,
    split_utf16le_on_nul,
};

// UTF-16LE for 'A', NUL, 'B', NUL — the same bytes, four correct answers.
let bytes = b"A\0\0\0B\0\0\0";
assert_eq!(decode_utf16le_keep_nuls(bytes).text, "A\0B\0");
assert_eq!(decode_utf16le_until_nul(bytes).text, "A");
assert_eq!(decode_utf16le_trim_end_nuls(bytes).text, "A\0B");
let parts = split_utf16le_on_nul(bytes);
let texts: Vec<&str> = parts.iter().map(|d| d.text.as_str()).collect();
assert_eq!(texts, ["A", "B", ""]);

Each decode returns a DecodedUtf16, which carries whether information was lost and why — an unpaired surrogate half, or an odd trailing byte that could not form a code unit. A caller that ignores it still gets well-formed text; a caller that cares can say so in a report.

use safe_decode::decode_utf16le_keep_nuls;

let lone_high_surrogate = decode_utf16le_keep_nuls(&[0x00, 0xD8]);
assert_eq!(lone_high_surrogate.text, "\u{FFFD}");
assert_eq!(lone_high_surrogate.unpaired_surrogates, 1);
assert!(lone_high_surrogate.is_lossy());

Structs§

DecodedUtf16
A decoded UTF-16 string together with what decoding it cost.

Functions§

decode_utf16be_keep_nuls
Decode the whole slice as UTF-16BE, keeping NUL code units as U+0000 characters.
decode_utf16be_trim_end_nuls
Decode the whole slice as UTF-16BE, then strip NUL code units from the end only.
decode_utf16be_until_nul
Decode UTF-16BE up to the first NUL code unit, which terminates the string.
decode_utf16le_keep_nuls
Decode the whole slice as UTF-16LE, keeping NUL code units as U+0000 characters.
decode_utf16le_trim_end_nuls
Decode the whole slice as UTF-16LE, then strip NUL code units from the end only.
decode_utf16le_until_nul
Decode UTF-16LE up to the first NUL code unit, which terminates the string.
rot13
Rotate every ASCII letter of s by thirteen positions, wrapping within its own case.
split_utf16be_on_nul
Split a UTF-16BE slice on NUL code units and decode every segment.
split_utf16le_on_nul
Split a UTF-16LE slice on NUL code units and decode every segment.
to_hex_lower
Render bytes as lowercase hex, two characters per byte, no separator.
to_hex_upper
Render bytes as uppercase hex, two characters per byte, no separator.