1#[cfg(test)]
8mod tests;
9
10mod decode;
11mod encode;
12
13use crate::{Error, Limits, contract};
14use alloc::vec::Vec;
15use core::cell::Cell;
16use serde::{Deserialize, Serialize};
17
18struct Budget {
19 nodes: Cell<usize>,
20 heap_units: Cell<usize>,
21 depth: usize,
22 refused: Cell<bool>,
23}
24impl Budget {
25 fn new(limits: Limits) -> Self {
26 Self {
27 nodes: Cell::new(limits.nodes),
28 heap_units: Cell::new(limits.allocation.saturating_sub(limits.bytes) / 8),
33 depth: limits.depth,
34 refused: Cell::new(limits.allocation < limits.bytes),
35 }
36 }
37 #[cfg_attr(target_arch = "wasm32", inline(always))]
38 fn enter(&self, depth: usize, size: usize) -> bool {
39 self.enter_many(depth, size, 1)
40 }
41 #[cfg_attr(target_arch = "wasm32", inline(always))]
42 fn enter_many(&self, depth: usize, size: usize, count: usize) -> bool {
43 if self.refused.get() {
44 return false;
45 }
46 let Some(nodes) = self.nodes.get().checked_sub(count) else {
47 self.refused.set(true);
48 return false;
49 };
50 if depth > self.depth || !self.bytes(size) {
51 self.refused.set(true);
52 return false;
53 }
54 self.nodes.set(nodes);
55 true
56 }
57 #[cfg_attr(target_arch = "wasm32", inline(always))]
58 fn bytes(&self, bytes: usize) -> bool {
59 let Some(heap) = self.heap_units.get().checked_sub(bytes) else {
60 self.refused.set(true);
61 return false;
62 };
63 self.heap_units.set(heap);
64 true
65 }
66 fn error(&self) -> Error {
67 if self.refused.get() {
68 Error::Limit
69 } else {
70 Error::Malformed
71 }
72 }
73}
74
75pub fn encoded_len<T: Serialize + ?Sized>(value: &T, limits: Limits) -> Result<usize, Error> {
77 let budget = Budget::new(limits);
78 let size = postcard::experimental::serialized_size(&encode::Bounded {
79 value,
80 budget: &budget,
81 depth: 1,
82 })
83 .map_err(|_| budget.error())?;
84 if size > limits.bytes {
85 return Err(Error::Limit);
86 }
87 Ok(size)
88}
89
90pub fn encode<T: Serialize + ?Sized>(value: &T, limits: Limits) -> Result<Vec<u8>, Error> {
92 encode_prefixed(value, limits, &[])
93}
94fn encode_prefixed<T: Serialize + ?Sized>(
95 value: &T,
96 limits: Limits,
97 prefix: &[u8],
98) -> Result<Vec<u8>, Error> {
99 let mut bytes = Vec::new();
100 encode_prefixed_into(value, limits, prefix, &mut bytes)?;
101 Ok(bytes)
102}
103
104pub fn encode_into<T: Serialize + ?Sized>(
108 value: &T,
109 limits: Limits,
110 bytes: &mut Vec<u8>,
111) -> Result<(), Error> {
112 encode_prefixed_into(value, limits, &[], bytes)
113}
114fn encode_prefixed_into<T: Serialize + ?Sized>(
115 value: &T,
116 limits: Limits,
117 prefix: &[u8],
118 bytes: &mut Vec<u8>,
119) -> Result<(), Error> {
120 bytes.clear();
121 let size = encoded_len(value, payload_limits(limits, prefix.len())?)?;
122 bytes
123 .try_reserve_exact(size + prefix.len())
124 .map_err(|_| Error::Allocation)?;
125 bytes.extend_from_slice(prefix);
126 let budget = Budget::new(limits);
127 *bytes = postcard::serialize_with_flavor(
128 &encode::Bounded {
129 value,
130 budget: &budget,
131 depth: 1,
132 },
133 contract::Output {
134 bytes: core::mem::take(bytes),
135 limit: size + prefix.len(),
136 },
137 )
138 .map_err(|_| budget.error())?;
139 Ok(())
140}
141
142pub fn encode_request_into<T: Serialize + ?Sized>(
144 id: u64,
145 value: &T,
146 limits: Limits,
147 bytes: &mut Vec<u8>,
148) -> Result<(), Error> {
149 encode_prefixed_into(value, limits, &contract::header(id, 0), bytes)
150}
151
152pub fn encode_response_into<T: Serialize + ?Sized>(
154 id: u64,
155 value: &T,
156 limits: Limits,
157 bytes: &mut Vec<u8>,
158) -> Result<(), Error> {
159 encode_prefixed_into(value, limits, &contract::header(id, 1), bytes)
160}
161
162pub fn decode<'de, T: Serialize + Deserialize<'de>>(
166 bytes: &'de [u8],
167 limits: Limits,
168) -> Result<T, Error> {
169 if bytes.len() > limits.bytes {
170 return Err(Error::Limit);
171 }
172 let budget = Budget::new(limits);
173 let mut input = postcard::Deserializer::from_bytes(bytes);
174 let value = T::deserialize(decode::Bounded {
175 inner: &mut input,
176 budget: &budget,
177 depth: 1,
178 })
179 .map_err(|_| budget.error())?;
180 if !input.finalize().map_err(|_| Error::Malformed)?.is_empty() {
181 return Err(Error::Trailing);
182 }
183 let budget = Budget::new(limits);
184 let equal = postcard::serialize_with_flavor(
185 &encode::Bounded {
186 value: &value,
187 budget: &budget,
188 depth: 1,
189 },
190 contract::Compare {
191 remaining: bytes,
192 allowance: limits.bytes,
193 equal: true,
194 },
195 )
196 .map_err(|_| budget.error())?;
197 if !equal {
198 return Err(Error::Noncanonical);
199 }
200 Ok(value)
201}
202
203pub fn encode_request<T: Serialize + ?Sized>(
205 id: u64,
206 value: &T,
207 limits: Limits,
208) -> Result<Vec<u8>, Error> {
209 encode_prefixed(value, limits, &contract::header(id, 0))
210}
211pub fn encode_response<T: Serialize + ?Sized>(
213 id: u64,
214 value: &T,
215 limits: Limits,
216) -> Result<Vec<u8>, Error> {
217 encode_prefixed(value, limits, &contract::header(id, 1))
218}
219pub fn encoded_frame_len<T: Serialize + ?Sized>(value: &T, limits: Limits) -> Result<usize, Error> {
221 encoded_len(value, payload_limits(limits, contract::HEADER)?)
222 .map(|size| size + contract::HEADER)
223}
224pub fn decode_request<'de, T: Serialize + Deserialize<'de>>(
226 id: u64,
227 bytes: &'de [u8],
228 limits: Limits,
229) -> Result<T, Error> {
230 decode_frame(id, 0, bytes, limits)
231}
232pub fn decode_response<'de, T: Serialize + Deserialize<'de>>(
234 id: u64,
235 bytes: &'de [u8],
236 limits: Limits,
237) -> Result<T, Error> {
238 decode_frame(id, 1, bytes, limits)
239}
240fn decode_frame<'de, T: Serialize + Deserialize<'de>>(
241 id: u64,
242 direction: u8,
243 bytes: &'de [u8],
244 limits: Limits,
245) -> Result<T, Error> {
246 if bytes.len() > limits.bytes {
247 return Err(Error::Limit);
248 }
249 let payload = contract::payload(id, direction, bytes)?;
250 decode(payload, payload_limits(limits, contract::HEADER)?)
251}
252
253fn payload_limits(limits: Limits, prefix: usize) -> Result<Limits, Error> {
254 Ok(Limits {
255 bytes: limits.bytes.checked_sub(prefix).ok_or(Error::Limit)?,
256 allocation: limits.allocation.checked_sub(prefix).ok_or(Error::Limit)?,
257 ..limits
258 })
259}