Skip to main content

cyclone_runtime/
writer.rs

1//! Encoding side of the runtime.
2
3/// Appends Cyclone-encoded values to a growable byte buffer.
4///
5/// The writer performs no validation: the generated codec decides *what* to
6/// write, the writer decides only *what shape those bytes have*. Every
7/// multi-byte value is written Little Endian, with no padding, no alignment,
8/// and no metadata between values (RFC-0002 §1).
9///
10/// ```
11/// use cyclone_runtime::Writer;
12///
13/// let mut w = Writer::new();
14/// w.write_u32(42);
15/// w.write_string("Sword");
16/// assert_eq!(
17///     w.as_slice(),
18///     &[0x2A, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, b'S', b'w', b'o', b'r', b'd'],
19/// );
20/// ```
21#[derive(Debug, Clone, Default)]
22pub struct Writer {
23    buf: Vec<u8>,
24}
25
26impl Writer {
27    /// Creates an empty writer.
28    #[inline]
29    pub fn new() -> Self {
30        Writer { buf: Vec::new() }
31    }
32
33    /// Creates an empty writer with room for `capacity` bytes.
34    ///
35    /// Cyclone values have sizes that are known from the schema, so a caller
36    /// that knows the message size can encode without a single reallocation.
37    #[inline]
38    pub fn with_capacity(capacity: usize) -> Self {
39        Writer { buf: Vec::with_capacity(capacity) }
40    }
41
42    /// Returns the bytes written so far.
43    #[inline]
44    pub fn as_slice(&self) -> &[u8] {
45        &self.buf
46    }
47
48    /// Consumes the writer and returns the bytes written.
49    #[inline]
50    pub fn into_bytes(self) -> Vec<u8> {
51        self.buf
52    }
53
54    /// Returns the number of bytes written so far.
55    #[inline]
56    pub fn len(&self) -> usize {
57        self.buf.len()
58    }
59
60    /// Returns `true` if nothing has been written yet.
61    #[inline]
62    pub fn is_empty(&self) -> bool {
63        self.buf.is_empty()
64    }
65
66    /// Discards everything written, keeping the allocated capacity for reuse.
67    #[inline]
68    pub fn clear(&mut self) {
69        self.buf.clear();
70    }
71
72    // ---------------------------------------------------------------- bool
73
74    /// Writes a `bool` as one byte: `0x00` for false, `0x01` for true.
75    ///
76    /// No other byte is ever emitted for a `bool` (RFC-0002 §2.4).
77    #[inline]
78    pub fn write_bool(&mut self, value: bool) {
79        self.buf.push(u8::from(value));
80    }
81
82    // ------------------------------------------------------------ integers
83
84    /// Writes an `i8` as 1 byte.
85    #[inline]
86    pub fn write_i8(&mut self, value: i8) {
87        self.buf.push(value as u8);
88    }
89
90    /// Writes a `u8` as 1 byte.
91    #[inline]
92    pub fn write_u8(&mut self, value: u8) {
93        self.buf.push(value);
94    }
95
96    /// Writes an `i16` as 2 bytes, Little Endian.
97    #[inline]
98    pub fn write_i16(&mut self, value: i16) {
99        self.buf.extend_from_slice(&value.to_le_bytes());
100    }
101
102    /// Writes a `u16` as 2 bytes, Little Endian.
103    #[inline]
104    pub fn write_u16(&mut self, value: u16) {
105        self.buf.extend_from_slice(&value.to_le_bytes());
106    }
107
108    /// Writes an `i32` as 4 bytes, Little Endian.
109    #[inline]
110    pub fn write_i32(&mut self, value: i32) {
111        self.buf.extend_from_slice(&value.to_le_bytes());
112    }
113
114    /// Writes a `u32` as 4 bytes, Little Endian.
115    #[inline]
116    pub fn write_u32(&mut self, value: u32) {
117        self.buf.extend_from_slice(&value.to_le_bytes());
118    }
119
120    /// Writes an `i64` as 8 bytes, Little Endian.
121    #[inline]
122    pub fn write_i64(&mut self, value: i64) {
123        self.buf.extend_from_slice(&value.to_le_bytes());
124    }
125
126    /// Writes a `u64` as 8 bytes, Little Endian.
127    #[inline]
128    pub fn write_u64(&mut self, value: u64) {
129        self.buf.extend_from_slice(&value.to_le_bytes());
130    }
131
132    // -------------------------------------------------------------- floats
133
134    /// Writes an `f32` as its raw IEEE 754 bit pattern, 4 bytes Little Endian.
135    ///
136    /// The bit pattern is written unmodified: `NaN` payloads are preserved,
137    /// `-0.0` stays distinct from `0.0`, and infinities are written as-is
138    /// (RFC-0002 §2.3). No canonicalization is performed.
139    #[inline]
140    pub fn write_f32(&mut self, value: f32) {
141        self.buf.extend_from_slice(&value.to_bits().to_le_bytes());
142    }
143
144    /// Writes an `f64` as its raw IEEE 754 bit pattern, 8 bytes Little Endian.
145    ///
146    /// Same rule as [`write_f32`](Self::write_f32): the bits are preserved
147    /// exactly, never normalized.
148    #[inline]
149    pub fn write_f64(&mut self, value: f64) {
150        self.buf.extend_from_slice(&value.to_bits().to_le_bytes());
151    }
152
153    // ------------------------------------------------------------- complex
154
155    /// Writes a string as a `u32` UTF-8 **byte** length followed by those bytes.
156    ///
157    /// The length counts bytes, not characters (RFC-0002 §3).
158    ///
159    /// # Panics
160    ///
161    /// Panics if the string is longer than `u32::MAX` bytes, which the wire
162    /// format cannot represent.
163    #[inline]
164    pub fn write_string(&mut self, value: &str) {
165        self.write_len(value.len());
166        self.buf.extend_from_slice(value.as_bytes());
167    }
168
169    /// Writes a byte blob as a `u32` length followed by the raw bytes.
170    ///
171    /// Identical to [`write_string`](Self::write_string) minus the UTF-8
172    /// constraint (RFC-0002 §4).
173    ///
174    /// # Panics
175    ///
176    /// Panics if the blob is longer than `u32::MAX` bytes, which the wire
177    /// format cannot represent.
178    #[inline]
179    pub fn write_bytes(&mut self, value: &[u8]) {
180        self.write_len(value.len());
181        self.buf.extend_from_slice(value);
182    }
183
184    /// Writes the element count of an array as a `u32` (RFC-0002 §6).
185    ///
186    /// The elements themselves are written by the generated codec, which is
187    /// the only party that knows their type.
188    ///
189    /// # Panics
190    ///
191    /// Panics if `count` exceeds `u32::MAX`.
192    #[inline]
193    pub fn write_array_count(&mut self, count: usize) {
194        self.write_len(count);
195    }
196
197    /// Writes a `usize` length prefix as a `u32`.
198    ///
199    /// A length above `u32::MAX` has no representation in the wire format, so
200    /// truncating it would emit a byte stream that silently describes the wrong
201    /// data. This is an encoder bug in the caller, not a decodable condition,
202    /// so it panics rather than producing corrupt bytes.
203    #[inline]
204    fn write_len(&mut self, len: usize) {
205        let len = u32::try_from(len)
206            .expect("cyclone: length exceeds u32::MAX and cannot be represented on the wire");
207        self.write_u32(len);
208    }
209}