charcode
Character encoding conversion for Rust, implementing the WHATWG Encoding Standard — the set of encodings, labels and error behaviours that browsers actually use.
- No dependencies. Nothing outside the standard library.
serdeis optional and off by default. - No
unsafe. The crate is#![forbid(unsafe_code)]. no_std. Works with an allocator, or without one.- Complete. All 40 encodings in the standard and all 228 of their labels.
[]
= "0.1"
Converting a buffer
use ;
let = WINDOWS_1252.decode;
assert_eq!;
assert_eq!;
assert!;
decode implements the standard's decode hook: a byte order mark wins over the
encoding you name, and malformed sequences become U+FFFD, with the returned flag
telling you whether any did. Encodings are looked up by any label the standard
defines, which is what a Content-Type header or a <meta charset> carries:
use Encoding;
let encoding = for_label.unwrap;
assert_eq!;
let = encoding.decode_without_bom_handling;
assert_eq!;
Encoding goes the other way. Characters the target cannot represent become HTML numeric character references, the way form submission does:
use EUC_KR;
let = EUC_KR.encode;
assert_eq!;
assert!;
Both return a Cow, borrowed when the input already is the output, so ASCII
through an ASCII-compatible encoding costs only the scan.
Converting a stream
For input that arrives in pieces, a Decoder carries a partial sequence from one
buffer to the next. Pass last = true with the final buffer so a truncated
sequence is reported rather than dropped:
use BIG5;
let mut decoder = BIG5.new_decoder;
let mut text = Stringnew;
decoder.decode_to_string;
decoder.decode_to_string;
assert_eq!;
Decoder::decode_to_utf8 and Encoder::encode_from_utf8 are the
allocation-free forms, writing into a &mut [u8] you provide. They work with an
output buffer as small as four bytes.
Handling errors instead of substituting them
Every conversion comes in two flavours. The default substitutes, as the standard
requires for web content. The without_replacement forms stop at the first
problem and report it, for callers that need to reject bad input:
use ;
assert!;
let mut bytes = Vecnew;
let error = WINDOWS_1252
.new_encoder
.encode_from_str_without_replacement
.unwrap_err;
assert_eq!;
assert_eq!;
Windows code pages
Encodings can also be looked up by Microsoft code page number, which is what
GetACP, a .NET Encoding.CodePage or an old database column reports:
use ;
assert_eq!;
assert_eq!;
assert_eq!;
A number for an encoding the standard folds into a superset resolves to that
superset, exactly as the equivalent label does — 28591 (ISO-8859-1) and 20127
(US-ASCII) both give windows-1252. Encoding::for_cp is the same lookup under
the name a cp932-style spelling suggests. The CLI accepts those spellings too
— charcode -f cp932, -f windows-932, -f x-cp20936 — though a bare number
is not a charset name and stays an error.
Command-line tool
The cli feature builds charcode, an iconv-style converter that adds no
dependencies of its own:
$ printf 'caf\xe9' | charcode -f latin1 -t utf-8
café
$ charcode -f shift_jis -t utf-8 notes.txt > notes.utf8.txt
$ charcode --list-labels | grep 'windows-1252$'
Like iconv, it stops at the first byte it cannot convert, -c omits the
offending input instead, and either way a lossy run exits non-zero so a script
can tell the difference. --substitute asks for the standard's web behaviour
instead — U+FFFD for malformed input, a numeric character reference for a
character the output encoding cannot represent:
$ printf 'a\xffb' | charcode
charcode: (standard input): malformed byte sequence of 1 byte(s) at offset 1
Use -c to omit it, or --substitute to replace it with U+FFFD.
$ printf 'a\xffb' | charcode --substitute
a<U+FFFD>b
Any label works wherever an encoding is named, a leading byte order mark
overrides -f, and iconv's //IGNORE suffix is accepted as a synonym for
-c. charcode --help lists the rest.
Supported encodings
| Group | Encodings |
|---|---|
| Unicode | UTF-8, UTF-16BE, UTF-16LE |
| Single-byte | IBM866, ISO-8859-2/3/4/5/6/7/8/8-I/10/13/14/15/16, KOI8-R, KOI8-U, macintosh, windows-874, windows-1250 through windows-1258, x-mac-cyrillic |
| Chinese | GBK, gb18030, Big5 |
| Japanese | EUC-JP, ISO-2022-JP, Shift_JIS |
| Korean | EUC-KR |
| Other | replacement, x-user-defined |
UTF-16BE, UTF-16LE and replacement decode only; asking them to encode gives
UTF-8, which is what the standard's get an output encoding prescribes.
Features
std(default) — implementsstd::error::Errorfor the error types. Impliesalloc.alloc(default, viastd) — the conveniences that hand back an ownedString,VecorCow:Encoding::decodeandencode,Decoder::decode_to_string,Encoder::encode_from_str.serde— an encoding serializes as its name and deserializes from any label. Needs neitherstdnoralloc.cli— builds thecharcodecommand-line tool described above. Adds no dependencies; it needsstd.
With default-features = false and no alloc, what remains is the whole
conversion engine plus encoding lookup — everything in the Converting a
stream section — converting into buffers you provide and
never touching a heap:
[]
= { = "0.1", = false }
use ;
let mut decoder = WINDOWS_1252.new_decoder_without_bom_handling;
let mut buffer = ;
let =
decoder.decode_to_utf8_with_replacement;
assert_eq!;
assert_eq!;
Relation to encoding_rs
Both implement the same standard and should agree on every conversion.
encoding_rs is the one Firefox ships and is faster, using SIMD and a good deal
of unsafe to get there. charcode trades that for having no dependencies, no
unsafe, and a smaller surface to audit. Use encoding_rs if throughput on
large documents is what matters; use charcode if a dependency-free, unsafe-free
build is.
Index tables
The tables in src/tables/ are generated from the standard's own
indexes.json by tools/generate_tables.py, from the copy of that data checked in under
tools/data/. The generated sources are checked in too, so
building the crate needs no build script and no network. Re-run the generator
only when the upstream indexes change:
Minimum supported Rust version
1.88. Raising it is a breaking change.
License
MIT. See LICENSE.