1mod decode;
8mod encode;
9
10use crate::{Error, Limits, contract};
11use alloc::vec::Vec;
12use core::cell::Cell;
13use serde::{Serialize, de::DeserializeOwned};
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 size = encoded_len(value, payload_limits(limits, prefix.len())?)?;
97 let mut bytes = Vec::new();
98 bytes
99 .try_reserve_exact(size + prefix.len())
100 .map_err(|_| Error::Allocation)?;
101 bytes.extend_from_slice(prefix);
102 let budget = Budget::new(limits);
103 postcard::serialize_with_flavor(
104 &encode::Bounded {
105 value,
106 budget: &budget,
107 depth: 1,
108 },
109 contract::Output {
110 bytes,
111 limit: size + prefix.len(),
112 },
113 )
114 .map_err(|_| budget.error())
115}
116
117pub fn decode<T: Serialize + DeserializeOwned>(bytes: &[u8], limits: Limits) -> Result<T, Error> {
119 if bytes.len() > limits.bytes {
120 return Err(Error::Limit);
121 }
122 let budget = Budget::new(limits);
123 let mut input = postcard::Deserializer::from_bytes(bytes);
124 let value = T::deserialize(decode::Bounded {
125 inner: &mut input,
126 budget: &budget,
127 depth: 1,
128 })
129 .map_err(|_| budget.error())?;
130 if !input.finalize().map_err(|_| Error::Malformed)?.is_empty() {
131 return Err(Error::Trailing);
132 }
133 let budget = Budget::new(limits);
134 let equal = postcard::serialize_with_flavor(
135 &encode::Bounded {
136 value: &value,
137 budget: &budget,
138 depth: 1,
139 },
140 contract::Compare {
141 remaining: bytes,
142 allowance: limits.bytes,
143 equal: true,
144 },
145 )
146 .map_err(|_| budget.error())?;
147 if !equal {
148 return Err(Error::Noncanonical);
149 }
150 Ok(value)
151}
152
153pub fn encode_request<T: Serialize + ?Sized>(
155 id: u64,
156 value: &T,
157 limits: Limits,
158) -> Result<Vec<u8>, Error> {
159 encode_prefixed(value, limits, &contract::header(id, 0))
160}
161pub fn encode_response<T: Serialize + ?Sized>(
163 id: u64,
164 value: &T,
165 limits: Limits,
166) -> Result<Vec<u8>, Error> {
167 encode_prefixed(value, limits, &contract::header(id, 1))
168}
169pub fn encoded_frame_len<T: Serialize + ?Sized>(value: &T, limits: Limits) -> Result<usize, Error> {
171 encoded_len(value, payload_limits(limits, contract::HEADER)?)
172 .map(|size| size + contract::HEADER)
173}
174pub fn decode_request<T: Serialize + DeserializeOwned>(
176 id: u64,
177 bytes: &[u8],
178 limits: Limits,
179) -> Result<T, Error> {
180 decode_frame(id, 0, bytes, limits)
181}
182pub fn decode_response<T: Serialize + DeserializeOwned>(
184 id: u64,
185 bytes: &[u8],
186 limits: Limits,
187) -> Result<T, Error> {
188 decode_frame(id, 1, bytes, limits)
189}
190fn decode_frame<T: Serialize + DeserializeOwned>(
191 id: u64,
192 direction: u8,
193 bytes: &[u8],
194 limits: Limits,
195) -> Result<T, Error> {
196 if bytes.len() > limits.bytes {
197 return Err(Error::Limit);
198 }
199 let payload = contract::payload(id, direction, bytes)?;
200 decode(payload, payload_limits(limits, contract::HEADER)?)
201}
202
203fn payload_limits(limits: Limits, prefix: usize) -> Result<Limits, Error> {
204 Ok(Limits {
205 bytes: limits.bytes.checked_sub(prefix).ok_or(Error::Limit)?,
206 allocation: limits.allocation.checked_sub(prefix).ok_or(Error::Limit)?,
207 ..limits
208 })
209}