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}