Encoding v0.1.0
Installation
Add Encoding to the [Dependencies] table of your package's Rux.toml:
[Dependencies]
Encoding = { Namespace = "Rux", Version = "0.1.0" }
Standard packages are declared by hand: the registry does not serve them, so rux add Rux/Encoding cannot add them.
Dependencies
Guide
What it provides
| Module | Covers |
|---|---|
Hex | HexEncode, HexDecode, their length functions, HexCase, and the ViewHex display adapter |
Base64 | Base64Encode, Base64Decode, their length functions, the four Base64Variants, and ViewBase64 |
Base32 | Base32Encode, Base32Decode, their length functions, and the RFC 4648 and Crockford Base32Variants |
Base58 | Base58Encode, Base58Decode and Base58EncodedMaxLength, in the Bitcoin alphabet |
Percent | PercentEncode, PercentDecode, their length functions, and the Component, Path and Form sets |
Stream | EncodingWriter and DecodingReader, which encode and decode hexadecimal, Base64 and Base32 through Io streams |
Error | DecodeError, shared by every decoder |
The module names describe where each declaration comes from; they are not part of an import path.
Example
import Encoding::{ Base64Decode, Base64DecodedLength, Base64Encode, Base64EncodedLength, Base64Variant };
func RoundTrip() -> bool {
let message = "Many hands make light work.";
var text: char8[64];
let written = Base64Encode(message, text[..], Base64Variant::UrlUnpadded) catch { else => 0 };
var bytes: byte[64];
let read = Base64Decode(text[..written], bytes[..], Base64Variant::UrlUnpadded) catch { else => 0 };
return read == message.length && written == Base64EncodedLength(message.length, Base64Variant::UrlUnpadded);
}
Design
Every function writes into a destination the caller provides and returns how much it wrote, so nothing here allocates. Each scheme has a length function that says exactly how large that destination must be, except Base58, whose length depends on the value and which offers a bound instead. ViewHex and ViewBase64 borrow bytes and render them under a format placeholder, which is the way to get an allocated string: Render("{}", ViewHex(digest)).
A destination that is too short is refused rather than filled with what fits, because a truncated encoding is a different value rather than a shorter one. Encoders report FormatError::InsufficientCapacity; decoders report DecodeError::InsufficientCapacity with the length they needed. Decoders check the whole text before writing a byte, so every refusal except Base58's capacity refusal leaves the destination exactly as it was.
Decoding is strict, so that each value has one encoded form and two systems cannot read the same text differently. Hexadecimal and Base32 accept either case and nothing else: no prefix, no separators, no whitespace. Base64 accepts only its variant's alphabet, padding only where the variant puts it, and a last character whose unused bits are zero. The exceptions are deliberate: Crockford's Base32 reads I and L as 1, O as 0 and skips hyphens because its specification asks readers to, and percent-decoding passes through characters an encoder would have escaped because text arrives from many encoders.
Each DecodeError carries where it was found: the byte offset of the character that is wrong, or the end of the text when the length is.
Streams
EncodingWriter encodes what is written to it onto another Io::Writer, and DecodingReader decodes text from another Io::Reader, for hexadecimal, Base64 and Base32 in every form a StreamCodec names. Both work a group at a time through the slice functions, so the text a stream writes is exactly the text encoding the whole input would give. A writer holds back the bytes of an incomplete group until Finish ends the text with its last group and padding. A reader decodes the text it has read in whole groups and fails with InvalidInput when it reaches text the decoder refuses. Base58 has no streaming form, since every one of its characters depends on the whole input.
Timing
Keys, tokens and secrets travel through these encodings as often as anything else, so hexadecimal, Base64 and RFC 4648 Base32 encode and decode in time that depends only on the length. They map digits with masks built from arithmetic rather than branches or table lookups, which would otherwise make the timing and the cache lines touched depend on the secret. Base58 and Crockford's Base32 are meant for identifiers and do not make that promise.
Index
Types
| Name | Summary |
|---|---|
Base32Variant | Which Base32 alphabet a text uses, and whether it is padded. |
Base64Variant | Which Base64 alphabet a text uses, and whether it is padded. |
Base64View | A borrowed byte sequence that renders as Base64 under a format placeholder. |
DecodeError | Why text could not be decoded. |
HexCase | Which case hexadecimal digits above nine are written in. |
HexView | A borrowed byte sequence that renders as hexadecimal under a format placeholder. |
PercentSet | Which characters percent-encoding leaves as they are. |
StreamCodec | Which encoding a stream uses, with the form of it. |
EncodingWriter | A writer that encodes what is written to it onto another writer. |
DecodingReader | A reader that decodes text from another reader into bytes. |
Functions
| Name | Summary |
|---|---|
Base32EncodedLength | How many characters the Base32 form of length bytes occupies in form. |
Base32Encode | Writes bytes as Base32 into into. |
Base32DecodedLength | How many bytes the Base32 text decodes to, after checking its length and the placement of its padding. |
Base32Decode | Reads Base32 text in form into into. |
Base58EncodedMaxLength | The most characters the Base58 form of length bytes can occupy. |
Base58Encode | Writes bytes as Base58 into the front of into. |
Base58Decode | Reads Base58 text in the Bitcoin alphabet into the front of into. |
Base64EncodedLength | How many characters the Base64 form of length bytes occupies in form. |
Base64Encode | Writes bytes as Base64 into into. |
Base64DecodedLength | How many bytes the Base64 text decodes to, after checking its length and the placement of its padding. |
Base64Decode | Reads Base64 text in form into into. |
ViewBase64 | Borrows bytes for rendering as Base64. |
HexEncodedLength | How many characters the hexadecimal form of length bytes occupies: exactly twice as many. |
HexEncode | Writes bytes as hexadecimal digits into into, two per byte, high nibble first. |
HexDecodedLength | How many bytes the hexadecimal text decodes to, after checking that its length is even. |
HexDecode | Reads hexadecimal digits of either case from text into into, one byte per pair. |
ViewHex | Borrows bytes for rendering as hexadecimal. |
PercentEncodedLength | How many characters the percent-encoded form of bytes occupies in set. |
PercentEncode | Writes the bytes of text percent-encoded for set into into. |
PercentDecodedLength | How many bytes percent-encoded text decodes to, after checking that every escape is well formed. |
PercentDecode | Reads percent-encoded text into into, turning each escape back into its byte. |