substrate_parser/
compacts.rs

1//! [`Compact`] search and processing.
2use external_memory_tools::{AddressableBuffer, BufferError, ExternalMemory};
3use parity_scale_codec::{Compact, Decode, HasCompact};
4
5use crate::error::ParserError;
6
7/// Compact found in data.
8#[derive(Debug)]
9pub struct FoundCompact<T: HasCompact> {
10    /// Compact found and decoded.
11    pub compact: T,
12
13    /// Position of first data element after the compact part.
14    pub start_next_unit: usize,
15}
16
17/// Maximum possible encoded compact length, `Compact::compact_len(&u128::MAX)`.
18///
19/// Would not make sense to check slices of higher length for a compact.
20pub const MAX_COMPACT_LEN: usize = 17;
21
22/// Search bytes slice for compact at given position by brute force.
23///
24/// Tries to find shortest slice that could be decoded as a compact.
25/// Does not shift current parser position.
26pub fn find_compact<T, B, E>(
27    data: &B,
28    ext_memory: &mut E,
29    position: usize,
30) -> Result<FoundCompact<T>, ParserError<E>>
31where
32    B: AddressableBuffer<E>,
33    E: ExternalMemory,
34    T: HasCompact,
35    Compact<T>: Decode,
36{
37    if data.total_len() < position {
38        return Err(ParserError::Buffer(BufferError::OutOfRange {
39            position,
40            total_length: data.total_len(),
41        }));
42    }
43    let mut out = None;
44    for i in 0..(data.total_len() - position) {
45        // checking if length exceeds maximum possible length for a compact
46        if i > MAX_COMPACT_LEN {
47            break;
48        }
49
50        let hippo = data.read_slice(ext_memory, position, i + 1)?;
51        let unhippo = <Compact<T>>::decode(&mut hippo.as_ref());
52        if let Ok(hurray) = unhippo {
53            let start_next_unit = {
54                if data.total_len() - position == i {
55                    data.total_len()
56                } else {
57                    position + i + 1
58                }
59            };
60            out = Some(FoundCompact {
61                compact: hurray.0,
62                start_next_unit,
63            });
64            break;
65        }
66    }
67    match out {
68        Some(c) => Ok(c),
69        None => Err(ParserError::NoCompact { position }),
70    }
71}
72
73/// Find compact and move current parser position accordingly.
74pub fn get_compact<T, B, E>(
75    data: &B,
76    ext_memory: &mut E,
77    position: &mut usize,
78) -> Result<T, ParserError<E>>
79where
80    B: AddressableBuffer<E>,
81    E: ExternalMemory,
82    T: HasCompact,
83    Compact<T>: Decode,
84{
85    let found_compact = find_compact::<T, B, E>(data, ext_memory, *position)?;
86    *position = found_compact.start_next_unit;
87    Ok(found_compact.compact)
88}