moonframing

    Backend-neutral incremental byte-stream framing for MoonBit

    streaming
    framing
    codec
    protocol
    Download zip
    Version
    0.2.0
    License
    Apache-2.0
    Last updated
    last month
    Downloads
    34

    No dependencies

    #MoonFraming

    MoonFraming is a pure-MoonBit framing toolkit for protocols that run over arbitrary byte streams. It separates message boundaries from transport I/O, handles fragmented and coalesced input, enforces resource limits before payload allocation, and can attach CRC-32 corruption detection.

    #Highlights

    • Unsigned LEB128 length prefixes with a five-byte bound
    • One-shot and incremental decoders
    • Correct handling of one-byte fragments and multiple frames per chunk
    • Configurable maximum frame and buffered-input sizes
    • Explicit truncated, malformed, oversized, and checksum failures
    • Optional IEEE CRC-32 protected frames
    • Decoder reset at application-defined resynchronization boundaries
    • Cross-backend CI for Wasm, Wasm-GC, and JavaScript

    #Run

    moon test --deny-warn moon run cmd/main

    #Library example

    ///|
    test {
    let decoder = @moonframing.Decoder::new(max_frame_size=4096)
    let wire = @moonframing.encode_frame(b"hello")
    match decoder.feed(wire) {
    Ok(frames) => assert_true(frames[0] == b"hello")
    Err(_) => fail("valid frame")
    }
    }

    #Protocol

    The base wire format is uLEB128(payload_length) || payload. A checked frame uses uLEB128(payload_length + 4) || payload || crc32_be. The length prefix is limited to five bytes and the decoder checks the configured payload limit as soon as the prefix is complete.

    MoonFraming is not a socket or filesystem library. It is designed to compose with MoonBit async readers, files, WebSockets, RPC transports, and parsers without forcing any particular I/O runtime.

    Licensed under Apache-2.0.

    ByteOrder

    pub enum ByteOrder {
    BigEndian
    LittleEndian
    }

    Byte order used by fixed-width length fields.

    CheckedDecoder

    pub struct CheckedDecoder {
    inner : Decoder
    }

    Incremental decoder for CRC-32 protected frames.

    CheckedDecoder::buffered

    fn CheckedDecoder::buffered(self : CheckedDecoder) -> Int

    CheckedDecoder::feed

    fn CheckedDecoder::feed(self : CheckedDecoder, chunk : Bytes) -> Result[Array[Bytes], FrameError]

    CheckedDecoder::finish

    fn CheckedDecoder::finish(self : CheckedDecoder) -> Result[Unit, FrameError]

    CheckedDecoder::new

    fn CheckedDecoder::new(max_frame_size? : Int, max_buffer_size? : Int) -> CheckedDecoder

    CheckedDecoder::reset

    fn CheckedDecoder::reset(self : CheckedDecoder) -> Unit

    Decoder

    pub struct Decoder {
    buffer : Array[Byte]
    max_frame_size : Int
    max_buffer_size : Int
    header_value : Array[Int]
    header_shift : Array[Int]
    header_bytes : Array[Int]
    frame_header_bytes : Array[Int]
    expected_length : Array[Int]
    }

    Stateful decoder accepting arbitrarily fragmented byte chunks.

    Decoder::buffered

    fn Decoder::buffered(self : Decoder) -> Int

    Number of bytes retained while waiting for a complete frame.

    Decoder::feed

    fn Decoder::feed(self : Decoder, chunk : Bytes) -> Result[Array[Bytes], FrameError]

    Feed any chunk size and return every complete frame now available.

    Decoder::finish

    fn Decoder::finish(self : Decoder) -> Result[Unit, FrameError]

    Signal end-of-input. A partial header or payload is an explicit error.

    Decoder::new

    fn Decoder::new(max_frame_size? : Int, max_buffer_size? : Int) -> Decoder

    Decoder::reset

    fn Decoder::reset(self : Decoder) -> Unit

    Discard partial data so a caller can recover at a known boundary.

    DelimiterDecoder

    pub struct DelimiterDecoder {
    buffer : Array[Byte]
    delimiter : Bytes
    strip_delimiter : Bool
    max_frame_size : Int
    max_buffer_size : Int
    }

    Stateful framing for line protocols and other delimiter-terminated streams.

    DelimiterDecoder::buffered

    fn DelimiterDecoder::buffered(self : DelimiterDecoder) -> Int

    DelimiterDecoder::encode

    fn DelimiterDecoder::encode(self : DelimiterDecoder, payload : Bytes) -> Result[Bytes, FrameError]

    Encode one delimiter-terminated item.

    DelimiterDecoder::feed

    fn DelimiterDecoder::feed(self : DelimiterDecoder, chunk : Bytes) -> Result[Array[Bytes], FrameError]

    Feed arbitrary chunks. Multi-byte delimiters may span chunk boundaries.

    DelimiterDecoder::finish

    fn DelimiterDecoder::finish(self : DelimiterDecoder) -> Result[Unit, FrameError]

    DelimiterDecoder::new

    fn DelimiterDecoder::new(delimiter : Bytes, strip_delimiter? : Bool, max_frame_size? : Int, max_buffer_size? : Int) -> Result[DelimiterDecoder, FrameError]

    DelimiterDecoder::reset

    fn DelimiterDecoder::reset(self : DelimiterDecoder) -> Unit

    FrameError

    pub enum FrameError {
    NeedMore
    MalformedVarint
    InvalidConfiguration(String)
    LengthFieldOverflow(Int, Int)
    InvalidLengthAdjustment(Int)
    FrameTooLarge(Int, Int)
    MissingChecksum
    ChecksumMismatch(UInt, UInt)
    BufferLimitExceeded(Int, Int)
    }

    Deterministic failures returned by frame parsing.

    LengthDelimitedCodec

    pub struct LengthDelimitedCodec {
    buffer : Array[Byte]
    length_field_offset : Int
    length_field_length : Int
    length_adjustment : Int
    num_skip : Int
    byte_order : ByteOrder
    max_frame_size : Int
    max_buffer_size : Int
    }

    A configurable fixed-width length-delimited codec.

    Its framing rules mirror the useful configuration points of mature length-delimited codecs: field width, field offset, byte order, signed adjustment, bytes skipped before the returned payload, and resource limits.

    LengthDelimitedCodec::buffered

    fn LengthDelimitedCodec::buffered(self : LengthDelimitedCodec) -> Int

    LengthDelimitedCodec::encode

    fn LengthDelimitedCodec::encode(self : LengthDelimitedCodec, payload : Bytes) -> Result[Bytes, FrameError]

    Encode one item using this codec's header layout.

    LengthDelimitedCodec::feed

    fn LengthDelimitedCodec::feed(self : LengthDelimitedCodec, chunk : Bytes) -> Result[Array[Bytes], FrameError]

    Feed a fragmented or coalesced stream and return every complete item.

    LengthDelimitedCodec::finish

    fn LengthDelimitedCodec::finish(self : LengthDelimitedCodec) -> Result[Unit, FrameError]

    LengthDelimitedCodec::new

    fn LengthDelimitedCodec::new(length_field_offset? : Int, length_field_length? : Int, length_adjustment? : Int, num_skip? : Int, byte_order? : ByteOrder, max_frame_size? : Int, max_buffer_size? : Int) -> Result[LengthDelimitedCodec, FrameError]

    Construct a fixed-width codec. Field widths from one through four bytes are supported. Invalid configurations are rejected before any input is read.

    LengthDelimitedCodec::reset

    fn LengthDelimitedCodec::reset(self : LengthDelimitedCodec) -> Unit

    big_endian

    fn big_endian() -> ByteOrder

    crc32

    fn crc32(data : Bytes) -> UInt

    IEEE CRC-32 for optional corruption detection on framed transports.

    decode_all

    fn decode_all(data : Bytes, max_frame_size? : Int) -> Result[Array[Bytes], FrameError]

    Decode an entire finite stream and reject trailing partial data.

    decode_checked_frame

    fn decode_checked_frame(data : Bytes, max_frame_size? : Int) -> Result[(Bytes, Int), FrameError]

    Decode and verify one checksummed frame.

    decode_frame

    fn decode_frame(data : Bytes, max_frame_size? : Int) -> Result[(Bytes, Int), FrameError]

    Decode one complete frame and report total bytes consumed.

    decode_varint

    fn decode_varint(data : Bytes, offset? : Int) -> (Int, Int)?

    Decode one unsigned LEB128 value, returning value and bytes consumed.

    encode_checked_frame

    fn encode_checked_frame(payload : Bytes) -> Bytes

    Encode payload plus a big-endian CRC-32 inside the length envelope.

    encode_frame

    fn encode_frame(payload : Bytes) -> Bytes

    Prefix a payload with its unsigned LEB128 byte length.

    encode_frames

    fn encode_frames(payloads : ArrayView[Bytes]) -> Bytes

    Encode several payloads as one concatenated framed stream.

    encode_varint

    fn encode_varint(value : Int) -> Bytes

    Encode a non-negative length as unsigned LEB128 (maximum 31 bits).

    little_endian

    fn little_endian() -> ByteOrder