1#[cfg(test)]
8mod tests;
9
10mod decode;
11mod encode;
12mod parser;
13
14use crate::{Error, Limits, contract};
15use alloc::vec::Vec;
16use core::cell::Cell;
17use serde::{Deserialize, Serialize};
18
19pub const fn method_id(name: &str) -> u64 {
32 let mut hash = 14_695_981_039_346_656_037u64;
33 let domain = b"ic-wire:method:";
34 let mut index = 0;
35 while index < domain.len() {
36 hash = (hash ^ domain[index] as u64).wrapping_mul(1_099_511_628_211);
37 index += 1;
38 }
39 let bytes = name.as_bytes();
40 index = 0;
41 while index < bytes.len() {
42 hash = (hash ^ bytes[index] as u64).wrapping_mul(1_099_511_628_211);
43 index += 1;
44 }
45 hash
46}
47
48struct Budget {
49 nodes: Cell<usize>,
50 heap_units: Cell<usize>,
51 depth: usize,
52 refused: Cell<bool>,
53}
54impl Budget {
55 fn new(limits: Limits) -> Self {
56 Self {
57 nodes: Cell::new(limits.nodes),
58 heap_units: Cell::new(limits.allocation.saturating_sub(limits.bytes) / 8),
63 depth: limits.depth,
64 refused: Cell::new(limits.allocation < limits.bytes),
65 }
66 }
67 #[cfg_attr(target_arch = "wasm32", inline(always))]
68 fn enter(&self, depth: usize, size: usize) -> bool {
69 self.enter_many(depth, size, 1)
70 }
71 #[cfg_attr(target_arch = "wasm32", inline(always))]
72 fn enter_many(&self, depth: usize, size: usize, count: usize) -> bool {
73 if self.refused.get() {
74 return false;
75 }
76 let Some(nodes) = self.nodes.get().checked_sub(count) else {
77 self.refused.set(true);
78 return false;
79 };
80 if depth > self.depth || !self.bytes(size) {
81 self.refused.set(true);
82 return false;
83 }
84 self.nodes.set(nodes);
85 true
86 }
87 #[cfg_attr(target_arch = "wasm32", inline(always))]
88 fn bytes(&self, bytes: usize) -> bool {
89 let Some(heap) = self.heap_units.get().checked_sub(bytes) else {
90 self.refused.set(true);
91 return false;
92 };
93 self.heap_units.set(heap);
94 true
95 }
96 fn error(&self) -> Error {
97 if self.refused.get() {
98 Error::Limit
99 } else {
100 Error::Malformed
101 }
102 }
103}
104
105pub fn encoded_len<T: Serialize + ?Sized>(value: &T, limits: Limits) -> Result<usize, Error> {
107 let budget = Budget::new(limits);
108 let size = postcard::experimental::serialized_size(&encode::Bounded {
109 value,
110 budget: &budget,
111 depth: 1,
112 })
113 .map_err(|_| budget.error())?;
114 if size > limits.bytes {
115 return Err(Error::Limit);
116 }
117 Ok(size)
118}
119
120pub fn encode<T: Serialize + ?Sized>(value: &T, limits: Limits) -> Result<Vec<u8>, Error> {
122 encode_prefixed(value, limits, &[])
123}
124fn encode_prefixed<T: Serialize + ?Sized>(
125 value: &T,
126 limits: Limits,
127 prefix: &[u8],
128) -> Result<Vec<u8>, Error> {
129 let mut bytes = Vec::new();
130 encode_prefixed_into(value, limits, prefix, &mut bytes)?;
131 Ok(bytes)
132}
133
134pub fn encode_into<T: Serialize + ?Sized>(
138 value: &T,
139 limits: Limits,
140 bytes: &mut Vec<u8>,
141) -> Result<(), Error> {
142 encode_prefixed_into(value, limits, &[], bytes)
143}
144
145pub fn encode_to_slice<'a, T: Serialize + ?Sized>(
153 value: &T,
154 limits: Limits,
155 storage: &'a mut [u8],
156) -> Result<&'a mut [u8], Error> {
157 encode_prefixed_to_slice(value, limits, &[], storage)
158}
159
160pub fn with_encoded<T: Serialize, R>(
196 value: T,
197 limits: Limits,
198 scratch: &mut [u8],
199 consume: impl FnOnce(&[u8]) -> R,
200) -> Result<R, Error> {
201 let size = encoded_len(&value, limits)?;
202 let budget = Budget::new(limits);
203 let bounded = encode::Bounded {
204 value: &value,
205 budget: &budget,
206 depth: 1,
207 };
208 if let Some(storage) = scratch.get_mut(..size) {
212 let bytes = postcard::to_slice(&bounded, storage).map_err(|_| budget.error())?;
213 drop(value);
214 Ok(consume(bytes))
215 } else {
216 let mut bytes = Vec::new();
217 bytes
218 .try_reserve_exact(size)
219 .map_err(|_| Error::Allocation)?;
220 let bytes =
221 postcard::serialize_with_flavor(&bounded, contract::Output { bytes, limit: size })
222 .map_err(|_| budget.error())?;
223 drop(value);
224 Ok(consume(&bytes))
225 }
226}
227
228pub fn encode_request_to_slice<'a, T: Serialize + ?Sized>(
230 id: u64,
231 value: &T,
232 limits: Limits,
233 storage: &'a mut [u8],
234) -> Result<&'a mut [u8], Error> {
235 encode_prefixed_to_slice(value, limits, &contract::header(id, 0), storage)
236}
237
238pub fn encode_response_to_slice<'a, T: Serialize + ?Sized>(
240 id: u64,
241 value: &T,
242 limits: Limits,
243 storage: &'a mut [u8],
244) -> Result<&'a mut [u8], Error> {
245 encode_prefixed_to_slice(value, limits, &contract::header(id, 1), storage)
246}
247
248fn encode_prefixed_to_slice<'a, T: Serialize + ?Sized>(
249 value: &T,
250 limits: Limits,
251 prefix: &[u8],
252 storage: &'a mut [u8],
253) -> Result<&'a mut [u8], Error> {
254 let size = encoded_len(value, payload_limits(limits, prefix.len())?)?;
255 let output = storage.get_mut(..size + prefix.len()).ok_or(Error::Limit)?;
256 output[..prefix.len()].copy_from_slice(prefix);
257 let budget = Budget::new(limits);
258 let payload = postcard::to_slice(
263 &encode::Bounded {
264 value,
265 budget: &budget,
266 depth: 1,
267 },
268 &mut output[prefix.len()..],
269 )
270 .map_err(|_| budget.error())?;
271 let written = prefix.len() + payload.len();
272 Ok(&mut output[..written])
273}
274
275fn encode_prefixed_into<T: Serialize + ?Sized>(
276 value: &T,
277 limits: Limits,
278 prefix: &[u8],
279 bytes: &mut Vec<u8>,
280) -> Result<(), Error> {
281 bytes.clear();
282 let size = encoded_len(value, payload_limits(limits, prefix.len())?)?;
283 bytes
284 .try_reserve_exact(size + prefix.len())
285 .map_err(|_| Error::Allocation)?;
286 bytes.extend_from_slice(prefix);
287 let budget = Budget::new(limits);
288 *bytes = postcard::serialize_with_flavor(
289 &encode::Bounded {
290 value,
291 budget: &budget,
292 depth: 1,
293 },
294 contract::Output {
295 bytes: core::mem::take(bytes),
296 limit: size + prefix.len(),
297 },
298 )
299 .map_err(|_| budget.error())?;
300 Ok(())
301}
302
303pub fn encode_request_into<T: Serialize + ?Sized>(
305 id: u64,
306 value: &T,
307 limits: Limits,
308 bytes: &mut Vec<u8>,
309) -> Result<(), Error> {
310 encode_prefixed_into(value, limits, &contract::header(id, 0), bytes)
311}
312
313pub fn encode_response_into<T: Serialize + ?Sized>(
315 id: u64,
316 value: &T,
317 limits: Limits,
318 bytes: &mut Vec<u8>,
319) -> Result<(), Error> {
320 encode_prefixed_into(value, limits, &contract::header(id, 1), bytes)
321}
322
323pub fn decode<'de, T: Deserialize<'de>>(bytes: &'de [u8], limits: Limits) -> Result<T, Error> {
327 if bytes.len() > limits.bytes {
328 return Err(Error::Limit);
329 }
330 let budget = Budget::new(limits);
331 let mut input = parser::Deserializer::from_bytes(bytes);
332 let value = T::deserialize(decode::Bounded {
333 inner: &mut input,
334 budget: &budget,
335 depth: 1,
336 })
337 .map_err(|_| budget.error())?;
338 if !input.finalize().map_err(|_| Error::Malformed)?.is_empty() {
339 return Err(Error::Trailing);
340 }
341 Ok(value)
346}
347
348pub fn encode_request<T: Serialize + ?Sized>(
350 id: u64,
351 value: &T,
352 limits: Limits,
353) -> Result<Vec<u8>, Error> {
354 encode_prefixed(value, limits, &contract::header(id, 0))
355}
356pub fn encode_response<T: Serialize + ?Sized>(
358 id: u64,
359 value: &T,
360 limits: Limits,
361) -> Result<Vec<u8>, Error> {
362 encode_prefixed(value, limits, &contract::header(id, 1))
363}
364pub fn encoded_frame_len<T: Serialize + ?Sized>(value: &T, limits: Limits) -> Result<usize, Error> {
366 encoded_len(value, payload_limits(limits, contract::HEADER)?)
367 .map(|size| size + contract::HEADER)
368}
369pub fn decode_request<'de, T: Deserialize<'de>>(
371 id: u64,
372 bytes: &'de [u8],
373 limits: Limits,
374) -> Result<T, Error> {
375 decode_frame(id, 0, bytes, limits)
376}
377pub fn decode_response<'de, T: Deserialize<'de>>(
379 id: u64,
380 bytes: &'de [u8],
381 limits: Limits,
382) -> Result<T, Error> {
383 decode_frame(id, 1, bytes, limits)
384}
385fn decode_frame<'de, T: Deserialize<'de>>(
386 id: u64,
387 direction: u8,
388 bytes: &'de [u8],
389 limits: Limits,
390) -> Result<T, Error> {
391 if bytes.len() > limits.bytes {
392 return Err(Error::Limit);
393 }
394 let payload = contract::payload(id, direction, bytes)?;
395 decode(payload, payload_limits(limits, contract::HEADER)?)
396}
397
398fn payload_limits(limits: Limits, prefix: usize) -> Result<Limits, Error> {
399 Ok(Limits {
400 bytes: limits.bytes.checked_sub(prefix).ok_or(Error::Limit)?,
401 allocation: limits.allocation.checked_sub(prefix).ok_or(Error::Limit)?,
402 ..limits
403 })
404}