module encoding

module lib/encoding.mu

import "encoding"

encoding provides tiny helpers for serializing binary data at the syscall layer.

This module deliberately imports nothing. It sits underneath json, sockets and sha256, and every one of them reaches it from a hot loop, so it stays as close to the bytes as the language allows.

Functions

bytes#

fn bytes(s)

bytes returns the list of byte values (0..255) of an ASCII/byte string.

Source lib/encoding.mu:28
fn bytes(s) {
    out := []
    i := 0

    while i < len(s) {
        out = append(out, ord(s[i]))
        i = i + 1
    }

    return out
}

encode_uint64_le#

fn encode_uint64_le(value)

encode_uint64_le writes exactly eight bytes in little-endian order so mu code can build syscall buffers that expect 64-bit values.

Source lib/encoding.mu:13
fn encode_uint64_le(value) {
    out := ""
    i := 0

    while i < 8 {
        out = out + chr(value & 255)
        value = value >> 8
        i = i + 1
    }

    return out
}

format_hex#

fn format_hex(value, uppercase...)

format_hex renders a non-negative integer as hexadecimal, with no prefix and no leading zeros ("0" for zero). Pass true for upper case.

Source lib/encoding.mu:134
fn format_hex(value, uppercase...) {
    if type(value) != "INTEGER" || value < 0 {
        return error("encoding.format_hex expects a non-negative integer, got " + inspect(value))
    }

    upper := len(uppercase) > 0 && uppercase[0]

    if value == 0 {
        return "0"
    }

    out := ""

    while value > 0 {
        out = hex_digit(value % 16, upper) + out
        value = value / 16
    }

    return out
}

hex#

fn hex(byte_list)

hex returns the lowercase hex string of a list of byte values, two characters per byte.

Source lib/encoding.mu:88
fn hex(byte_list) {
    out := ""
    i := 0

    while i < len(byte_list) {
        b := byte_list[i]
        out = out + _HEX_LOWER[b >> 4 & 15] + _HEX_LOWER[b & 15]
        i = i + 1
    }

    return out
}

hex_digit#

fn hex_digit(value, uppercase...)

hex_digit returns the hexadecimal character for a value in 0..15. Pass true to get upper case.

Source lib/encoding.mu:43
fn hex_digit(value, uppercase...) {
    if type(value) != "INTEGER" || value < 0 || value > 15 {
        return error("encoding.hex_digit expects an integer in 0..15, got " + inspect(value))
    }

    if len(uppercase) > 0 && uppercase[0] {
        return _HEX_UPPER[value]
    }

    return _HEX_LOWER[value]
}

hex_value#

fn hex_value(ch)

hex_value returns the numeric value of a hexadecimal character, or -1 when ch is not one.

It answers a sentinel rather than an error because its callers are scanners: they walk until the digits stop, and "this byte is not a digit" is the normal way that loop ends, not a failure. A caller that does want an error can say so in its own words, which is what json.decode does.

Source lib/encoding.mu:63
fn hex_value(ch) {
    if type(ch) != "STRING" || len(ch) != 1 {
        return -1
    }

    code := ord(ch)

    if code >= ord("0") && code <= ord("9") {
        return code - ord("0")
    }

    if code >= ord("a") && code <= ord("f") {
        return code - ord("a") + 10
    }

    if code >= ord("A") && code <= ord("F") {
        return code - ord("A") + 10
    }

    return -1
}

text#

fn text(buffer, length)

text rebuilds length bytes of a buffer as a string, every byte faithful — NULs included. This is THE way bytes become a string: str(buffer) copies raw bytes but stops at the first NUL, and chr() is a codepoint constructor that spells 0x80–0xFF as two UTF-8 bytes, so any byte-wise chr() loop silently corrupts binary data. Non-NUL runs go through one str() copy each; NUL runs are built by doubling.

Source lib/encoding.mu:162
fn text(buffer, length) {
    parts := []
    run := buf(0)
    i := 0

    while i < length {
        if buffer[i] == 0 {
            if len(run) > 0 {
                parts = append(parts, str(run))
                run = buf(0)
            }

            zeros := 0

            while i < length && buffer[i] == 0 {
                zeros = zeros + 1
                i = i + 1
            }

            parts = append(parts, _nuls(zeros))
        } else {
            run = append(run, buffer[i])
            i = i + 1
        }
    }

    if len(run) > 0 {
        parts = append(parts, str(run))
    }

    return _join(parts, 0, len(parts))
}

unhex#

fn unhex(text)

unhex returns the byte values of a hex string, in either case. The text must have an even length and hold nothing but hex digits.

Source lib/encoding.mu:104
fn unhex(text) {
    if type(text) != "STRING" {
        return error("encoding.unhex expects string text, got " + type(text))
    }

    if len(text) % 2 != 0 {
        return error("encoding.unhex expects an even number of digits, got " + str(len(text)))
    }

    out := []
    i := 0

    while i < len(text) {
        high := hex_value(text[i])
        low := hex_value(text[i + 1])

        if high < 0 || low < 0 {
            return error("encoding.unhex: not a hex digit at offset " + str(i))
        }

        out = append(out, high * 16 + low)
        i = i + 2
    }

    return out
}

Internal helpers

Underscore-prefixed names are implementation detail. They are listed so the module's source reads without surprises, not as API — they may change at any time.

_HEX_LOWER
_HEX_UPPER
_join(parts, lo, hi)
_nuls(n)