encoding

tape
import encoding from "encoding";

let b: string = encoding.base64_encode("foobar");          // "Zm9vYmFy"
let t: string = encoding.base64url_encode("\xFB\xFF");     // "-_8"
let h: string = encoding.hex_encode("tape");                // "74617065"
let q: string = encoding.percent_encode("a b/ü");           // "a%20b%2F%C3%BC"

let raw: string = encoding.base64_decode(b) or {
    io.println("bad base64: {err}");
    return 1;
};

encoding turns bytes into text and back:

EncodingAlphabetUsed for
base64A-Z a-z 0-9 + /, padded with =binary data in text formats, email, data URLs
base64urlA-Z a-z 0-9 - _, no paddingtokens, file names, URLs (JWT)
hex0-9 a-fdumps, hashes, IDs
percentunreserved characters kept, everything else %XXURL paths and query strings

encoding is a pure module.

Functions

Each encoding has an encoder and a decoder, and each of those comes in two forms: one returns a new string, the _to one writes into a buffer you pass and returns how many bytes it wrote (as in fmt).

FunctionReturns
base64_encode(data), base64_encode_to(dst, data)base64 text, padded
base64_decode(s), base64_decode_to(dst, s)the bytes, or DecodeError
base64url_encode(data), base64url_encode_to(dst, data)base64url text, unpadded
base64url_decode(s), base64url_decode_to(dst, s)the bytes; padded input is accepted too
hex_encode(data), hex_encode_to(dst, data)lowercase hex, two digits per byte
hex_decode(s), hex_decode_to(dst, s)the bytes; either case accepted
percent_encode(s), percent_encode_to(dst, s)s with every byte but A-Z a-z 0-9 - . _ ~ as %XX
percent_decode(s), percent_decode_to(dst, s)s with each %XX turned back into its byte

Decoded data comes back as a string, tape’s byte buffer: it can hold any bytes, not just text.

A _to function stops the program with a trap when the buffer is too small. These give a size that is always enough:

FunctionSize
base64_encoded_len(n)base64 of n bytes: 4 * ceil(n / 3)
base64url_encoded_len(n)base64url of n bytes: ceil(4 * n / 3)
base64_decoded_len(n)at most this many bytes from n characters of either base64
percent_encoded_len(s)exactly what percent_encode(s) writes

Hex is 2 * n characters for n bytes; decoding percent-encoding never gives more bytes than the text has.

tape
var buf: [64]u8;
let n: i64 = encoding.base64_encode_to(buf[..], "id-42");
let text: []const u8 = buf[0..n];          // "aWQtNDI=", no allocation

Errors

Decoding is strict: nothing is skipped or guessed, so every byte string has exactly one accepted encoding. Decoders return DecodeError:

DecodeErrorWhen
InvalidCharacterA byte outside the alphabet: a space or line break in base64, + in base64url, g in hex, % without two hex digits
InvalidLengthA length the encoding can’t produce: base64 not a multiple of 4, an odd number of hex digits
InvalidPadding= in the wrong place or too many, or leftover bits that aren’t zero ("Zh==" instead of "Zg==")

Percent-decoding leaves + alone: it is not a space here (that’s HTML form encoding). Base64 with line breaks (MIME, PEM) must have them removed before decoding.

Last modified: