moonvar

    Fixed-width and variable-length integer encodings for MoonBit.

    varint
    variable-length-integer
    leb128
    uleb128
    sleb128
    zigzag
    protobuf
    hpack
    qpack
    prefix-integer
    quic
    quic-varint
    lenenc
    length-encoded-integer
    mysql
    endian
    endianness
    big-endian
    little-endian
    fixed-width
    binary
    moonbit
    Download zip
    Version
    0.2.1
    License
    Apache-2.0
    Last updated
    2 days ago
    Downloads
    3K
    moonvar


    // The fixed widths, either way round, reading nothing it was not given.
    let buf = Buffer()
    @fixed.write_u32(buf, 0x12345678) // 12 34 56 78
    @fixed.write_u16(buf, 0x1234, order=Little) // 34 12
    let wire = buf.to_bytes()
    @fixed.read_u32(wire[:]) // Some(305419896)
    @fixed.read_u32(wire[:], at=10) // None, the view is shorter

    // One number, in each protocol's own spelling.
    @quic.encode(1337UL) // 45 39
    @prefix.encode(1337, prefix=5) // 1f 9a 0a
    @leb128.encode(1337UL) // b9 0a
    @lenenc.encode(1337UL) // fc 39 05

    #Packages

    EncodingWritten by
    fixedByte order for u8…u64, i8…i64, f32, f64, the 24-bit length protocols use, and a width carried in a variableEverything
    quicTwo bits name the width, sixty-two carry the value — RFC 9000 §16QUIC, HTTP/3
    prefixA value in the low N bits, continuing when it does not fit — RFC 7541 §5.1HPACK, QPACK
    leb128Seven bits an octet, least significant first, plus zigzag for signed valuesprotobuf, DWARF
    lenencOne octet below 251, otherwise a marker and two, three or eight little-endian octetsMySQL, MariaDB

    Each package is separate and carries no dependency, so wanting protobuf's varint does not mean linking QUIC's.

    #The same number, five ways

    1337, as each package writes it:

    fixedquicprefix(5)leb128lenenc
    05 3945 391f 9a 0ab9 0afc 39 05

    Two octets, two, three, two, three — and each one is the length some wire already expects. quic spends two bits on the width; prefix spends five on being a value that shares an octet with flags; leb128 spends one bit per octet on saying "another follows"; lenenc spends a whole octet on a marker; fixed spends nothing and pays for it with width.

    #Reading is half of it

    • Nothing read past the end. Every fixed reader checks the width it needs and answers None when the view is short. The variable-length readers raise a Refused that names the fault: Truncated, or past the ceiling the encoding allows.
    • A width without writing it. Every package has size(): how many octets a value takes, without producing them. A length field has to be measured before the payload it describes is written.
    • Signed values stay short. leb128.zigzag folds a negative onto an unsigned value, so -1 costs one octet rather than ten.

    #Install

    moon add moonbitstack/moonvar

    Then import the package whose wire you speak:

    import { "moonbitstack/moonvar/quic", }

    #Examples

    src/examples/tourThe whole surface in one run: every encoding, both directions, and the octet count each costs
    src/examples/frameA length-prefixed frame — a varint length, then a payload — with the truncated case refused rather than half-read

    moon run src/examples/tour moon run src/examples/frame

    #Licence

    Apache-2.0. See LICENSE.