Skip to main content

packr_abi/
lib.rs

1//! Graph-encoded ABI for Pack runtime
2//!
3//! This crate provides encoding and decoding for recursive data types
4//! using a graph-based binary format. It's `no_std` compatible for use
5//! in WASM packages.
6//!
7//! # Derive Macros
8//!
9//! Enable the `derive` feature to use `#[derive(GraphValue)]`:
10//!
11//! ```ignore
12//! use packr_abi::{GraphValue, Value};
13//!
14//! #[derive(GraphValue)]
15//! struct Point {
16//!     x: i64,
17//!     y: i64,
18//! }
19//!
20//! let point = Point { x: 10, y: 20 };
21//! let value: Value = point.into();
22//! let back: Point = value.try_into().unwrap();
23//! ```
24
25#![cfg_attr(not(feature = "std"), no_std)]
26
27extern crate alloc;
28
29mod hash;
30mod parse;
31mod value;
32
33pub use hash::{
34    hash_function,
35    hash_interface,
36    // Compound hash functions
37    hash_list,
38    hash_option,
39    hash_record,
40    hash_result,
41    hash_tuple,
42    hash_variant,
43    Binding,
44    TypeHash,
45    TypeHasher,
46    // Primitive hashes
47    HASH_BOOL,
48    HASH_CHAR,
49    HASH_F32,
50    HASH_F64,
51    HASH_FLAGS,
52    HASH_S16,
53    HASH_S32,
54    HASH_S64,
55    HASH_S8,
56    HASH_SELF_REF,
57    HASH_STRING,
58    HASH_U16,
59    HASH_U32,
60    HASH_U64,
61    HASH_U8,
62};
63pub use parse::{parse_value, ParseError};
64pub use value::{FromValue, KnownValueType, Value, ValueType};
65
66// Re-export derive macro when feature is enabled
67#[cfg(feature = "derive")]
68pub use packr_derive::GraphValue;
69
70// ============================================================================
71// Conversion Error
72// ============================================================================
73
74use alloc::boxed::Box;
75
76/// Error type for Value conversions
77#[derive(Debug, Clone)]
78pub enum ConversionError {
79    /// Type mismatch during conversion
80    TypeMismatch { expected: String, got: String },
81    /// Missing field in record
82    MissingField(String),
83    /// Missing index in tuple/list
84    MissingIndex(usize),
85    /// Wrong number of fields
86    WrongFieldCount { expected: usize, got: usize },
87    /// Expected a record value
88    ExpectedRecord(String),
89    /// Expected a tuple value
90    ExpectedTuple(String),
91    /// Expected a variant value
92    ExpectedVariant(String),
93    /// Expected a list value
94    ExpectedList(String),
95    /// Expected an option value
96    ExpectedOption(String),
97    /// Unknown variant tag
98    UnknownTag { tag: usize, max: usize },
99    /// Missing payload for variant
100    MissingPayload,
101    /// Unexpected payload for unit variant
102    UnexpectedPayload,
103    /// Error in field conversion
104    FieldError(String, Box<ConversionError>),
105    /// Error in index conversion
106    IndexError(usize, Box<ConversionError>),
107    /// Error in payload conversion
108    PayloadError(Box<ConversionError>),
109}
110
111impl core::fmt::Display for ConversionError {
112    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
113        match self {
114            Self::TypeMismatch { expected, got } => {
115                write!(f, "type mismatch: expected {}, got {}", expected, got)
116            }
117            Self::MissingField(name) => write!(f, "missing field: {}", name),
118            Self::MissingIndex(idx) => write!(f, "missing index: {}", idx),
119            Self::WrongFieldCount { expected, got } => {
120                write!(f, "wrong field count: expected {}, got {}", expected, got)
121            }
122            Self::ExpectedRecord(got) => write!(f, "expected record, got {}", got),
123            Self::ExpectedTuple(got) => write!(f, "expected tuple, got {}", got),
124            Self::ExpectedVariant(got) => write!(f, "expected variant, got {}", got),
125            Self::ExpectedList(got) => write!(f, "expected list, got {}", got),
126            Self::ExpectedOption(got) => write!(f, "expected option, got {}", got),
127            Self::UnknownTag { tag, max } => {
128                write!(f, "unknown variant tag {} (max {})", tag, max)
129            }
130            Self::MissingPayload => write!(f, "missing payload for variant"),
131            Self::UnexpectedPayload => write!(f, "unexpected payload for unit variant"),
132            Self::FieldError(name, e) => write!(f, "field '{}': {}", name, e),
133            Self::IndexError(idx, e) => write!(f, "index {}: {}", idx, e),
134            Self::PayloadError(e) => write!(f, "payload: {}", e),
135        }
136    }
137}
138
139// ============================================================================
140// Private module for derive macro internals
141// ============================================================================
142
143/// Private module used by the derive macro. Not part of the public API.
144#[doc(hidden)]
145pub mod __private {
146    pub use alloc::boxed::Box;
147    pub use alloc::format;
148    pub use alloc::string::String;
149    pub use alloc::vec;
150    pub use alloc::vec::Vec;
151    pub use core::convert::From;
152    pub use core::convert::TryFrom;
153    pub use core::option::Option::{self, None, Some};
154    pub use core::result::Result;
155    pub use core::result::Result::{Err, Ok};
156}
157
158use alloc::format;
159use alloc::string::String;
160use alloc::vec;
161use alloc::vec::Vec;
162use hashbrown::{HashMap, HashSet};
163
164#[derive(Debug, Clone)]
165pub enum AbiError {
166    TypeMismatch { expected: String, got: String },
167    InvalidEncoding(String),
168    BufferTooSmall { need: usize, have: usize },
169    InvalidTag(u8),
170}
171
172const MAGIC: u32 = u32::from_le_bytes(*b"CGRF");
173const VERSION: u16 = 2;
174
175#[derive(Debug, Clone, Copy)]
176pub struct Limits {
177    pub max_buffer_size: usize,
178    pub max_node_count: usize,
179    pub max_payload_size: usize,
180    pub max_sequence_len: usize,
181}
182
183impl Default for Limits {
184    fn default() -> Self {
185        Self {
186            max_buffer_size: 16 * 1024 * 1024,
187            max_node_count: 1_000_000,
188            max_payload_size: 8 * 1024 * 1024,
189            max_sequence_len: 1_000_000,
190        }
191    }
192}
193
194#[derive(Debug, Clone, Copy, PartialEq, Eq)]
195pub enum NodeKind {
196    Bool = 0x01,
197    S32 = 0x02,
198    S64 = 0x03,
199    F32 = 0x04,
200    F64 = 0x05,
201    String = 0x06,
202    List = 0x07,
203    Variant = 0x08,
204    Record = 0x09,
205    Option = 0x0A,
206    Tuple = 0x0B,
207    U8 = 0x0C,
208    U16 = 0x0D,
209    U32 = 0x0E,
210    U64 = 0x0F,
211    S8 = 0x10,
212    S16 = 0x11,
213    Char = 0x12,
214    Flags = 0x13,
215    Result = 0x14,
216    Array = 0x15,
217}
218
219#[derive(Debug, Clone)]
220pub struct Node {
221    pub kind: NodeKind,
222    pub payload: Vec<u8>,
223}
224
225#[derive(Debug, Clone)]
226pub struct GraphBuffer {
227    pub nodes: Vec<Node>,
228    pub root: u32,
229}
230
231pub struct Encoder {
232    nodes: Vec<Node>,
233}
234
235impl Encoder {
236    pub fn new() -> Self {
237        Self { nodes: Vec::new() }
238    }
239
240    pub fn push_node(&mut self, node: Node) -> u32 {
241        let index = self.nodes.len() as u32;
242        self.nodes.push(node);
243        index
244    }
245
246    pub fn finish(self, root: u32) -> GraphBuffer {
247        GraphBuffer {
248            nodes: self.nodes,
249            root,
250        }
251    }
252}
253
254impl Default for Encoder {
255    fn default() -> Self {
256        Self::new()
257    }
258}
259
260pub struct Decoder<'a> {
261    pub buffer: &'a GraphBuffer,
262}
263
264impl<'a> Decoder<'a> {
265    pub fn new(buffer: &'a GraphBuffer) -> Self {
266        Self { buffer }
267    }
268
269    pub fn node(&self, index: u32) -> Option<&'a Node> {
270        self.buffer.nodes.get(index as usize)
271    }
272}
273
274pub trait GraphCodec {
275    fn encode_graph(&self, encoder: &mut Encoder) -> Result<u32, AbiError>;
276    fn decode_graph(decoder: &Decoder<'_>, root: u32) -> Result<Self, AbiError>
277    where
278        Self: Sized;
279}
280
281/// Encode a value to bytes (graph-encoded ABI)
282pub fn encode(value: &Value) -> Result<Vec<u8>, AbiError> {
283    let mut encoder = Encoder::new();
284    let root = value.encode_graph(&mut encoder)?;
285    let buffer = encoder.finish(root);
286    Ok(buffer.to_bytes())
287}
288
289/// Decode bytes to a value (graph-encoded ABI)
290pub fn decode(bytes: &[u8]) -> Result<Value, AbiError> {
291    let limits = Limits::default();
292    decode_with_limits(bytes, &limits)
293}
294
295/// Decode bytes to a value with custom limits
296pub fn decode_with_limits(bytes: &[u8], limits: &Limits) -> Result<Value, AbiError> {
297    let buffer = GraphBuffer::from_bytes_with_limits(bytes, limits)?;
298    buffer.validate_basic_with_limits(limits)?;
299    let decoder = Decoder::new(&buffer);
300    Value::decode_graph(&decoder, buffer.root)
301}
302
303impl GraphBuffer {
304    pub fn to_bytes(&self) -> Vec<u8> {
305        let mut out = Vec::new();
306        out.extend_from_slice(&MAGIC.to_le_bytes());
307        out.extend_from_slice(&VERSION.to_le_bytes());
308        out.extend_from_slice(&0u16.to_le_bytes());
309        out.extend_from_slice(&(self.nodes.len() as u32).to_le_bytes());
310        out.extend_from_slice(&self.root.to_le_bytes());
311
312        for node in &self.nodes {
313            let kind = node.kind as u8;
314            out.push(kind);
315            out.push(0u8);
316            out.extend_from_slice(&0u16.to_le_bytes());
317            out.extend_from_slice(&(node.payload.len() as u32).to_le_bytes());
318            out.extend_from_slice(&node.payload);
319        }
320
321        out
322    }
323
324    pub fn from_bytes(bytes: &[u8]) -> Result<Self, AbiError> {
325        let limits = Limits::default();
326        Self::from_bytes_with_limits(bytes, &limits)
327    }
328
329    pub fn from_bytes_with_limits(bytes: &[u8], limits: &Limits) -> Result<Self, AbiError> {
330        if bytes.len() > limits.max_buffer_size {
331            return Err(AbiError::InvalidEncoding(String::from("Buffer too large")));
332        }
333
334        let mut cursor = Cursor::new(bytes);
335        let magic = cursor.read_u32()?;
336        if magic != MAGIC {
337            return Err(AbiError::InvalidEncoding(String::from("Invalid magic")));
338        }
339
340        let version = cursor.read_u16()?;
341        if version != VERSION {
342            return Err(AbiError::InvalidEncoding(String::from(
343                "Unsupported version",
344            )));
345        }
346
347        let _flags = cursor.read_u16()?;
348        let node_count = cursor.read_u32()? as usize;
349        if node_count > limits.max_node_count {
350            return Err(AbiError::InvalidEncoding(String::from(
351                "Node count exceeds limit",
352            )));
353        }
354        let root = cursor.read_u32()?;
355
356        let mut nodes = Vec::with_capacity(node_count);
357        for _ in 0..node_count {
358            let kind = node_kind_from_u8(cursor.read_u8()?)?;
359            let _node_flags = cursor.read_u8()?;
360            let _reserved = cursor.read_u16()?;
361            let payload_len = cursor.read_u32()? as usize;
362            if payload_len > limits.max_payload_size {
363                return Err(AbiError::InvalidEncoding(String::from("Payload too large")));
364            }
365            let payload = cursor.read_bytes(payload_len)?.to_vec();
366            nodes.push(Node { kind, payload });
367        }
368
369        if (root as usize) >= nodes.len() {
370            return Err(AbiError::InvalidEncoding(String::from(
371                "Root index out of range",
372            )));
373        }
374
375        if !cursor.is_eof() {
376            return Err(AbiError::InvalidEncoding(String::from("Trailing bytes")));
377        }
378
379        Ok(Self { nodes, root })
380    }
381
382    pub fn validate_basic(&self) -> Result<(), AbiError> {
383        let limits = Limits::default();
384        self.validate_basic_with_limits(&limits)
385    }
386
387    pub fn validate_basic_with_limits(&self, limits: &Limits) -> Result<(), AbiError> {
388        let node_count = self.nodes.len();
389        if (self.root as usize) >= node_count {
390            return Err(AbiError::InvalidEncoding(String::from(
391                "Root index out of range",
392            )));
393        }
394        if node_count > limits.max_node_count {
395            return Err(AbiError::InvalidEncoding(String::from(
396                "Node count exceeds limit",
397            )));
398        }
399
400        for (index, node) in self.nodes.iter().enumerate() {
401            if node.payload.len() > limits.max_payload_size {
402                return Err(AbiError::InvalidEncoding(format!(
403                    "Payload too large at node {index}"
404                )));
405            }
406            let mut cursor = Cursor::new(&node.payload);
407            match node.kind {
408                NodeKind::Bool => {
409                    let value = cursor.read_u8()?;
410                    if value > 1 {
411                        return Err(AbiError::InvalidEncoding(format!(
412                            "Invalid bool payload at node {index}"
413                        )));
414                    }
415                }
416                NodeKind::S32 | NodeKind::F32 | NodeKind::U32 => {
417                    cursor.read_bytes(4)?;
418                }
419                NodeKind::S64 | NodeKind::F64 | NodeKind::U64 => {
420                    cursor.read_bytes(8)?;
421                }
422                NodeKind::U8 | NodeKind::S8 => {
423                    cursor.read_bytes(1)?;
424                }
425                NodeKind::U16 | NodeKind::S16 => {
426                    cursor.read_bytes(2)?;
427                }
428                NodeKind::Char => {
429                    let value = cursor.read_u32()?;
430                    if char::from_u32(value).is_none() {
431                        return Err(AbiError::InvalidEncoding(format!(
432                            "Invalid char scalar at node {index}"
433                        )));
434                    }
435                }
436                NodeKind::Flags => {
437                    cursor.read_bytes(8)?;
438                }
439                NodeKind::String => {
440                    let len = cursor.read_u32()? as usize;
441                    let bytes = cursor.read_bytes(len)?;
442                    if core::str::from_utf8(bytes).is_err() {
443                        return Err(AbiError::InvalidEncoding(format!(
444                            "Invalid UTF-8 string at node {index}"
445                        )));
446                    }
447                }
448                NodeKind::Tuple => {
449                    // Tuple format unchanged: [count:u32, child_indices:u32*]
450                    let count = cursor.read_u32()? as usize;
451                    if count > limits.max_sequence_len {
452                        return Err(AbiError::InvalidEncoding(format!(
453                            "Sequence too large at node {index}"
454                        )));
455                    }
456                    for _ in 0..count {
457                        let child = cursor.read_u32()? as usize;
458                        if child >= node_count {
459                            return Err(AbiError::InvalidEncoding(format!(
460                                "Child index out of range at node {index}"
461                            )));
462                        }
463                    }
464                }
465                // v2 format nodes with variable-length headers - skip detailed validation
466                // The actual decode will catch any format errors
467                NodeKind::Array => {
468                    // Array: [elem_type:u8, count:u32, data:u8*]
469                    let elem_type = decode_value_type(&mut cursor)?;
470                    let width = fixed_width(&elem_type).ok_or_else(|| {
471                        AbiError::InvalidEncoding(format!(
472                            "Non-primitive element type in Array at node {index}"
473                        ))
474                    })?;
475                    let count = cursor.read_u32()? as usize;
476                    if count > limits.max_sequence_len {
477                        return Err(AbiError::InvalidEncoding(format!(
478                            "Sequence too large at node {index}"
479                        )));
480                    }
481                    cursor.read_bytes(count * width)?;
482                }
483                NodeKind::List
484                | NodeKind::Option
485                | NodeKind::Record
486                | NodeKind::Variant
487                | NodeKind::Result => {
488                    // These have variable-length type tags or string headers
489                    // Skip detailed validation, just ensure payload is not too large (already checked)
490                }
491            }
492
493            // Don't check for trailing bytes on v2 nodes with variable headers
494            if !matches!(
495                node.kind,
496                NodeKind::List
497                    | NodeKind::Option
498                    | NodeKind::Record
499                    | NodeKind::Variant
500                    | NodeKind::Result
501            ) && !cursor.is_eof()
502            {
503                return Err(AbiError::InvalidEncoding(format!(
504                    "Trailing payload bytes at node {index}"
505                )));
506            }
507        }
508
509        Ok(())
510    }
511}
512
513struct Cursor<'a> {
514    bytes: &'a [u8],
515    pos: usize,
516}
517
518impl<'a> Cursor<'a> {
519    fn new(bytes: &'a [u8]) -> Self {
520        Self { bytes, pos: 0 }
521    }
522
523    fn is_eof(&self) -> bool {
524        self.pos >= self.bytes.len()
525    }
526
527    fn read_bytes(&mut self, len: usize) -> Result<&'a [u8], AbiError> {
528        if self.pos + len > self.bytes.len() {
529            return Err(AbiError::BufferTooSmall {
530                need: self.pos + len,
531                have: self.bytes.len(),
532            });
533        }
534        let start = self.pos;
535        self.pos += len;
536        Ok(&self.bytes[start..self.pos])
537    }
538
539    fn read_u8(&mut self) -> Result<u8, AbiError> {
540        let bytes = self.read_bytes(1)?;
541        Ok(bytes[0])
542    }
543
544    fn read_u16(&mut self) -> Result<u16, AbiError> {
545        let bytes = self.read_bytes(2)?;
546        Ok(u16::from_le_bytes([bytes[0], bytes[1]]))
547    }
548
549    fn read_u32(&mut self) -> Result<u32, AbiError> {
550        let bytes = self.read_bytes(4)?;
551        Ok(u32::from_le_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]))
552    }
553
554    fn read_u64(&mut self) -> Result<u64, AbiError> {
555        let bytes = self.read_bytes(8)?;
556        Ok(u64::from_le_bytes([
557            bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], bytes[7],
558        ]))
559    }
560}
561
562fn node_kind_from_u8(value: u8) -> Result<NodeKind, AbiError> {
563    match value {
564        0x01 => Ok(NodeKind::Bool),
565        0x02 => Ok(NodeKind::S32),
566        0x03 => Ok(NodeKind::S64),
567        0x04 => Ok(NodeKind::F32),
568        0x05 => Ok(NodeKind::F64),
569        0x06 => Ok(NodeKind::String),
570        0x07 => Ok(NodeKind::List),
571        0x08 => Ok(NodeKind::Variant),
572        0x09 => Ok(NodeKind::Record),
573        0x0A => Ok(NodeKind::Option),
574        0x0B => Ok(NodeKind::Tuple),
575        0x0C => Ok(NodeKind::U8),
576        0x0D => Ok(NodeKind::U16),
577        0x0E => Ok(NodeKind::U32),
578        0x0F => Ok(NodeKind::U64),
579        0x10 => Ok(NodeKind::S8),
580        0x11 => Ok(NodeKind::S16),
581        0x12 => Ok(NodeKind::Char),
582        0x13 => Ok(NodeKind::Flags),
583        0x14 => Ok(NodeKind::Result),
584        0x15 => Ok(NodeKind::Array),
585        _ => Err(AbiError::InvalidTag(value)),
586    }
587}
588
589// Type tag bytes for CGRF v2 type encoding
590const TYPE_BOOL: u8 = 0x01;
591const TYPE_S32: u8 = 0x02;
592const TYPE_S64: u8 = 0x03;
593const TYPE_F32: u8 = 0x04;
594const TYPE_F64: u8 = 0x05;
595const TYPE_STRING: u8 = 0x06;
596const TYPE_LIST: u8 = 0x07;
597const TYPE_VARIANT: u8 = 0x08;
598const TYPE_RECORD: u8 = 0x09;
599const TYPE_OPTION: u8 = 0x0A;
600const TYPE_TUPLE: u8 = 0x0B;
601const TYPE_U8: u8 = 0x0C;
602const TYPE_U16: u8 = 0x0D;
603const TYPE_U32: u8 = 0x0E;
604const TYPE_U64: u8 = 0x0F;
605const TYPE_S8: u8 = 0x10;
606const TYPE_S16: u8 = 0x11;
607const TYPE_CHAR: u8 = 0x12;
608const TYPE_FLAGS: u8 = 0x13;
609const TYPE_RESULT: u8 = 0x14;
610
611/// Returns the byte width of a fixed-size primitive ValueType, or None for compound types.
612fn fixed_width(ty: &ValueType) -> Option<usize> {
613    match ty {
614        ValueType::Bool | ValueType::U8 | ValueType::S8 => Some(1),
615        ValueType::U16 | ValueType::S16 => Some(2),
616        ValueType::U32 | ValueType::S32 | ValueType::F32 | ValueType::Char => Some(4),
617        ValueType::U64 | ValueType::S64 | ValueType::F64 | ValueType::Flags => Some(8),
618        _ => None,
619    }
620}
621
622/// Encode a single primitive value into a contiguous byte buffer (for Array nodes).
623fn encode_array_element(
624    value: &Value,
625    expected: &ValueType,
626    out: &mut Vec<u8>,
627) -> Result<(), AbiError> {
628    match (value, expected) {
629        (Value::Bool(v), ValueType::Bool) => out.push(if *v { 1 } else { 0 }),
630        (Value::U8(v), ValueType::U8) => out.push(*v),
631        (Value::S8(v), ValueType::S8) => out.push(*v as u8),
632        (Value::U16(v), ValueType::U16) => out.extend_from_slice(&v.to_le_bytes()),
633        (Value::S16(v), ValueType::S16) => out.extend_from_slice(&v.to_le_bytes()),
634        (Value::U32(v), ValueType::U32) => out.extend_from_slice(&v.to_le_bytes()),
635        (Value::S32(v), ValueType::S32) => out.extend_from_slice(&v.to_le_bytes()),
636        (Value::U64(v), ValueType::U64) => out.extend_from_slice(&v.to_le_bytes()),
637        (Value::S64(v), ValueType::S64) => out.extend_from_slice(&v.to_le_bytes()),
638        (Value::F32(v), ValueType::F32) => out.extend_from_slice(&v.to_le_bytes()),
639        (Value::F64(v), ValueType::F64) => out.extend_from_slice(&v.to_le_bytes()),
640        (Value::Char(v), ValueType::Char) => out.extend_from_slice(&(*v as u32).to_le_bytes()),
641        (Value::Flags(v), ValueType::Flags) => out.extend_from_slice(&v.to_le_bytes()),
642        _ => {
643            return Err(AbiError::InvalidEncoding(String::from(
644                "Array element type mismatch",
645            )))
646        }
647    }
648    Ok(())
649}
650
651/// Decode a single primitive value from a cursor (for Array nodes).
652fn decode_array_element(cursor: &mut Cursor<'_>, elem_type: &ValueType) -> Result<Value, AbiError> {
653    Ok(match elem_type {
654        ValueType::Bool => Value::Bool(cursor.read_u8()? != 0),
655        ValueType::U8 => Value::U8(cursor.read_u8()?),
656        ValueType::S8 => Value::S8(cursor.read_u8()? as i8),
657        ValueType::U16 => Value::U16(cursor.read_u16()?),
658        ValueType::S16 => Value::S16(cursor.read_u16()? as i16),
659        ValueType::U32 => Value::U32(cursor.read_u32()?),
660        ValueType::S32 => Value::S32(cursor.read_u32()? as i32),
661        ValueType::U64 => Value::U64(cursor.read_u64()?),
662        ValueType::S64 => Value::S64(cursor.read_u64()? as i64),
663        ValueType::F32 => {
664            let raw = cursor.read_u32()?;
665            Value::F32(f32::from_le_bytes(raw.to_le_bytes()))
666        }
667        ValueType::F64 => {
668            let raw = cursor.read_u64()?;
669            Value::F64(f64::from_le_bytes(raw.to_le_bytes()))
670        }
671        ValueType::Char => {
672            let raw = cursor.read_u32()?;
673            Value::Char(
674                char::from_u32(raw).ok_or_else(|| {
675                    AbiError::InvalidEncoding(String::from("Invalid char in array"))
676                })?,
677            )
678        }
679        ValueType::Flags => Value::Flags(cursor.read_u64()?),
680        _ => {
681            return Err(AbiError::InvalidEncoding(String::from(
682                "Non-primitive type in array",
683            )))
684        }
685    })
686}
687
688/// Encode a ValueType to bytes
689fn encode_value_type(ty: &ValueType, out: &mut Vec<u8>) {
690    match ty {
691        ValueType::Bool => out.push(TYPE_BOOL),
692        ValueType::U8 => out.push(TYPE_U8),
693        ValueType::U16 => out.push(TYPE_U16),
694        ValueType::U32 => out.push(TYPE_U32),
695        ValueType::U64 => out.push(TYPE_U64),
696        ValueType::S8 => out.push(TYPE_S8),
697        ValueType::S16 => out.push(TYPE_S16),
698        ValueType::S32 => out.push(TYPE_S32),
699        ValueType::S64 => out.push(TYPE_S64),
700        ValueType::F32 => out.push(TYPE_F32),
701        ValueType::F64 => out.push(TYPE_F64),
702        ValueType::Char => out.push(TYPE_CHAR),
703        ValueType::String => out.push(TYPE_STRING),
704        ValueType::Flags => out.push(TYPE_FLAGS),
705        ValueType::List(elem) => {
706            out.push(TYPE_LIST);
707            encode_value_type(elem, out);
708        }
709        ValueType::Option(inner) => {
710            out.push(TYPE_OPTION);
711            encode_value_type(inner, out);
712        }
713        ValueType::Result { ok, err } => {
714            out.push(TYPE_RESULT);
715            encode_value_type(ok, out);
716            encode_value_type(err, out);
717        }
718        ValueType::Record(name) => {
719            out.push(TYPE_RECORD);
720            let bytes = name.as_bytes();
721            out.extend_from_slice(&(bytes.len() as u32).to_le_bytes());
722            out.extend_from_slice(bytes);
723        }
724        ValueType::Variant(name) => {
725            out.push(TYPE_VARIANT);
726            let bytes = name.as_bytes();
727            out.extend_from_slice(&(bytes.len() as u32).to_le_bytes());
728            out.extend_from_slice(bytes);
729        }
730        ValueType::Tuple(elems) => {
731            out.push(TYPE_TUPLE);
732            out.extend_from_slice(&(elems.len() as u32).to_le_bytes());
733            for elem in elems {
734                encode_value_type(elem, out);
735            }
736        }
737    }
738}
739
740/// Decode a ValueType from a cursor
741fn decode_value_type(cursor: &mut Cursor<'_>) -> Result<ValueType, AbiError> {
742    let tag = cursor.read_u8()?;
743    match tag {
744        TYPE_BOOL => Ok(ValueType::Bool),
745        TYPE_U8 => Ok(ValueType::U8),
746        TYPE_U16 => Ok(ValueType::U16),
747        TYPE_U32 => Ok(ValueType::U32),
748        TYPE_U64 => Ok(ValueType::U64),
749        TYPE_S8 => Ok(ValueType::S8),
750        TYPE_S16 => Ok(ValueType::S16),
751        TYPE_S32 => Ok(ValueType::S32),
752        TYPE_S64 => Ok(ValueType::S64),
753        TYPE_F32 => Ok(ValueType::F32),
754        TYPE_F64 => Ok(ValueType::F64),
755        TYPE_CHAR => Ok(ValueType::Char),
756        TYPE_STRING => Ok(ValueType::String),
757        TYPE_FLAGS => Ok(ValueType::Flags),
758        TYPE_LIST => {
759            let elem = decode_value_type(cursor)?;
760            Ok(ValueType::List(Box::new(elem)))
761        }
762        TYPE_OPTION => {
763            let inner = decode_value_type(cursor)?;
764            Ok(ValueType::Option(Box::new(inner)))
765        }
766        TYPE_RESULT => {
767            let ok = decode_value_type(cursor)?;
768            let err = decode_value_type(cursor)?;
769            Ok(ValueType::Result {
770                ok: Box::new(ok),
771                err: Box::new(err),
772            })
773        }
774        TYPE_RECORD => {
775            let len = cursor.read_u32()? as usize;
776            let bytes = cursor.read_bytes(len)?;
777            let name = core::str::from_utf8(bytes).map_err(|_| {
778                AbiError::InvalidEncoding(String::from("Invalid UTF-8 in type name"))
779            })?;
780            Ok(ValueType::Record(String::from(name)))
781        }
782        TYPE_VARIANT => {
783            let len = cursor.read_u32()? as usize;
784            let bytes = cursor.read_bytes(len)?;
785            let name = core::str::from_utf8(bytes).map_err(|_| {
786                AbiError::InvalidEncoding(String::from("Invalid UTF-8 in type name"))
787            })?;
788            Ok(ValueType::Variant(String::from(name)))
789        }
790        TYPE_TUPLE => {
791            let count = cursor.read_u32()? as usize;
792            let mut elems = Vec::with_capacity(count);
793            for _ in 0..count {
794                elems.push(decode_value_type(cursor)?);
795            }
796            Ok(ValueType::Tuple(elems))
797        }
798        _ => Err(AbiError::InvalidTag(tag)),
799    }
800}
801
802impl GraphCodec for Value {
803    fn encode_graph(&self, encoder: &mut Encoder) -> Result<u32, AbiError> {
804        match self {
805            Value::Bool(value) => Ok(encoder.push_node(Node {
806                kind: NodeKind::Bool,
807                payload: vec![u8::from(*value)],
808            })),
809            Value::U8(value) => Ok(encoder.push_node(Node {
810                kind: NodeKind::U8,
811                payload: vec![*value],
812            })),
813            Value::U16(value) => Ok(encoder.push_node(Node {
814                kind: NodeKind::U16,
815                payload: value.to_le_bytes().to_vec(),
816            })),
817            Value::U32(value) => Ok(encoder.push_node(Node {
818                kind: NodeKind::U32,
819                payload: value.to_le_bytes().to_vec(),
820            })),
821            Value::U64(value) => Ok(encoder.push_node(Node {
822                kind: NodeKind::U64,
823                payload: value.to_le_bytes().to_vec(),
824            })),
825            Value::S8(value) => Ok(encoder.push_node(Node {
826                kind: NodeKind::S8,
827                payload: value.to_le_bytes().to_vec(),
828            })),
829            Value::S16(value) => Ok(encoder.push_node(Node {
830                kind: NodeKind::S16,
831                payload: value.to_le_bytes().to_vec(),
832            })),
833            Value::S32(value) => Ok(encoder.push_node(Node {
834                kind: NodeKind::S32,
835                payload: value.to_le_bytes().to_vec(),
836            })),
837            Value::S64(value) => Ok(encoder.push_node(Node {
838                kind: NodeKind::S64,
839                payload: value.to_le_bytes().to_vec(),
840            })),
841            Value::F32(value) => Ok(encoder.push_node(Node {
842                kind: NodeKind::F32,
843                payload: value.to_le_bytes().to_vec(),
844            })),
845            Value::F64(value) => Ok(encoder.push_node(Node {
846                kind: NodeKind::F64,
847                payload: value.to_le_bytes().to_vec(),
848            })),
849            Value::Char(value) => Ok(encoder.push_node(Node {
850                kind: NodeKind::Char,
851                payload: (*value as u32).to_le_bytes().to_vec(),
852            })),
853            Value::Flags(mask) => Ok(encoder.push_node(Node {
854                kind: NodeKind::Flags,
855                payload: mask.to_le_bytes().to_vec(),
856            })),
857            Value::String(value) => {
858                let bytes = value.as_bytes();
859                let mut payload = Vec::with_capacity(4 + bytes.len());
860                payload.extend_from_slice(&(bytes.len() as u32).to_le_bytes());
861                payload.extend_from_slice(bytes);
862                Ok(encoder.push_node(Node {
863                    kind: NodeKind::String,
864                    payload,
865                }))
866            }
867            Value::List { elem_type, items }
868                if fixed_width(elem_type).is_some()
869                    && items.iter().all(|v| v.infer_type() == *elem_type) =>
870            {
871                // Array: contiguous encoding for fixed-size primitive lists
872                // Format: [elem_type:u8, count:u32, data:u8*]
873                let width = fixed_width(elem_type).unwrap();
874                let mut payload = Vec::with_capacity(1 + 4 + items.len() * width);
875                encode_value_type(elem_type, &mut payload);
876                payload.extend_from_slice(&(items.len() as u32).to_le_bytes());
877                for item in items {
878                    encode_array_element(item, elem_type, &mut payload)?;
879                }
880                Ok(encoder.push_node(Node {
881                    kind: NodeKind::Array,
882                    payload,
883                }))
884            }
885            Value::List { elem_type, items } => {
886                // Primitive elem_types must use Array encoding — reject mismatched items
887                if fixed_width(elem_type).is_some() {
888                    return Err(AbiError::InvalidEncoding(String::from(
889                        "List with primitive elem_type contains non-matching items; \
890                         fix the elem_type or the items",
891                    )));
892                }
893                // Encode children first
894                let mut child_indices = Vec::with_capacity(items.len());
895                for item in items {
896                    child_indices.push(item.encode_graph(encoder)?);
897                }
898                // v2 format: [elem_type:type_tag*, count:u32, child_indices:u32*]
899                let mut payload = Vec::new();
900                encode_value_type(elem_type, &mut payload);
901                payload.extend_from_slice(&(child_indices.len() as u32).to_le_bytes());
902                for child in child_indices {
903                    payload.extend_from_slice(&child.to_le_bytes());
904                }
905                Ok(encoder.push_node(Node {
906                    kind: NodeKind::List,
907                    payload,
908                }))
909            }
910            Value::Tuple(items) => {
911                let mut child_indices = Vec::with_capacity(items.len());
912                for item in items {
913                    child_indices.push(item.encode_graph(encoder)?);
914                }
915                let mut payload = Vec::with_capacity(4 + 4 * child_indices.len());
916                payload.extend_from_slice(&(child_indices.len() as u32).to_le_bytes());
917                for child in child_indices {
918                    payload.extend_from_slice(&child.to_le_bytes());
919                }
920                Ok(encoder.push_node(Node {
921                    kind: NodeKind::Tuple,
922                    payload,
923                }))
924            }
925            Value::Option { inner_type, value } => {
926                // v2 format: [inner_type:type_tag*, presence:u8, child_index?:u32]
927                let mut payload = Vec::new();
928                encode_value_type(inner_type, &mut payload);
929                if let Some(inner) = value {
930                    payload.push(1);
931                    let child = inner.encode_graph(encoder)?;
932                    payload.extend_from_slice(&child.to_le_bytes());
933                } else {
934                    payload.push(0);
935                }
936                Ok(encoder.push_node(Node {
937                    kind: NodeKind::Option,
938                    payload,
939                }))
940            }
941            Value::Result {
942                ok_type,
943                err_type,
944                value,
945            } => {
946                // v2 format: [ok_type:type_tag*, err_type:type_tag*, tag:u32, has_payload:u8, child_index?:u32]
947                let mut payload = Vec::new();
948                encode_value_type(ok_type, &mut payload);
949                encode_value_type(err_type, &mut payload);
950                match value {
951                    Ok(inner) => {
952                        payload.extend_from_slice(&0u32.to_le_bytes()); // tag 0 = ok
953                        payload.push(1);
954                        let child = inner.encode_graph(encoder)?;
955                        payload.extend_from_slice(&child.to_le_bytes());
956                    }
957                    Err(inner) => {
958                        payload.extend_from_slice(&1u32.to_le_bytes()); // tag 1 = err
959                        payload.push(1);
960                        let child = inner.encode_graph(encoder)?;
961                        payload.extend_from_slice(&child.to_le_bytes());
962                    }
963                }
964                Ok(encoder.push_node(Node {
965                    kind: NodeKind::Result,
966                    payload,
967                }))
968            }
969            Value::Record { type_name, fields } => {
970                // Encode children first
971                let mut child_indices = Vec::with_capacity(fields.len());
972                for (_, value) in fields {
973                    child_indices.push(value.encode_graph(encoder)?);
974                }
975                // v2 format: [type_name_len:u32, type_name:utf8, field_count:u32, field_names:(len:u32, name:utf8)*, child_indices:u32*]
976                let mut payload = Vec::new();
977                let name_bytes = type_name.as_bytes();
978                payload.extend_from_slice(&(name_bytes.len() as u32).to_le_bytes());
979                payload.extend_from_slice(name_bytes);
980                payload.extend_from_slice(&(fields.len() as u32).to_le_bytes());
981                for (name, _) in fields {
982                    let field_name_bytes = name.as_bytes();
983                    payload.extend_from_slice(&(field_name_bytes.len() as u32).to_le_bytes());
984                    payload.extend_from_slice(field_name_bytes);
985                }
986                for child in child_indices {
987                    payload.extend_from_slice(&child.to_le_bytes());
988                }
989                Ok(encoder.push_node(Node {
990                    kind: NodeKind::Record,
991                    payload,
992                }))
993            }
994            Value::Variant {
995                type_name,
996                case_name,
997                tag,
998                payload: var_payload,
999            } => {
1000                // Encode children first
1001                let mut child_indices = Vec::with_capacity(var_payload.len());
1002                for item in var_payload {
1003                    child_indices.push(item.encode_graph(encoder)?);
1004                }
1005                // v2 format: [type_name_len:u32, type_name:utf8, case_name_len:u32, case_name:utf8, tag:u32, payload_count:u32, child_indices:u32*]
1006                let mut payload = Vec::new();
1007                let type_bytes = type_name.as_bytes();
1008                payload.extend_from_slice(&(type_bytes.len() as u32).to_le_bytes());
1009                payload.extend_from_slice(type_bytes);
1010                let case_bytes = case_name.as_bytes();
1011                payload.extend_from_slice(&(case_bytes.len() as u32).to_le_bytes());
1012                payload.extend_from_slice(case_bytes);
1013                payload.extend_from_slice(&(*tag as u32).to_le_bytes());
1014                payload.extend_from_slice(&(child_indices.len() as u32).to_le_bytes());
1015                for child in child_indices {
1016                    payload.extend_from_slice(&child.to_le_bytes());
1017                }
1018                Ok(encoder.push_node(Node {
1019                    kind: NodeKind::Variant,
1020                    payload,
1021                }))
1022            }
1023        }
1024    }
1025
1026    fn decode_graph(decoder: &Decoder<'_>, root: u32) -> Result<Self, AbiError> {
1027        let mut cache = HashMap::new();
1028        let mut visiting = HashSet::new();
1029        decode_value(decoder, root, &mut cache, &mut visiting)
1030    }
1031}
1032
1033fn decode_value(
1034    decoder: &Decoder<'_>,
1035    index: u32,
1036    cache: &mut HashMap<u32, Value>,
1037    visiting: &mut HashSet<u32>,
1038) -> Result<Value, AbiError> {
1039    if let Some(value) = cache.get(&index) {
1040        return Ok(value.clone());
1041    }
1042
1043    if !visiting.insert(index) {
1044        return Err(AbiError::InvalidEncoding(String::from(
1045            "Cycle detected in graph buffer",
1046        )));
1047    }
1048
1049    let node = decoder
1050        .node(index)
1051        .ok_or_else(|| AbiError::InvalidEncoding(format!("Node index {index} out of range")))?;
1052    let mut cursor = Cursor::new(&node.payload);
1053    let value = match node.kind {
1054        NodeKind::Bool => Value::Bool(cursor.read_u8()? == 1),
1055        NodeKind::U8 => Value::U8(cursor.read_u8()?),
1056        NodeKind::U16 => Value::U16(cursor.read_u16()?),
1057        NodeKind::U32 => Value::U32(cursor.read_u32()?),
1058        NodeKind::U64 => Value::U64(cursor.read_u64()?),
1059        NodeKind::S8 => {
1060            let raw = cursor.read_u8()?;
1061            Value::S8(i8::from_le_bytes([raw]))
1062        }
1063        NodeKind::S16 => {
1064            let raw = cursor.read_u16()?;
1065            Value::S16(i16::from_le_bytes(raw.to_le_bytes()))
1066        }
1067        NodeKind::S32 => {
1068            let raw = cursor.read_u32()?;
1069            Value::S32(i32::from_le_bytes(raw.to_le_bytes()))
1070        }
1071        NodeKind::S64 => {
1072            let raw = cursor.read_u64()?;
1073            Value::S64(i64::from_le_bytes(raw.to_le_bytes()))
1074        }
1075        NodeKind::F32 => {
1076            let raw = cursor.read_u32()?;
1077            Value::F32(f32::from_le_bytes(raw.to_le_bytes()))
1078        }
1079        NodeKind::F64 => {
1080            let raw = cursor.read_u64()?;
1081            Value::F64(f64::from_le_bytes(raw.to_le_bytes()))
1082        }
1083        NodeKind::Char => {
1084            let raw = cursor.read_u32()?;
1085            let ch = char::from_u32(raw)
1086                .ok_or_else(|| AbiError::InvalidEncoding(String::from("Invalid char scalar")))?;
1087            Value::Char(ch)
1088        }
1089        NodeKind::Flags => Value::Flags(cursor.read_u64()?),
1090        NodeKind::String => {
1091            let len = cursor.read_u32()? as usize;
1092            let bytes = cursor.read_bytes(len)?;
1093            let s = core::str::from_utf8(bytes)
1094                .map_err(|_| AbiError::InvalidEncoding(String::from("Invalid UTF-8")))?;
1095            Value::String(String::from(s))
1096        }
1097        NodeKind::Array => {
1098            // Array: [elem_type:u8, count:u32, data:u8*]
1099            let elem_type = decode_value_type(&mut cursor)?;
1100            let count = cursor.read_u32()? as usize;
1101            let mut items = Vec::with_capacity(count);
1102            for _ in 0..count {
1103                items.push(decode_array_element(&mut cursor, &elem_type)?);
1104            }
1105            Value::List { elem_type, items }
1106        }
1107        NodeKind::List => {
1108            // v2 format: [elem_type:type_tag*, count:u32, child_indices:u32*]
1109            let elem_type = decode_value_type(&mut cursor)?;
1110            if fixed_width(&elem_type).is_some() {
1111                return Err(AbiError::InvalidEncoding(String::from(
1112                    "List node with primitive elem_type; expected Array node",
1113                )));
1114            }
1115            let count = cursor.read_u32()? as usize;
1116            let mut items = Vec::with_capacity(count);
1117            for _ in 0..count {
1118                let child = cursor.read_u32()?;
1119                items.push(decode_value(decoder, child, cache, visiting)?);
1120            }
1121            Value::List { elem_type, items }
1122        }
1123        NodeKind::Record => {
1124            // v2 format: [type_name_len:u32, type_name:utf8, field_count:u32, field_names:(len:u32, name:utf8)*, child_indices:u32*]
1125            let type_name_len = cursor.read_u32()? as usize;
1126            let type_name_bytes = cursor.read_bytes(type_name_len)?;
1127            let type_name = core::str::from_utf8(type_name_bytes).map_err(|_| {
1128                AbiError::InvalidEncoding(String::from("Invalid UTF-8 in type name"))
1129            })?;
1130            let type_name = String::from(type_name);
1131            let count = cursor.read_u32()? as usize;
1132            let mut field_names = Vec::with_capacity(count);
1133            for _ in 0..count {
1134                let name_len = cursor.read_u32()? as usize;
1135                let name_bytes = cursor.read_bytes(name_len)?;
1136                let name = core::str::from_utf8(name_bytes).map_err(|_| {
1137                    AbiError::InvalidEncoding(String::from("Invalid UTF-8 in field name"))
1138                })?;
1139                field_names.push(String::from(name));
1140            }
1141            let mut fields = Vec::with_capacity(count);
1142            for name in field_names {
1143                let child = cursor.read_u32()?;
1144                let value = decode_value(decoder, child, cache, visiting)?;
1145                fields.push((name, value));
1146            }
1147            Value::Record { type_name, fields }
1148        }
1149        NodeKind::Tuple => {
1150            let count = cursor.read_u32()? as usize;
1151            let mut items = Vec::with_capacity(count);
1152            for _ in 0..count {
1153                let child = cursor.read_u32()?;
1154                items.push(decode_value(decoder, child, cache, visiting)?);
1155            }
1156            Value::Tuple(items)
1157        }
1158        NodeKind::Option => {
1159            // v2 format: [inner_type:type_tag*, presence:u8, child_index?:u32]
1160            let inner_type = decode_value_type(&mut cursor)?;
1161            let has_value = cursor.read_u8()?;
1162            let value = if has_value == 1 {
1163                let child = cursor.read_u32()?;
1164                Some(alloc::boxed::Box::new(decode_value(
1165                    decoder, child, cache, visiting,
1166                )?))
1167            } else {
1168                None
1169            };
1170            Value::Option { inner_type, value }
1171        }
1172        NodeKind::Result => {
1173            // v2 format: [ok_type:type_tag*, err_type:type_tag*, tag:u32, has_payload:u8, child_index?:u32]
1174            let ok_type = decode_value_type(&mut cursor)?;
1175            let err_type = decode_value_type(&mut cursor)?;
1176            let tag = cursor.read_u32()?;
1177            let has_payload = cursor.read_u8()?;
1178            let value = if has_payload == 1 {
1179                let child = cursor.read_u32()?;
1180                let inner = decode_value(decoder, child, cache, visiting)?;
1181                if tag == 0 {
1182                    Ok(alloc::boxed::Box::new(inner))
1183                } else {
1184                    Err(alloc::boxed::Box::new(inner))
1185                }
1186            } else {
1187                return Err(AbiError::InvalidEncoding(String::from(
1188                    "Result must have payload",
1189                )));
1190            };
1191            Value::Result {
1192                ok_type,
1193                err_type,
1194                value,
1195            }
1196        }
1197        NodeKind::Variant => {
1198            // v2 format: [type_name_len:u32, type_name:utf8, case_name_len:u32, case_name:utf8, tag:u32, payload_count:u32, child_indices:u32*]
1199            let type_name_len = cursor.read_u32()? as usize;
1200            let type_name_bytes = cursor.read_bytes(type_name_len)?;
1201            let type_name = core::str::from_utf8(type_name_bytes).map_err(|_| {
1202                AbiError::InvalidEncoding(String::from("Invalid UTF-8 in type name"))
1203            })?;
1204            let type_name = String::from(type_name);
1205            let case_name_len = cursor.read_u32()? as usize;
1206            let case_name_bytes = cursor.read_bytes(case_name_len)?;
1207            let case_name = core::str::from_utf8(case_name_bytes).map_err(|_| {
1208                AbiError::InvalidEncoding(String::from("Invalid UTF-8 in case name"))
1209            })?;
1210            let case_name = String::from(case_name);
1211            let tag = cursor.read_u32()? as usize;
1212            let payload_count = cursor.read_u32()? as usize;
1213            let mut payload = Vec::with_capacity(payload_count);
1214            for _ in 0..payload_count {
1215                let child = cursor.read_u32()?;
1216                payload.push(decode_value(decoder, child, cache, visiting)?);
1217            }
1218            Value::Variant {
1219                type_name,
1220                case_name,
1221                tag,
1222                payload,
1223            }
1224        }
1225    };
1226
1227    if !cursor.is_eof() {
1228        return Err(AbiError::InvalidEncoding(format!(
1229            "Trailing payload bytes at node {index}"
1230        )));
1231    }
1232
1233    visiting.remove(&index);
1234    cache.insert(index, value.clone());
1235    Ok(value)
1236}