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