Skip to main content

ic_wire/shape/
mod.rs

1use serde::{Serialize, de::DeserializeOwned};
2
3/// Worst-case bounds for one finite value.
4#[derive(Clone, Copy, Debug)]
5pub struct Shape {
6    /// Maximum canonical Postcard payload bytes.
7    pub bytes: usize,
8    /// Maximum requested heap bytes owned by the decoded value.
9    pub heap: usize,
10    /// Maximum visited value nodes, including zero-byte values.
11    pub nodes: usize,
12    /// Maximum nested value depth.
13    pub depth: usize,
14}
15impl Shape {
16    /// A leaf with no allocation.
17    pub const fn scalar(bytes: usize) -> Self {
18        Self {
19            bytes,
20            heap: 0,
21            nodes: 1,
22            depth: 1,
23        }
24    }
25    /// A record with these children. Overflow saturates and fails admission.
26    pub const fn record(children: &[Self]) -> Self {
27        let mut out = Self::scalar(0);
28        let mut i = 0;
29        while i < children.len() {
30            let child = children[i];
31            out.bytes = out.bytes.saturating_add(child.bytes);
32            out.heap = out.heap.saturating_add(child.heap);
33            out.nodes = out.nodes.saturating_add(child.nodes);
34            let depth = child.depth.saturating_add(1);
35            if depth > out.depth {
36                out.depth = depth;
37            }
38            i += 1;
39        }
40        out
41    }
42    /// A tagged choice, using the largest bound in each dimension.
43    pub const fn choice(tag_bytes: usize, alternatives: &[Self]) -> Self {
44        let mut out = Self::scalar(0);
45        let mut i = 0;
46        while i < alternatives.len() {
47            let child = alternatives[i];
48            if child.bytes > out.bytes {
49                out.bytes = child.bytes;
50            }
51            if child.heap > out.heap {
52                out.heap = child.heap;
53            }
54            if child.nodes > out.nodes {
55                out.nodes = child.nodes;
56            }
57            if child.depth > out.depth {
58                out.depth = child.depth;
59            }
60            i += 1;
61        }
62        out.bytes = out.bytes.saturating_add(tag_bytes);
63        out.nodes = out.nodes.saturating_add(1);
64        out.depth = out.depth.saturating_add(1);
65        out
66    }
67}
68
69/// Finite values. Prefer the derive with explicit field identities.
70///
71/// Manual implementations must provide honest bounds, reject before excessive
72/// allocation/work, and serialize deterministically. Unbounded Serde types have
73/// no implementation. Recursive types are unsupported.
74pub trait Wire: Serialize + DeserializeOwned {
75    /// Static resource authority for this value.
76    const SHAPE: Shape;
77    /// Static projection metadata; custom implementations must opt in honestly.
78    const SCHEMA: crate::schema::Schema = crate::schema::Schema::Opaque;
79}
80
81/// Consumer-selected allowances for finite schema or explicit native admission.
82#[derive(Clone, Copy, Debug)]
83pub struct Limits {
84    /// Maximum encoding bytes; framed APIs include the 11-byte header.
85    pub bytes: usize,
86    /// Maximum value heap plus codec scratch (excludes caller input).
87    pub allocation: usize,
88    /// Maximum visited value nodes, including unit elements.
89    pub nodes: usize,
90    /// Maximum nesting depth.
91    pub depth: usize,
92}
93macro_rules! scalars {
94    ($($ty:ty => ($bytes:expr, $schema:expr)),* $(,)?) => { $(
95        impl Wire for $ty {
96            const SHAPE: Shape = Shape::scalar($bytes);
97            const SCHEMA: crate::schema::Schema = $schema;
98        }
99    )* };
100}
101use crate::schema::Schema;
102scalars! { () => (0, Schema::Unit), bool => (1, Schema::Bool),
103u8 => (1, Schema::Unsigned(8)), i8 => (1, Schema::Signed(8)),
104u16 => (3, Schema::Unsigned(16)), i16 => (3, Schema::Signed(16)),
105u32 => (5, Schema::Unsigned(32)), i32 => (5, Schema::Signed(32)),
106u64 => (10, Schema::Unsigned(64)), i64 => (10, Schema::Signed(64)),
107u128 => (19, Schema::Unsigned(128)), i128 => (19, Schema::Signed(128)) }
108impl<T: Wire> Wire for Option<T> {
109    const SHAPE: Shape = Shape::choice(1, &[T::SHAPE]);
110    const SCHEMA: Schema = Schema::Option(&T::SCHEMA);
111}
112impl<T: Wire, E: Wire> Wire for Result<T, E> {
113    const SHAPE: Shape = Shape::choice(1, &[T::SHAPE, E::SHAPE]);
114    const SCHEMA: Schema = Schema::Result(&T::SCHEMA, &E::SCHEMA);
115}
116
117impl<T: Wire, const N: usize> Wire for [T; N]
118where
119    [T; N]: Serialize + DeserializeOwned,
120{
121    const SCHEMA: Schema = Schema::Array(&T::SCHEMA, N);
122    const SHAPE: Shape = Shape {
123        bytes: T::SHAPE.bytes.saturating_mul(N),
124        heap: T::SHAPE.heap.saturating_mul(N),
125        nodes: 1_usize.saturating_add(T::SHAPE.nodes.saturating_mul(N)),
126        depth: T::SHAPE.depth.saturating_add(1),
127    };
128}