Skip to main content

weaveffi_abi/
buffer.rs

1//! The WeaveFFI value-buffer protocol: the by-value serialization format
2//! records, rich enums, optionals, lists, maps, and error payloads use to
3//! cross the C ABI.
4//!
5//! A *buffered* value crosses the boundary as one `(const uint8_t*, size_t)`
6//! slot pair containing the value serialized in this module's format, rather
7//! than as an opaque object pointer or parallel arrays. Parameters are
8//! borrowed for the duration of the call (the consumer owns and frees its own
9//! encoding); returns are producer-allocated and released by the consumer
10//! with `weaveffi_free_bytes` after decoding.
11//!
12//! # Encoding
13//!
14//! All multi-byte values are **little-endian**. There is no padding and no
15//! alignment; values are packed back to back.
16//!
17//! | IDL type            | Encoding                                            |
18//! |---------------------|-----------------------------------------------------|
19//! | `bool`              | 1 byte: `0` or `1`                                  |
20//! | `i8`/`u8`           | 1 byte                                              |
21//! | `i16`/`u16`         | 2 bytes                                             |
22//! | `i32`/`u32`         | 4 bytes                                             |
23//! | `i64`/`u64`         | 8 bytes                                             |
24//! | `f32`               | 4 bytes (IEEE 754 bits)                             |
25//! | `f64`               | 8 bytes (IEEE 754 bits)                             |
26//! | enum (C-style)      | `i32` discriminant                                  |
27//! | `handle`/`handle<T>`| `u64`                                               |
28//! | `string`            | `u32` byte length + UTF-8 bytes (no NUL terminator) |
29//! | `bytes`             | `u32` length + raw bytes                            |
30//! | `T?`                | 1 byte flag (`0` absent, `1` present) + value       |
31//! | `[T]`               | `u32` count + each element                          |
32//! | `{K:V}`             | `u32` count + alternating key, value                |
33//! | record              | each field in declaration order                     |
34//! | rich enum           | `i32` tag + active variant's fields in order        |
35//! | error payload       | the matched code's fields in declaration order      |
36//!
37//! Because the format is compositional, arbitrary nesting (`{string:[T?]}`,
38//! records containing records, and so on) works with no per-shape special
39//! cases. Interfaces, iterators, and borrowed views never appear inside a
40//! buffer; validation rejects them in buffered positions.
41//!
42//! Encoded lengths and counts are `u32`, capping any single string, byte
43//! buffer, or collection at `u32::MAX` entries; [`BufferWriter`] panics past
44//! that bound rather than truncating.
45
46/// An error produced while decoding a value buffer.
47///
48/// Consumers treat a decode failure as a producer/consumer contract violation
49/// (both sides are generated from the same IDL), so this surfaces through the
50/// same channel as a producer panic: a trap, not a typed domain error.
51#[derive(Debug, Clone, PartialEq, Eq)]
52pub struct BufferDecodeError {
53    /// What the reader was trying to decode when the buffer ran out or held
54    /// invalid data.
55    pub context: &'static str,
56}
57
58impl std::fmt::Display for BufferDecodeError {
59    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
60        write!(f, "malformed WeaveFFI value buffer: {}", self.context)
61    }
62}
63
64impl std::error::Error for BufferDecodeError {}
65
66/// Serializes values into the WeaveFFI buffer format.
67///
68/// The `#[weaveffi::module]` expansion writes record fields, enum payloads,
69/// collection elements, and error payloads through one of these, then hands
70/// the finished bytes across the ABI (via
71/// [`lower_bytes`](crate::lower_bytes) for returns, or borrowed directly for
72/// callback arguments).
73#[derive(Debug, Default)]
74pub struct BufferWriter {
75    buf: Vec<u8>,
76}
77
78impl BufferWriter {
79    /// Create an empty writer.
80    #[must_use]
81    pub fn new() -> Self {
82        Self::default()
83    }
84
85    /// Consume the writer and return the encoded bytes.
86    #[must_use]
87    pub fn finish(self) -> Vec<u8> {
88        self.buf
89    }
90
91    /// Write a `bool` as one byte (`0` or `1`).
92    pub fn write_bool(&mut self, v: bool) {
93        self.buf.push(u8::from(v));
94    }
95
96    /// Write an `i8`.
97    pub fn write_i8(&mut self, v: i8) {
98        self.buf.extend_from_slice(&v.to_le_bytes());
99    }
100
101    /// Write a `u8`.
102    pub fn write_u8(&mut self, v: u8) {
103        self.buf.push(v);
104    }
105
106    /// Write an `i16` little-endian.
107    pub fn write_i16(&mut self, v: i16) {
108        self.buf.extend_from_slice(&v.to_le_bytes());
109    }
110
111    /// Write a `u16` little-endian.
112    pub fn write_u16(&mut self, v: u16) {
113        self.buf.extend_from_slice(&v.to_le_bytes());
114    }
115
116    /// Write an `i32` little-endian. Also the encoding of C-style enum values
117    /// and rich-enum tags.
118    pub fn write_i32(&mut self, v: i32) {
119        self.buf.extend_from_slice(&v.to_le_bytes());
120    }
121
122    /// Write a `u32` little-endian.
123    pub fn write_u32(&mut self, v: u32) {
124        self.buf.extend_from_slice(&v.to_le_bytes());
125    }
126
127    /// Write an `i64` little-endian.
128    pub fn write_i64(&mut self, v: i64) {
129        self.buf.extend_from_slice(&v.to_le_bytes());
130    }
131
132    /// Write a `u64` little-endian. Also the encoding of handles.
133    pub fn write_u64(&mut self, v: u64) {
134        self.buf.extend_from_slice(&v.to_le_bytes());
135    }
136
137    /// Write an `f32` as its IEEE 754 bits, little-endian.
138    pub fn write_f32(&mut self, v: f32) {
139        self.buf.extend_from_slice(&v.to_le_bytes());
140    }
141
142    /// Write an `f64` as its IEEE 754 bits, little-endian.
143    pub fn write_f64(&mut self, v: f64) {
144        self.buf.extend_from_slice(&v.to_le_bytes());
145    }
146
147    /// Write a length or element count as a `u32`.
148    ///
149    /// # Panics
150    ///
151    /// Panics when `len` exceeds `u32::MAX`; truncating would corrupt the
152    /// stream, and a value that large cannot round-trip through the format.
153    pub fn write_len(&mut self, len: usize) {
154        let len = u32::try_from(len).expect("WeaveFFI buffer length exceeds u32::MAX");
155        self.write_u32(len);
156    }
157
158    /// Write a string as a `u32` byte length followed by its UTF-8 bytes.
159    /// Interior NUL bytes round-trip unchanged (the format is not
160    /// NUL-terminated).
161    pub fn write_string(&mut self, v: &str) {
162        self.write_len(v.len());
163        self.buf.extend_from_slice(v.as_bytes());
164    }
165
166    /// Write a byte buffer as a `u32` length followed by the raw bytes.
167    pub fn write_bytes(&mut self, v: &[u8]) {
168        self.write_len(v.len());
169        self.buf.extend_from_slice(v);
170    }
171
172    /// Write an optional's presence flag: `0` for absent, `1` for present.
173    /// When `present`, the caller writes the inner value next.
174    pub fn write_option_flag(&mut self, present: bool) {
175        self.buf.push(u8::from(present));
176    }
177}
178
179/// Decodes values from the WeaveFFI buffer format.
180///
181/// Every `read_*` method returns [`BufferDecodeError`] when the buffer is
182/// exhausted or holds invalid data, so a malformed buffer can never cause an
183/// out-of-bounds read.
184#[derive(Debug)]
185pub struct BufferReader<'a> {
186    data: &'a [u8],
187    pos: usize,
188}
189
190impl<'a> BufferReader<'a> {
191    /// Wrap an encoded buffer for reading.
192    #[must_use]
193    pub fn new(data: &'a [u8]) -> Self {
194        Self { data, pos: 0 }
195    }
196
197    /// The number of bytes not yet consumed.
198    #[must_use]
199    pub fn remaining(&self) -> usize {
200        self.data.len() - self.pos
201    }
202
203    fn take(&mut self, n: usize, context: &'static str) -> Result<&'a [u8], BufferDecodeError> {
204        if self.remaining() < n {
205            return Err(BufferDecodeError { context });
206        }
207        let slice = &self.data[self.pos..self.pos + n];
208        self.pos += n;
209        Ok(slice)
210    }
211
212    /// Read a `bool`.
213    ///
214    /// # Errors
215    ///
216    /// Returns an error when the buffer is exhausted or the byte is not `0`
217    /// or `1`.
218    pub fn read_bool(&mut self) -> Result<bool, BufferDecodeError> {
219        match self.take(1, "bool")?[0] {
220            0 => Ok(false),
221            1 => Ok(true),
222            _ => Err(BufferDecodeError {
223                context: "bool byte out of range",
224            }),
225        }
226    }
227
228    /// Read an `i8`.
229    ///
230    /// # Errors
231    ///
232    /// Returns an error when the buffer is exhausted.
233    pub fn read_i8(&mut self) -> Result<i8, BufferDecodeError> {
234        Ok(i8::from_le_bytes([self.take(1, "i8")?[0]]))
235    }
236
237    /// Read a `u8`.
238    ///
239    /// # Errors
240    ///
241    /// Returns an error when the buffer is exhausted.
242    pub fn read_u8(&mut self) -> Result<u8, BufferDecodeError> {
243        Ok(self.take(1, "u8")?[0])
244    }
245
246    /// Read an `i16` little-endian.
247    ///
248    /// # Errors
249    ///
250    /// Returns an error when the buffer is exhausted.
251    pub fn read_i16(&mut self) -> Result<i16, BufferDecodeError> {
252        let b = self.take(2, "i16")?;
253        Ok(i16::from_le_bytes([b[0], b[1]]))
254    }
255
256    /// Read a `u16` little-endian.
257    ///
258    /// # Errors
259    ///
260    /// Returns an error when the buffer is exhausted.
261    pub fn read_u16(&mut self) -> Result<u16, BufferDecodeError> {
262        let b = self.take(2, "u16")?;
263        Ok(u16::from_le_bytes([b[0], b[1]]))
264    }
265
266    /// Read an `i32` little-endian. Also decodes C-style enum values and
267    /// rich-enum tags.
268    ///
269    /// # Errors
270    ///
271    /// Returns an error when the buffer is exhausted.
272    pub fn read_i32(&mut self) -> Result<i32, BufferDecodeError> {
273        let b = self.take(4, "i32")?;
274        Ok(i32::from_le_bytes([b[0], b[1], b[2], b[3]]))
275    }
276
277    /// Read a `u32` little-endian.
278    ///
279    /// # Errors
280    ///
281    /// Returns an error when the buffer is exhausted.
282    pub fn read_u32(&mut self) -> Result<u32, BufferDecodeError> {
283        let b = self.take(4, "u32")?;
284        Ok(u32::from_le_bytes([b[0], b[1], b[2], b[3]]))
285    }
286
287    /// Read an `i64` little-endian.
288    ///
289    /// # Errors
290    ///
291    /// Returns an error when the buffer is exhausted.
292    pub fn read_i64(&mut self) -> Result<i64, BufferDecodeError> {
293        let b = self.take(8, "i64")?;
294        Ok(i64::from_le_bytes([
295            b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7],
296        ]))
297    }
298
299    /// Read a `u64` little-endian. Also decodes handles.
300    ///
301    /// # Errors
302    ///
303    /// Returns an error when the buffer is exhausted.
304    pub fn read_u64(&mut self) -> Result<u64, BufferDecodeError> {
305        let b = self.take(8, "u64")?;
306        Ok(u64::from_le_bytes([
307            b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7],
308        ]))
309    }
310
311    /// Read an `f32` from its IEEE 754 bits, little-endian.
312    ///
313    /// # Errors
314    ///
315    /// Returns an error when the buffer is exhausted.
316    pub fn read_f32(&mut self) -> Result<f32, BufferDecodeError> {
317        let b = self.take(4, "f32")?;
318        Ok(f32::from_le_bytes([b[0], b[1], b[2], b[3]]))
319    }
320
321    /// Read an `f64` from its IEEE 754 bits, little-endian.
322    ///
323    /// # Errors
324    ///
325    /// Returns an error when the buffer is exhausted.
326    pub fn read_f64(&mut self) -> Result<f64, BufferDecodeError> {
327        let b = self.take(8, "f64")?;
328        Ok(f64::from_le_bytes([
329            b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7],
330        ]))
331    }
332
333    /// Read a length or element count (a `u32`).
334    ///
335    /// # Errors
336    ///
337    /// Returns an error when the buffer is exhausted or the decoded length
338    /// exceeds the bytes remaining (which would make follow-up reads fail
339    /// anyway; rejecting here gives a clearer error).
340    pub fn read_len(&mut self) -> Result<usize, BufferDecodeError> {
341        let len = self.read_u32()? as usize;
342        // A length can never exceed what is left in the buffer: even the
343        // densest elements occupy at least one byte each.
344        if len > self.remaining() {
345            return Err(BufferDecodeError {
346                context: "length prefix exceeds remaining buffer",
347            });
348        }
349        Ok(len)
350    }
351
352    /// Read a string: `u32` byte length + UTF-8 bytes.
353    ///
354    /// # Errors
355    ///
356    /// Returns an error when the buffer is exhausted or the bytes are not
357    /// valid UTF-8.
358    pub fn read_string(&mut self) -> Result<String, BufferDecodeError> {
359        let len = self.read_len()?;
360        let bytes = self.take(len, "string bytes")?;
361        String::from_utf8(bytes.to_vec()).map_err(|_| BufferDecodeError {
362            context: "string is not valid UTF-8",
363        })
364    }
365
366    /// Read a byte buffer: `u32` length + raw bytes.
367    ///
368    /// # Errors
369    ///
370    /// Returns an error when the buffer is exhausted.
371    pub fn read_bytes(&mut self) -> Result<Vec<u8>, BufferDecodeError> {
372        let len = self.read_len()?;
373        Ok(self.take(len, "byte buffer")?.to_vec())
374    }
375
376    /// Read an optional's presence flag.
377    ///
378    /// # Errors
379    ///
380    /// Returns an error when the buffer is exhausted or the flag byte is not
381    /// `0` or `1`.
382    pub fn read_option_flag(&mut self) -> Result<bool, BufferDecodeError> {
383        match self.take(1, "option flag")?[0] {
384            0 => Ok(false),
385            1 => Ok(true),
386            _ => Err(BufferDecodeError {
387                context: "option flag byte out of range",
388            }),
389        }
390    }
391
392    /// Assert the whole buffer was consumed. Called after decoding a complete
393    /// value to catch trailing garbage.
394    ///
395    /// # Errors
396    ///
397    /// Returns an error when unconsumed bytes remain.
398    pub fn expect_end(&self) -> Result<(), BufferDecodeError> {
399        if self.remaining() != 0 {
400            return Err(BufferDecodeError {
401                context: "trailing bytes after value",
402            });
403        }
404        Ok(())
405    }
406}
407
408/// A value that can serialize itself into (and decode itself from) the
409/// WeaveFFI buffer format.
410///
411/// The `#[weaveffi::record]`, `#[weaveffi::enumeration]`, and
412/// `#[weaveffi::error]` expansions implement this for annotated types, and
413/// blanket implementations below cover primitives, `String`, `Vec<u8>`,
414/// `Option<T>`, `Vec<T>`, and the map types, so nested composites compose
415/// automatically.
416pub trait BufferValue: Sized {
417    /// Append this value's encoding to `w`.
418    fn write_value(&self, w: &mut BufferWriter);
419
420    /// Decode one value of this type from `r`.
421    ///
422    /// # Errors
423    ///
424    /// Returns an error when the buffer is exhausted or holds invalid data
425    /// for this type.
426    fn read_value(r: &mut BufferReader<'_>) -> Result<Self, BufferDecodeError>;
427}
428
429macro_rules! scalar_buffer_value {
430    ($($t:ty => ($write:ident, $read:ident)),* $(,)?) => {
431        $(
432            impl BufferValue for $t {
433                fn write_value(&self, w: &mut BufferWriter) {
434                    w.$write(*self);
435                }
436                fn read_value(r: &mut BufferReader<'_>) -> Result<Self, BufferDecodeError> {
437                    r.$read()
438                }
439            }
440        )*
441    };
442}
443
444scalar_buffer_value! {
445    bool => (write_bool, read_bool),
446    i8 => (write_i8, read_i8),
447    u8 => (write_u8, read_u8),
448    i16 => (write_i16, read_i16),
449    u16 => (write_u16, read_u16),
450    i32 => (write_i32, read_i32),
451    u32 => (write_u32, read_u32),
452    i64 => (write_i64, read_i64),
453    u64 => (write_u64, read_u64),
454    f32 => (write_f32, read_f32),
455    f64 => (write_f64, read_f64),
456}
457
458impl BufferValue for String {
459    fn write_value(&self, w: &mut BufferWriter) {
460        w.write_string(self);
461    }
462    fn read_value(r: &mut BufferReader<'_>) -> Result<Self, BufferDecodeError> {
463        r.read_string()
464    }
465}
466
467impl<T: BufferValue> BufferValue for Option<T> {
468    fn write_value(&self, w: &mut BufferWriter) {
469        match self {
470            Some(v) => {
471                w.write_option_flag(true);
472                v.write_value(w);
473            }
474            None => w.write_option_flag(false),
475        }
476    }
477    fn read_value(r: &mut BufferReader<'_>) -> Result<Self, BufferDecodeError> {
478        if r.read_option_flag()? {
479            Ok(Some(T::read_value(r)?))
480        } else {
481            Ok(None)
482        }
483    }
484}
485
486impl<T: BufferValue> BufferValue for Vec<T> {
487    fn write_value(&self, w: &mut BufferWriter) {
488        w.write_len(self.len());
489        for item in self {
490            item.write_value(w);
491        }
492    }
493    fn read_value(r: &mut BufferReader<'_>) -> Result<Self, BufferDecodeError> {
494        let len = r.read_len()?;
495        let mut out = Vec::with_capacity(len.min(r.remaining()));
496        for _ in 0..len {
497            out.push(T::read_value(r)?);
498        }
499        Ok(out)
500    }
501}
502
503impl<K: BufferValue + Ord, V: BufferValue> BufferValue for std::collections::BTreeMap<K, V> {
504    fn write_value(&self, w: &mut BufferWriter) {
505        w.write_len(self.len());
506        for (k, v) in self {
507            k.write_value(w);
508            v.write_value(w);
509        }
510    }
511    fn read_value(r: &mut BufferReader<'_>) -> Result<Self, BufferDecodeError> {
512        let len = r.read_len()?;
513        let mut out = Self::new();
514        for _ in 0..len {
515            let k = K::read_value(r)?;
516            let v = V::read_value(r)?;
517            out.insert(k, v);
518        }
519        Ok(out)
520    }
521}
522
523impl<K: BufferValue + std::hash::Hash + Eq, V: BufferValue> BufferValue
524    for std::collections::HashMap<K, V>
525{
526    fn write_value(&self, w: &mut BufferWriter) {
527        w.write_len(self.len());
528        for (k, v) in self {
529            k.write_value(w);
530            v.write_value(w);
531        }
532    }
533    fn read_value(r: &mut BufferReader<'_>) -> Result<Self, BufferDecodeError> {
534        let len = r.read_len()?;
535        let mut out = Self::with_capacity(len.min(r.remaining()));
536        for _ in 0..len {
537            let k = K::read_value(r)?;
538            let v = V::read_value(r)?;
539            out.insert(k, v);
540        }
541        Ok(out)
542    }
543}
544
545/// Encode one [`BufferValue`] into a fresh byte buffer.
546#[must_use]
547pub fn encode_value<T: BufferValue>(value: &T) -> Vec<u8> {
548    let mut w = BufferWriter::new();
549    value.write_value(&mut w);
550    w.finish()
551}
552
553/// Decode one [`BufferValue`] from an encoded buffer, requiring the buffer to
554/// be fully consumed.
555///
556/// # Errors
557///
558/// Returns an error when the buffer is malformed for `T` or holds trailing
559/// bytes.
560pub fn decode_value<T: BufferValue>(data: &[u8]) -> Result<T, BufferDecodeError> {
561    let mut r = BufferReader::new(data);
562    let value = T::read_value(&mut r)?;
563    r.expect_end()?;
564    Ok(value)
565}
566
567#[cfg(test)]
568mod tests {
569    use super::*;
570    use std::collections::BTreeMap;
571
572    fn roundtrip<T: BufferValue + PartialEq + std::fmt::Debug>(value: T) {
573        let bytes = encode_value(&value);
574        let back: T = decode_value(&bytes).unwrap();
575        assert_eq!(back, value);
576    }
577
578    #[test]
579    fn scalars_roundtrip() {
580        roundtrip(true);
581        roundtrip(false);
582        roundtrip(-5i8);
583        roundtrip(200u8);
584        roundtrip(-1234i16);
585        roundtrip(54321u16);
586        roundtrip(-7i32);
587        roundtrip(4_000_000_000u32);
588        roundtrip(i64::MIN);
589        roundtrip(u64::MAX);
590        roundtrip(1.5f32);
591        roundtrip(-2.25f64);
592    }
593
594    #[test]
595    fn strings_roundtrip_including_interior_nul() {
596        roundtrip(String::new());
597        roundtrip("hello".to_string());
598        roundtrip("emoji \u{1F980} and\0nul".to_string());
599    }
600
601    #[test]
602    fn options_roundtrip() {
603        roundtrip::<Option<i32>>(None);
604        roundtrip(Some(42i32));
605        roundtrip(Some("text".to_string()));
606        roundtrip::<Option<Option<i64>>>(Some(None));
607        roundtrip::<Option<Option<i64>>>(Some(Some(9)));
608    }
609
610    #[test]
611    fn collections_roundtrip() {
612        roundtrip(vec![1u8, 2, 3]);
613        roundtrip(vec!["a".to_string(), String::new(), "ccc".to_string()]);
614        roundtrip(vec![vec![1i32, 2], vec![], vec![3]]);
615        roundtrip(vec![Some(1i32), None, Some(3)]);
616        let mut m = BTreeMap::new();
617        m.insert("a".to_string(), vec![1i64, 2]);
618        m.insert("b".to_string(), vec![]);
619        roundtrip(m);
620    }
621
622    #[test]
623    fn known_byte_layout() {
624        // Lock the wire format: [count=2][len=1]'a'[len=0] for `["a", ""]`.
625        let bytes = encode_value(&vec!["a".to_string(), String::new()]);
626        assert_eq!(bytes, [2, 0, 0, 0, 1, 0, 0, 0, b'a', 0, 0, 0, 0].as_slice());
627    }
628
629    #[test]
630    fn truncated_buffer_is_rejected() {
631        let bytes = encode_value(&"hello".to_string());
632        let err = decode_value::<String>(&bytes[..bytes.len() - 1]).unwrap_err();
633        assert!(err.to_string().contains("malformed"));
634    }
635
636    #[test]
637    fn trailing_bytes_are_rejected() {
638        let mut bytes = encode_value(&7i32);
639        bytes.push(0);
640        assert!(decode_value::<i32>(&bytes).is_err());
641    }
642
643    #[test]
644    fn hostile_length_prefix_is_rejected() {
645        // A length claiming more elements than bytes remain must fail fast
646        // instead of attempting a huge allocation.
647        let bytes = [0xFF, 0xFF, 0xFF, 0xFF];
648        assert!(decode_value::<Vec<u8>>(&bytes).is_err());
649    }
650
651    #[test]
652    fn invalid_bool_and_flag_bytes_are_rejected() {
653        assert!(decode_value::<bool>(&[2]).is_err());
654        assert!(decode_value::<Option<i32>>(&[9]).is_err());
655    }
656
657    #[test]
658    fn invalid_utf8_is_rejected() {
659        let bytes = [2, 0, 0, 0, 0xFF, 0xFE];
660        assert!(decode_value::<String>(&bytes).is_err());
661    }
662}