Skip to main content

Crate cyclone_runtime

Crate cyclone_runtime 

Source
Expand description

Rust reference runtime for the Cyclone binary wire format.

Cyclone is a wire format specification: the same logical value must produce exactly one byte sequence, in every language and every implementation. This crate is the smallest layer that turns primitive values into those bytes and back.

User Model  →  Cyclone Compiler / Derive  →  Generated Codec  →  cyclone-runtime  →  Bytes

§What this crate does

  • Writer - appends primitives, strings and byte blobs to a buffer.
  • Reader - reads them back, rejecting malformed input.
  • Limits - allocation guards for untrusted input.
  • DecodeError - every way a byte stream can fail to conform.
  • Encode / Decode - the traits a codec implements.
  • to_bytes / from_bytes - the two calls that use those traits.

A codec reaches these traits from either direction, and the runtime cannot tell which: #[derive(Network)] from cyclone-codegen-rust writes an impl in place, the Cyclone CLI writes one into a *.codec.rs file you own and may edit, and a hand-written impl is just as valid.

There are no blanket impls of Encode / Decode for u32, String, Vec<T> or any other Rust type. Which Cyclone type a value has is schema knowledge - Vec<T> is an Array<T> only because a schema said so - and a runtime that guessed it from the Rust type would be deciding the wire format by inference. It writes what a codec tells it to write, nothing more.

§What this crate does not do

It does not parse schemas, read annotations, generate codecs, or hold any registry or type information. It contains no proc macro and no reflection. Composite types - models, arrays, enums - are the generated codec’s job; the runtime only knows bytes. Two consequences worth stating plainly:

  • There is no write_model / read_model. A model is written by writing its fields in declaration order, with nothing in between (RFC-0002 §5).
  • There is no InvalidEnum error. Which u32 values an enum admits is schema knowledge, so the generated codec validates it.

§Format at a glance

TypeBytes
bool1 - 0x00 or 0x01, nothing else
i8 / u81
i16 / u162, Little Endian
i32 / u324, Little Endian
i64 / u648, Little Endian
f32 / f644 / 8 - raw IEEE 754 bits, never normalized
Stringu32 UTF-8 byte length, then the bytes
Bytesu32 length, then the raw bytes
Array<T>u32 element count, then each element
Enumalways u32
Modelits fields concatenated in declaration order

No varint, no padding, no alignment, no tag id, no object header.

§Example

Encoding the model Item { id: u32, name: String }, the way a generated codec would:

use cyclone_runtime::{from_bytes, to_bytes, Decode, DecodeError, Encode, Reader, Writer};

struct Item {
    id: u32,
    name: String,
}

impl Encode for Item {
    fn encode(&self, writer: &mut Writer) {
        writer.write_u32(self.id);
        writer.write_string(&self.name);
    }
}

impl Decode for Item {
    fn decode(reader: &mut Reader<'_>) -> Result<Self, DecodeError> {
        Ok(Item {
            id: reader.read_u32()?,
            name: reader.read_string()?,
        })
    }
}

let bytes = to_bytes(&Item { id: 42, name: "Sword".to_owned() });
assert_eq!(
    bytes,
    [0x2A, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x53, 0x77, 0x6F, 0x72, 0x64],
);

let item = from_bytes::<Item>(&bytes)?;
assert_eq!(item.id, 42);
assert_eq!(item.name, "Sword");

§References

  • RFC-0001 - what Cyclone is
  • RFC-0002 - the wire format specification
  • RFC-0003 - conformance test vectors

Structs§

Limits
Allocation guards applied while decoding.
Reader
Reads Cyclone-encoded values from a borrowed byte buffer.
Writer
Appends Cyclone-encoded values to a growable byte buffer.

Enums§

DecodeError
A byte stream that does not satisfy the Cyclone Specification.

Traits§

Decode
A type that can be read back from Cyclone bytes.
Encode
A type that can be written as Cyclone bytes.

Functions§

from_bytes
Decodes a T from bytes.
to_bytes
Encodes value into a fresh Vec<u8>.