cyclone-runtime-rust 1.0.1

Rust reference runtime for the Cyclone binary wire format: Writer, Reader, DecodeError.
Documentation
//! Encoding side of the runtime.

/// Appends Cyclone-encoded values to a growable byte buffer.
///
/// The writer performs no validation: the generated codec decides *what* to
/// write, the writer decides only *what shape those bytes have*. Every
/// multi-byte value is written Little Endian, with no padding, no alignment,
/// and no metadata between values (RFC-0002 §1).
///
/// ```
/// use cyclone_runtime::Writer;
///
/// let mut w = Writer::new();
/// w.write_u32(42);
/// w.write_string("Sword");
/// assert_eq!(
///     w.as_slice(),
///     &[0x2A, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, b'S', b'w', b'o', b'r', b'd'],
/// );
/// ```
#[derive(Debug, Clone, Default)]
pub struct Writer {
    buf: Vec<u8>,
}

impl Writer {
    /// Creates an empty writer.
    #[inline]
    pub fn new() -> Self {
        Writer { buf: Vec::new() }
    }

    /// Creates an empty writer with room for `capacity` bytes.
    ///
    /// Cyclone values have sizes that are known from the schema, so a caller
    /// that knows the message size can encode without a single reallocation.
    #[inline]
    pub fn with_capacity(capacity: usize) -> Self {
        Writer { buf: Vec::with_capacity(capacity) }
    }

    /// Returns the bytes written so far.
    #[inline]
    pub fn as_slice(&self) -> &[u8] {
        &self.buf
    }

    /// Consumes the writer and returns the bytes written.
    #[inline]
    pub fn into_bytes(self) -> Vec<u8> {
        self.buf
    }

    /// Returns the number of bytes written so far.
    #[inline]
    pub fn len(&self) -> usize {
        self.buf.len()
    }

    /// Returns `true` if nothing has been written yet.
    #[inline]
    pub fn is_empty(&self) -> bool {
        self.buf.is_empty()
    }

    /// Discards everything written, keeping the allocated capacity for reuse.
    #[inline]
    pub fn clear(&mut self) {
        self.buf.clear();
    }

    // ---------------------------------------------------------------- bool

    /// Writes a `bool` as one byte: `0x00` for false, `0x01` for true.
    ///
    /// No other byte is ever emitted for a `bool` (RFC-0002 §2.4).
    #[inline]
    pub fn write_bool(&mut self, value: bool) {
        self.buf.push(u8::from(value));
    }

    // ------------------------------------------------------------ integers

    /// Writes an `i8` as 1 byte.
    #[inline]
    pub fn write_i8(&mut self, value: i8) {
        self.buf.push(value as u8);
    }

    /// Writes a `u8` as 1 byte.
    #[inline]
    pub fn write_u8(&mut self, value: u8) {
        self.buf.push(value);
    }

    /// Writes an `i16` as 2 bytes, Little Endian.
    #[inline]
    pub fn write_i16(&mut self, value: i16) {
        self.buf.extend_from_slice(&value.to_le_bytes());
    }

    /// Writes a `u16` as 2 bytes, Little Endian.
    #[inline]
    pub fn write_u16(&mut self, value: u16) {
        self.buf.extend_from_slice(&value.to_le_bytes());
    }

    /// Writes an `i32` as 4 bytes, Little Endian.
    #[inline]
    pub fn write_i32(&mut self, value: i32) {
        self.buf.extend_from_slice(&value.to_le_bytes());
    }

    /// Writes a `u32` as 4 bytes, Little Endian.
    #[inline]
    pub fn write_u32(&mut self, value: u32) {
        self.buf.extend_from_slice(&value.to_le_bytes());
    }

    /// Writes an `i64` as 8 bytes, Little Endian.
    #[inline]
    pub fn write_i64(&mut self, value: i64) {
        self.buf.extend_from_slice(&value.to_le_bytes());
    }

    /// Writes a `u64` as 8 bytes, Little Endian.
    #[inline]
    pub fn write_u64(&mut self, value: u64) {
        self.buf.extend_from_slice(&value.to_le_bytes());
    }

    // -------------------------------------------------------------- floats

    /// Writes an `f32` as its raw IEEE 754 bit pattern, 4 bytes Little Endian.
    ///
    /// The bit pattern is written unmodified: `NaN` payloads are preserved,
    /// `-0.0` stays distinct from `0.0`, and infinities are written as-is
    /// (RFC-0002 §2.3). No canonicalization is performed.
    #[inline]
    pub fn write_f32(&mut self, value: f32) {
        self.buf.extend_from_slice(&value.to_bits().to_le_bytes());
    }

    /// Writes an `f64` as its raw IEEE 754 bit pattern, 8 bytes Little Endian.
    ///
    /// Same rule as [`write_f32`](Self::write_f32): the bits are preserved
    /// exactly, never normalized.
    #[inline]
    pub fn write_f64(&mut self, value: f64) {
        self.buf.extend_from_slice(&value.to_bits().to_le_bytes());
    }

    // ------------------------------------------------------------- complex

    /// Writes a string as a `u32` UTF-8 **byte** length followed by those bytes.
    ///
    /// The length counts bytes, not characters (RFC-0002 §3).
    ///
    /// # Panics
    ///
    /// Panics if the string is longer than `u32::MAX` bytes, which the wire
    /// format cannot represent.
    #[inline]
    pub fn write_string(&mut self, value: &str) {
        self.write_len(value.len());
        self.buf.extend_from_slice(value.as_bytes());
    }

    /// Writes a byte blob as a `u32` length followed by the raw bytes.
    ///
    /// Identical to [`write_string`](Self::write_string) minus the UTF-8
    /// constraint (RFC-0002 §4).
    ///
    /// # Panics
    ///
    /// Panics if the blob is longer than `u32::MAX` bytes, which the wire
    /// format cannot represent.
    #[inline]
    pub fn write_bytes(&mut self, value: &[u8]) {
        self.write_len(value.len());
        self.buf.extend_from_slice(value);
    }

    /// Writes the element count of an array as a `u32` (RFC-0002 §6).
    ///
    /// The elements themselves are written by the generated codec, which is
    /// the only party that knows their type.
    ///
    /// # Panics
    ///
    /// Panics if `count` exceeds `u32::MAX`.
    #[inline]
    pub fn write_array_count(&mut self, count: usize) {
        self.write_len(count);
    }

    /// Writes a `usize` length prefix as a `u32`.
    ///
    /// A length above `u32::MAX` has no representation in the wire format, so
    /// truncating it would emit a byte stream that silently describes the wrong
    /// data. This is an encoder bug in the caller, not a decodable condition,
    /// so it panics rather than producing corrupt bytes.
    #[inline]
    fn write_len(&mut self, len: usize) {
        let len = u32::try_from(len)
            .expect("cyclone: length exceeds u32::MAX and cannot be represented on the wire");
        self.write_u32(len);
    }
}