Skip to main content

ic_wire/bounded/
mod.rs

1mod borrowed;
2mod packed_i16;
3pub use borrowed::{BytesRef, TextRef};
4pub use packed_i16::PackedI16;
5
6use crate::Error;
7use alloc::{string::String, vec::Vec};
8use core::fmt;
9use serde::{Deserialize, Deserializer, Serialize, Serializer, de};
10
11/// A sequence with a compile-time element bound.
12#[derive(Clone, Debug, Eq, PartialEq)]
13pub struct List<T, const N: usize>(Vec<T>);
14impl<T, const N: usize> List<T, N> {
15    /// Admit an owned sequence without copying.
16    pub fn new(values: Vec<T>) -> Result<Self, Error> {
17        if values.len() > N {
18            Err(Error::Limit)
19        } else {
20            Ok(Self(values))
21        }
22    }
23    /// Borrow the elements.
24    pub fn as_slice(&self) -> &[T] {
25        &self.0
26    }
27    /// Recover the admitted sequence.
28    pub fn into_vec(self) -> Vec<T> {
29        self.0
30    }
31}
32impl<T: Serialize, const N: usize> Serialize for List<T, N> {
33    fn serialize<S: Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
34        self.0.serialize(s)
35    }
36}
37impl<'de, T: Deserialize<'de>, const N: usize> Deserialize<'de> for List<T, N> {
38    fn deserialize<D: Deserializer<'de>>(d: D) -> Result<Self, D::Error> {
39        struct Visitor<T, const N: usize>(core::marker::PhantomData<T>);
40        impl<'de, T: Deserialize<'de>, const N: usize> de::Visitor<'de> for Visitor<T, N> {
41            type Value = List<T, N>;
42            fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
43                f.write_str("bounded sequence")
44            }
45            fn visit_seq<A: de::SeqAccess<'de>>(self, mut seq: A) -> Result<Self::Value, A::Error> {
46                // Postcard hides its count when remaining bytes are fewer than
47                // elements (including legitimate unit values). Bound both paths.
48                let count = seq.size_hint();
49                if count.is_some_and(|count| count > N) {
50                    return Err(de::Error::custom("element limit"));
51                }
52                let mut values = Vec::new();
53                values
54                    .try_reserve_exact(count.unwrap_or(0))
55                    .map_err(|_| de::Error::custom("allocation"))?;
56                for _ in 0..N {
57                    let Some(value) = seq.next_element()? else {
58                        return Ok(List(values));
59                    };
60                    if values.len() == values.capacity() {
61                        // Hidden lengths need amortized growth, capped by the
62                        // declared element bound rather than Vec's growth policy.
63                        let next = values.len().saturating_mul(2).max(1).min(N);
64                        values
65                            .try_reserve_exact(next - values.len())
66                            .map_err(|_| de::Error::custom("allocation"))?;
67                    }
68                    values.push(value);
69                }
70                // Detect an excess element without invoking its decoder.
71                struct Reject;
72                impl<'de> de::DeserializeSeed<'de> for Reject {
73                    type Value = ();
74                    fn deserialize<D: Deserializer<'de>>(self, _: D) -> Result<(), D::Error> {
75                        Err(de::Error::custom("element limit"))
76                    }
77                }
78                seq.next_element_seed(Reject)?;
79                Ok(List(values))
80            }
81        }
82        d.deserialize_seq(Visitor::<T, N>(core::marker::PhantomData))
83    }
84}
85
86/// Bounded opaque bytes; consumers validate principal bytes using their existing
87/// principal type. IC Wire defines no new identity type.
88#[derive(Clone, Debug, Eq, PartialEq)]
89pub struct Bytes<const N: usize>(Vec<u8>);
90impl<const N: usize> Bytes<N> {
91    /// Admit bytes without copying.
92    pub fn new(bytes: Vec<u8>) -> Result<Self, Error> {
93        if bytes.len() > N {
94            Err(Error::Limit)
95        } else {
96            Ok(Self(bytes))
97        }
98    }
99    /// Borrow the bytes.
100    pub fn as_slice(&self) -> &[u8] {
101        &self.0
102    }
103    /// Borrow this bounded value without copying or repeating admission.
104    pub fn as_borrowed(&self) -> BytesRef<'_, N> {
105        BytesRef::from_owned(self)
106    }
107    /// Recover the admitted bytes without copying or allocating.
108    pub fn into_vec(self) -> Vec<u8> {
109        self.0
110    }
111}
112impl<const N: usize> Serialize for Bytes<N> {
113    fn serialize<S: Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
114        s.serialize_bytes(&self.0)
115    }
116}
117impl<'de, const N: usize> Deserialize<'de> for Bytes<N> {
118    fn deserialize<D: Deserializer<'de>>(d: D) -> Result<Self, D::Error> {
119        struct Visitor<const N: usize>;
120        impl<const N: usize> de::Visitor<'_> for Visitor<N> {
121            type Value = Bytes<N>;
122            fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
123                f.write_str("bounded bytes")
124            }
125            fn visit_bytes<E: de::Error>(self, bytes: &[u8]) -> Result<Self::Value, E> {
126                if bytes.len() > N {
127                    return Err(E::custom("byte limit"));
128                }
129                let mut out = Vec::new();
130                out.try_reserve_exact(bytes.len())
131                    .map_err(|_| E::custom("allocation"))?;
132                out.extend_from_slice(bytes);
133                Ok(Bytes(out))
134            }
135        }
136        d.deserialize_bytes(Visitor::<N>)
137    }
138}
139
140/// UTF-8 text bounded in bytes.
141#[derive(Clone, Debug, Eq, PartialEq)]
142pub struct Text<const N: usize>(String);
143impl<const N: usize> Text<N> {
144    /// Admit owned text without copying.
145    pub fn new(text: String) -> Result<Self, Error> {
146        if text.len() > N {
147            Err(Error::Limit)
148        } else {
149            Ok(Self(text))
150        }
151    }
152    /// Borrow the text.
153    pub fn as_str(&self) -> &str {
154        &self.0
155    }
156    /// Borrow this bounded value without copying or repeating admission.
157    pub fn as_borrowed(&self) -> TextRef<'_, N> {
158        TextRef::from_owned(self)
159    }
160    /// Recover the admitted text without copying or allocating.
161    pub fn into_string(self) -> String {
162        self.0
163    }
164}
165impl<const N: usize> Serialize for Text<N> {
166    fn serialize<S: Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
167        s.serialize_str(&self.0)
168    }
169}
170impl<'de, const N: usize> Deserialize<'de> for Text<N> {
171    fn deserialize<D: Deserializer<'de>>(d: D) -> Result<Self, D::Error> {
172        let bytes = Bytes::<N>::deserialize(d)?;
173        let text = String::from_utf8(bytes.0).map_err(|_| de::Error::custom("invalid UTF-8"))?;
174        Ok(Self(text))
175    }
176}