1use crate::metadata::{IoTypeDetails, IoTypeMetadataKind};
2use core::num::NonZeroU8;
3
4macro_rules! forward_option {
6 ($expr:expr) => {{
7 let Some(result) = $expr else {
8 return None;
9 };
10 result
11 }};
12}
13
14#[inline(always)]
15pub(super) const fn decode_type_details(mut metadata: &[u8]) -> Option<(IoTypeDetails, &[u8])> {
16 if metadata.is_empty() {
17 return None;
18 }
19
20 let kind = forward_option!(IoTypeMetadataKind::try_from_u8(metadata[0]));
21 metadata = forward_option!(skip_n_bytes(metadata, 1));
22
23 match kind {
24 IoTypeMetadataKind::Unit => Some((
25 IoTypeDetails {
26 recommended_capacity: 0,
27 alignment: NonZeroU8::new(1).expect("Not zero; qed"),
28 },
29 metadata,
30 )),
31 IoTypeMetadataKind::Bool | IoTypeMetadataKind::U8 | IoTypeMetadataKind::I8 => Some((
32 IoTypeDetails {
33 recommended_capacity: 1,
34 alignment: NonZeroU8::new(1).expect("Not zero; qed"),
35 },
36 metadata,
37 )),
38 IoTypeMetadataKind::U16 | IoTypeMetadataKind::I16 => Some((
39 IoTypeDetails {
40 recommended_capacity: 2,
41 alignment: NonZeroU8::new(2).expect("Not zero; qed"),
42 },
43 metadata,
44 )),
45 IoTypeMetadataKind::U32 | IoTypeMetadataKind::I32 => Some((
46 IoTypeDetails {
47 recommended_capacity: 4,
48 alignment: NonZeroU8::new(4).expect("Not zero; qed"),
49 },
50 metadata,
51 )),
52 IoTypeMetadataKind::U64 | IoTypeMetadataKind::I64 => Some((
53 IoTypeDetails {
54 recommended_capacity: 8,
55 alignment: NonZeroU8::new(8).expect("Not zero; qed"),
56 },
57 metadata,
58 )),
59 IoTypeMetadataKind::U128 | IoTypeMetadataKind::I128 => Some((
60 IoTypeDetails {
61 recommended_capacity: 16,
62 alignment: NonZeroU8::new(16).expect("Not zero; qed"),
63 },
64 metadata,
65 )),
66 IoTypeMetadataKind::Struct => struct_type_details(metadata, None, false),
67 IoTypeMetadataKind::Struct0 => struct_type_details(metadata, Some(0), false),
68 IoTypeMetadataKind::Struct1 => struct_type_details(metadata, Some(1), false),
69 IoTypeMetadataKind::Struct2 => struct_type_details(metadata, Some(2), false),
70 IoTypeMetadataKind::Struct3 => struct_type_details(metadata, Some(3), false),
71 IoTypeMetadataKind::Struct4 => struct_type_details(metadata, Some(4), false),
72 IoTypeMetadataKind::Struct5 => struct_type_details(metadata, Some(5), false),
73 IoTypeMetadataKind::Struct6 => struct_type_details(metadata, Some(6), false),
74 IoTypeMetadataKind::Struct7 => struct_type_details(metadata, Some(7), false),
75 IoTypeMetadataKind::Struct8 => struct_type_details(metadata, Some(8), false),
76 IoTypeMetadataKind::Struct9 => struct_type_details(metadata, Some(9), false),
77 IoTypeMetadataKind::Struct10 => struct_type_details(metadata, Some(10), false),
78 IoTypeMetadataKind::TupleStruct => struct_type_details(metadata, None, true),
79 IoTypeMetadataKind::TupleStruct1 => struct_type_details(metadata, Some(1), true),
80 IoTypeMetadataKind::TupleStruct2 => struct_type_details(metadata, Some(2), true),
81 IoTypeMetadataKind::TupleStruct3 => struct_type_details(metadata, Some(3), true),
82 IoTypeMetadataKind::TupleStruct4 => struct_type_details(metadata, Some(4), true),
83 IoTypeMetadataKind::TupleStruct5 => struct_type_details(metadata, Some(5), true),
84 IoTypeMetadataKind::TupleStruct6 => struct_type_details(metadata, Some(6), true),
85 IoTypeMetadataKind::TupleStruct7 => struct_type_details(metadata, Some(7), true),
86 IoTypeMetadataKind::TupleStruct8 => struct_type_details(metadata, Some(8), true),
87 IoTypeMetadataKind::TupleStruct9 => struct_type_details(metadata, Some(9), true),
88 IoTypeMetadataKind::TupleStruct10 => struct_type_details(metadata, Some(10), true),
89 IoTypeMetadataKind::Enum => enum_capacity(metadata, None, true),
90 IoTypeMetadataKind::Enum1 => enum_capacity(metadata, Some(1), true),
91 IoTypeMetadataKind::Enum2 => enum_capacity(metadata, Some(2), true),
92 IoTypeMetadataKind::Enum3 => enum_capacity(metadata, Some(3), true),
93 IoTypeMetadataKind::Enum4 => enum_capacity(metadata, Some(4), true),
94 IoTypeMetadataKind::Enum5 => enum_capacity(metadata, Some(5), true),
95 IoTypeMetadataKind::Enum6 => enum_capacity(metadata, Some(6), true),
96 IoTypeMetadataKind::Enum7 => enum_capacity(metadata, Some(7), true),
97 IoTypeMetadataKind::Enum8 => enum_capacity(metadata, Some(8), true),
98 IoTypeMetadataKind::Enum9 => enum_capacity(metadata, Some(9), true),
99 IoTypeMetadataKind::Enum10 => enum_capacity(metadata, Some(10), true),
100 IoTypeMetadataKind::EnumNoFields => enum_capacity(metadata, None, false),
101 IoTypeMetadataKind::EnumNoFields1 => enum_capacity(metadata, Some(1), false),
102 IoTypeMetadataKind::EnumNoFields2 => enum_capacity(metadata, Some(2), false),
103 IoTypeMetadataKind::EnumNoFields3 => enum_capacity(metadata, Some(3), false),
104 IoTypeMetadataKind::EnumNoFields4 => enum_capacity(metadata, Some(4), false),
105 IoTypeMetadataKind::EnumNoFields5 => enum_capacity(metadata, Some(5), false),
106 IoTypeMetadataKind::EnumNoFields6 => enum_capacity(metadata, Some(6), false),
107 IoTypeMetadataKind::EnumNoFields7 => enum_capacity(metadata, Some(7), false),
108 IoTypeMetadataKind::EnumNoFields8 => enum_capacity(metadata, Some(8), false),
109 IoTypeMetadataKind::EnumNoFields9 => enum_capacity(metadata, Some(9), false),
110 IoTypeMetadataKind::EnumNoFields10 => enum_capacity(metadata, Some(10), false),
111 IoTypeMetadataKind::Array8b | IoTypeMetadataKind::VariableElements8b => {
112 if metadata.is_empty() {
113 return None;
114 }
115
116 let num_elements = metadata[0] as u32;
117 metadata = forward_option!(skip_n_bytes(metadata, size_of::<u8>()));
118
119 let type_details;
120 (type_details, metadata) = forward_option!(decode_type_details(metadata));
121 let recommended_capacity =
122 forward_option!(type_details.recommended_capacity.checked_mul(num_elements));
123 Some((
124 IoTypeDetails {
125 recommended_capacity,
126 alignment: type_details.alignment,
127 },
128 metadata,
129 ))
130 }
131 IoTypeMetadataKind::Array16b | IoTypeMetadataKind::VariableElements16b => {
132 if metadata.is_empty() {
133 return None;
134 }
135
136 let mut num_elements = [0; size_of::<u16>()];
137 (metadata, _) =
138 forward_option!(copy_n_bytes(metadata, &mut num_elements, size_of::<u16>()));
139 let num_elements = u16::from_le_bytes(num_elements) as u32;
140
141 let type_details;
142 (type_details, metadata) = forward_option!(decode_type_details(metadata));
143 let recommended_capacity =
144 forward_option!(type_details.recommended_capacity.checked_mul(num_elements));
145 Some((
146 IoTypeDetails {
147 recommended_capacity,
148 alignment: type_details.alignment,
149 },
150 metadata,
151 ))
152 }
153 IoTypeMetadataKind::Array32b | IoTypeMetadataKind::VariableElements32b => {
154 if metadata.is_empty() {
155 return None;
156 }
157
158 let mut num_elements = [0; size_of::<u32>()];
159 (metadata, _) =
160 forward_option!(copy_n_bytes(metadata, &mut num_elements, size_of::<u32>()));
161 let num_elements = u32::from_le_bytes(num_elements);
162
163 let type_details;
164 (type_details, metadata) = forward_option!(decode_type_details(metadata));
165 let recommended_capacity =
166 forward_option!(type_details.recommended_capacity.checked_mul(num_elements));
167 Some((
168 IoTypeDetails {
169 recommended_capacity,
170 alignment: type_details.alignment,
171 },
172 metadata,
173 ))
174 }
175 IoTypeMetadataKind::ArrayU8x8 => Some((IoTypeDetails::bytes(8), metadata)),
176 IoTypeMetadataKind::ArrayU8x16 => Some((IoTypeDetails::bytes(16), metadata)),
177 IoTypeMetadataKind::ArrayU8x32 => Some((IoTypeDetails::bytes(32), metadata)),
178 IoTypeMetadataKind::ArrayU8x64 => Some((IoTypeDetails::bytes(64), metadata)),
179 IoTypeMetadataKind::ArrayU8x128 => Some((IoTypeDetails::bytes(128), metadata)),
180 IoTypeMetadataKind::ArrayU8x256 => Some((IoTypeDetails::bytes(256), metadata)),
181 IoTypeMetadataKind::ArrayU8x512 => Some((IoTypeDetails::bytes(512), metadata)),
182 IoTypeMetadataKind::ArrayU8x1024 => Some((IoTypeDetails::bytes(1024), metadata)),
183 IoTypeMetadataKind::ArrayU8x2028 => Some((IoTypeDetails::bytes(2028), metadata)),
184 IoTypeMetadataKind::ArrayU8x4096 => Some((IoTypeDetails::bytes(4096), metadata)),
185 IoTypeMetadataKind::VariableBytes8b => {
186 if metadata.is_empty() {
187 return None;
188 }
189
190 let num_bytes = metadata[0] as u32;
191 metadata = forward_option!(skip_n_bytes(metadata, size_of::<u8>()));
192
193 Some((IoTypeDetails::bytes(num_bytes), metadata))
194 }
195 IoTypeMetadataKind::VariableBytes16b => {
196 if metadata.is_empty() {
197 return None;
198 }
199
200 let mut num_bytes = [0; size_of::<u16>()];
201 (metadata, _) =
202 forward_option!(copy_n_bytes(metadata, &mut num_bytes, size_of::<u16>()));
203 let num_bytes = u16::from_le_bytes(num_bytes) as u32;
204
205 Some((IoTypeDetails::bytes(num_bytes), metadata))
206 }
207 IoTypeMetadataKind::VariableBytes32b => {
208 if metadata.is_empty() {
209 return None;
210 }
211
212 let mut num_bytes = [0; size_of::<u32>()];
213 (metadata, _) =
214 forward_option!(copy_n_bytes(metadata, &mut num_bytes, size_of::<u32>()));
215 let num_bytes = u32::from_le_bytes(num_bytes);
216
217 Some((IoTypeDetails::bytes(num_bytes), metadata))
218 }
219 IoTypeMetadataKind::VariableBytes0 => Some((IoTypeDetails::bytes(0), metadata)),
220 IoTypeMetadataKind::VariableBytes512 => Some((IoTypeDetails::bytes(512), metadata)),
221 IoTypeMetadataKind::VariableBytes1024 => Some((IoTypeDetails::bytes(1024), metadata)),
222 IoTypeMetadataKind::VariableBytes2028 => Some((IoTypeDetails::bytes(2028), metadata)),
223 IoTypeMetadataKind::VariableBytes4096 => Some((IoTypeDetails::bytes(4096), metadata)),
224 IoTypeMetadataKind::VariableBytes8192 => Some((IoTypeDetails::bytes(8192), metadata)),
225 IoTypeMetadataKind::VariableBytes16384 => Some((IoTypeDetails::bytes(16384), metadata)),
226 IoTypeMetadataKind::VariableBytes32768 => Some((IoTypeDetails::bytes(32768), metadata)),
227 IoTypeMetadataKind::VariableBytes65536 => Some((IoTypeDetails::bytes(65536), metadata)),
228 IoTypeMetadataKind::VariableBytes131072 => Some((IoTypeDetails::bytes(131_072), metadata)),
229 IoTypeMetadataKind::VariableBytes262144 => Some((IoTypeDetails::bytes(262_144), metadata)),
230 IoTypeMetadataKind::VariableBytes524288 => Some((IoTypeDetails::bytes(524_288), metadata)),
231 IoTypeMetadataKind::VariableBytes1048576 => {
232 Some((IoTypeDetails::bytes(1_048_576), metadata))
233 }
234 IoTypeMetadataKind::VariableElements0 => {
235 if metadata.is_empty() {
236 return None;
237 }
238
239 (_, metadata) = forward_option!(decode_type_details(metadata));
240 Some((
241 IoTypeDetails {
242 recommended_capacity: 0,
243 alignment: NonZeroU8::new(1).expect("Not zero; qed"),
244 },
245 metadata,
246 ))
247 }
248 IoTypeMetadataKind::FixedCapacityBytes8b | IoTypeMetadataKind::FixedCapacityString8b => {
249 if metadata.is_empty() {
250 return None;
251 }
252
253 let num_bytes = metadata[0] as u32;
254 metadata = forward_option!(skip_n_bytes(metadata, size_of::<u8>()));
255
256 Some((
257 IoTypeDetails::bytes(num_bytes + size_of::<u8>() as u32),
258 metadata,
259 ))
260 }
261 IoTypeMetadataKind::FixedCapacityBytes16b | IoTypeMetadataKind::FixedCapacityString16b => {
262 if metadata.is_empty() {
263 return None;
264 }
265
266 let mut num_bytes = [0; size_of::<u16>()];
267 (metadata, _) =
268 forward_option!(copy_n_bytes(metadata, &mut num_bytes, size_of::<u16>()));
269 let num_bytes = u16::from_le_bytes(num_bytes) as u32;
270
271 Some((
272 IoTypeDetails {
273 recommended_capacity: num_bytes + size_of::<u16>() as u32,
274 alignment: NonZeroU8::new(2).expect("Not zero; qed"),
275 },
276 metadata,
277 ))
278 }
279 IoTypeMetadataKind::Address | IoTypeMetadataKind::Balance => Some((
280 IoTypeDetails {
281 recommended_capacity: 16,
282 alignment: NonZeroU8::new(8).expect("Not zero; qed"),
283 },
284 metadata,
285 )),
286 }
287}
288
289#[inline(always)]
290const fn struct_type_details(
291 mut input: &[u8],
292 field_count: Option<u8>,
293 tuple: bool,
294) -> Option<(IoTypeDetails, &[u8])> {
295 if input.is_empty() {
296 return None;
297 }
298
299 let struct_name_length = input[0] as usize;
301 input = forward_option!(skip_n_bytes(input, 1 + struct_name_length));
302
303 let mut field_count = if let Some(field_count) = field_count {
304 field_count
305 } else {
306 if input.is_empty() {
307 return None;
308 }
309
310 let field_count = input[0];
311 input = forward_option!(skip_n_bytes(input, 1));
312
313 field_count
314 };
315
316 let mut capacity = 0u32;
318 let mut alignment = 1u8;
319 while field_count > 0 {
320 if input.is_empty() {
321 return None;
322 }
323
324 if !tuple {
326 let field_name_length = input[0] as usize;
327 input = forward_option!(skip_n_bytes(input, 1 + field_name_length));
328 }
329
330 let type_details;
332 (type_details, input) = forward_option!(decode_type_details(input));
333 capacity = forward_option!(capacity.checked_add(type_details.recommended_capacity));
334 alignment = if type_details.alignment.get() > alignment {
336 type_details.alignment.get()
337 } else {
338 alignment
339 };
340
341 field_count -= 1;
342 }
343
344 Some((
345 IoTypeDetails {
346 recommended_capacity: capacity,
347 alignment: NonZeroU8::new(alignment).expect("At least zero; qed"),
348 },
349 input,
350 ))
351}
352
353#[inline(always)]
354const fn enum_capacity(
355 mut input: &[u8],
356 variant_count: Option<u8>,
357 has_fields: bool,
358) -> Option<(IoTypeDetails, &[u8])> {
359 if input.is_empty() {
360 return None;
361 }
362
363 let enum_name_length = input[0] as usize;
365 input = forward_option!(skip_n_bytes(input, 1 + enum_name_length));
366
367 let mut variant_count = if let Some(variant_count) = variant_count {
368 variant_count
369 } else {
370 if input.is_empty() {
371 return None;
372 }
373
374 let variant_count = input[0];
375 input = forward_option!(skip_n_bytes(input, 1));
376
377 variant_count
378 };
379
380 let mut enum_capacity = None;
382 let mut alignment = 1u8;
383 while variant_count > 0 {
384 if input.is_empty() {
385 return None;
386 }
387
388 let variant_type_details;
389
390 (variant_type_details, input) = forward_option!(struct_type_details(
392 input,
393 if has_fields { None } else { Some(0) },
394 false
395 ));
396 let variant_capacity = variant_type_details.recommended_capacity + 1;
398 alignment = if variant_type_details.alignment.get() > alignment {
400 variant_type_details.alignment.get()
401 } else {
402 alignment
403 };
404
405 match enum_capacity {
406 Some(capacity) => {
407 if capacity != variant_capacity {
408 return None;
409 }
410 }
411 None => {
412 enum_capacity.replace(variant_capacity);
413 }
414 }
415
416 variant_count -= 1;
417 }
418
419 let enum_capacity = match enum_capacity {
421 Some(enum_capacity) => enum_capacity,
422 None => 0,
423 };
424
425 Some((
426 IoTypeDetails {
427 recommended_capacity: enum_capacity,
428 alignment: NonZeroU8::new(alignment).expect("At least zero; qed"),
429 },
430 input,
431 ))
432}
433
434#[inline(always)]
436const fn copy_n_bytes<'i, 'o>(
437 mut input: &'i [u8],
438 mut output: &'o mut [u8],
439 n: usize,
440) -> Option<(&'i [u8], &'o mut [u8])> {
441 if n > input.len() || n > output.len() {
442 return None;
443 }
444
445 let source;
446 let target;
447 (source, input) = input.split_at(n);
448 (target, output) = output.split_at_mut(n);
449 target.copy_from_slice(source);
450
451 Some((input, output))
452}
453
454#[inline(always)]
456const fn skip_n_bytes(input: &[u8], n: usize) -> Option<&[u8]> {
457 if n > input.len() {
458 return None;
459 }
460
461 Some(input.split_at(n).1)
463}