Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Encodings and UUIDs

Networked programs keep writing bytes as text and back: credentials in an HTTP header, a token in a URL, a value in a query string, an identifier in a database. These live in a few places:

WhatWhereFunctions
Base64import base64base64.encode, base64.decode
Hexadecimalimport digestdigest.to_hex, digest.from_hex
Percent-escapesUri, in the preludeUri.encode, Uri.decode
Form dataUri, in the preludeUri.encode_form, Uri.decode_form
UUIDsimport uuidUuid.v4, Uuid.v7, Uuid.parse, to_string
# Encodings: base64, hexadecimal, percent-escapes and form data, and UUIDs.
import base64;
import digest;
import uuid;

main(): int {
    # base64: an HTTP basic authentication header, and a URL-safe,
    # unpadded token as JSON Web Tokens write them.
    var credentials = base64.encode("ada:secret".bytes());
    assert(credentials == "YWRhOnNlY3JldA==", "standard, padded");
    var token = base64.encode([251, 255, 0], true, false);
    assert(token == "-_8A", "URL-safe, unpadded");
    assert(base64.decode(token, true).value()[0] == 251, "decoded");
    assert(base64.decode("not base64!").is_err(), "rejected");

    # Hexadecimal, both ways.
    assert(digest.from_hex(digest.to_hex([1, 171])).value()[1] == 171, "round trip");

    # Percent-escapes for one path segment or query value, and form data.
    assert(Uri.encode("a b&c") == "a%20b%26c", "escaped");
    var query = Uri.encode_form([("q", "pepper lang"), ("page", "2")]);
    assert(query == "q=pepper+lang&page=2", "form-encoded");
    var pairs = Uri.decode_form(query).value();
    assert(pairs[0].1 == "pepper lang", "decoded");

    # UUIDs: random (v4) and time-ordered (v7).
    var id = uuid.Uuid.v4();
    assert(uuid.Uuid.parse(id.to_string()).value() == id, "round trip");
    assert(uuid.Uuid.v7().version() == 7, "time-ordered");
    return 0;
}

Base64

base64.encode(bytes) writes RFC 4648 base64 with the standard alphabet (A-Z a-z 0-9 + /) and = padding. Two optional arguments change that: url_safe = true uses - and _ in place of + and /, so the text can go in a URL or a file name, and padded = false leaves off the padding, as JSON Web Tokens do.

base64.decode(text) returns a Result<u8[]>. It accepts text with or without padding (pass url_safe = true for the other alphabet), and is strict otherwise: whitespace, a character outside the alphabet, a length no encoding gives, or unused bits that are not zero is an error. Remove line breaks from PEM or MIME text before decoding it. The functions work on whole arrays; there is no streaming encoder yet.

Hexadecimal

digest.to_hex(bytes) writes two lowercase digits per byte, and digest.from_hex(text) reads them back, in either case; an odd length or a character that is not a digit is an error.

Percent-escapes and form data

Uri.encode(text) escapes every byte outside RFC 3986’s unreserved set (A-Z a-z 0-9 - . _ ~), so the result is safe as one path segment or one query value: /, ?, &, = and % are escaped too. Uri.decode reverses it.

Uri.encode_form(pairs) writes names and values as application/x-www-form-urlencoded, the format of HTML forms and most query strings (q=pepper+lang&page=2), and Uri.decode_form(text) reads it back into an Array<(String, String)>, in order, keeping repeated names. In this format a space is +.

UUIDs

uuid.Uuid is a 128-bit identifier (RFC 9562), a value struct of two ulong halves, high and low, so == and hash work and it can key a map.

  • Uuid.v4() makes a random one: 122 bits from the operating system’s secure source.
  • Uuid.v7() puts the wall clock’s Unix time in milliseconds first, then 74 random bits, so ids made later sort later, both as values and as text. That keeps database indexes compact. Ids made in the same millisecond are in no particular order.
  • Uuid.parse(text) reads the usual form, 0190163d-8694-739b-aea5-966c26f8ad91, in either case, and to_string() (or interpolation) writes it in lowercase.
  • version() returns the version digit and bytes() the 16 bytes, for a database or a binary format.

The full API is in the reference: base64, uuid, hexadecimal and Uri.