tiders_svm_decode/
deserialize.rs

1use anyhow::{anyhow, Context, Result};
2
3/// Represents a parameter input with a name and dynamic type
4#[derive(Debug, Clone)]
5pub struct ParamInput {
6    pub name: String,
7    pub param_type: DynType,
8}
9
10#[cfg(feature = "pyo3")]
11impl<'py> pyo3::FromPyObject<'py> for ParamInput {
12    fn extract_bound(ob: &pyo3::Bound<'py, pyo3::PyAny>) -> pyo3::PyResult<Self> {
13        use pyo3::types::PyAnyMethods;
14
15        let name = ob.getattr("name")?.extract::<String>()?;
16        let param_type = ob.getattr("param_type")?.extract::<DynType>()?;
17        Ok(ParamInput { name, param_type })
18    }
19}
20
21/// Represents a dynamic type that can be deserialized from binary data
22#[derive(Debug, Clone, PartialEq)]
23pub enum DynType {
24    I8,
25    I16,
26    I32,
27    I64,
28    I128,
29    U8,
30    U16,
31    U32,
32    U64,
33    U128,
34    Bool,
35    /// Complex types
36    FixedArray(Box<DynType>, usize),
37    Array(Box<DynType>),
38    Struct(Vec<(String, DynType)>),
39    Enum(Vec<(String, Option<DynType>)>),
40    Option(Box<DynType>),
41}
42
43#[cfg(feature = "pyo3")]
44impl<'py> pyo3::FromPyObject<'py> for DynType {
45    fn extract_bound(ob: &pyo3::Bound<'py, pyo3::PyAny>) -> pyo3::PyResult<Self> {
46        use pyo3::types::PyAnyMethods;
47        use pyo3::types::PyTypeMethods;
48
49        let variant_str: String = ob.get_type().name()?.to_string();
50        // A str value means the type was passed as a string literal, not a class instance
51        let variant_str = if variant_str == "str" {
52            ob.to_string()
53        } else {
54            variant_str
55        };
56
57        match variant_str.as_str() {
58            "i8" => Ok(DynType::I8),
59            "i16" => Ok(DynType::I16),
60            "i32" => Ok(DynType::I32),
61            "i64" => Ok(DynType::I64),
62            "i128" => Ok(DynType::I128),
63            "u8" => Ok(DynType::U8),
64            "u16" => Ok(DynType::U16),
65            "u32" => Ok(DynType::U32),
66            "u64" => Ok(DynType::U64),
67            "u128" => Ok(DynType::U128),
68            "bool" => Ok(DynType::Bool),
69            "FixedArray" => {
70                let inner_bound = ob
71                    .getattr("element_type")
72                    .context("Failed to retrieve FixedArray element type")?;
73                let size: usize = ob
74                    .getattr("size")
75                    .context("Failed to retrieve size")?
76                    .extract::<usize>()?;
77                let inner_type = inner_bound.extract::<DynType>()?;
78                Ok(DynType::FixedArray(Box::new(inner_type), size))
79            }
80            "Array" => {
81                let inner_bound = ob
82                    .getattr("element_type")
83                    .context("Failed to retrieve Array element type")?;
84                let inner_type = inner_bound.extract::<DynType>()?;
85                Ok(DynType::Array(Box::new(inner_type)))
86            }
87            "Struct" => {
88                let py_fields = ob
89                    .getattr("fields")
90                    .context("Failed to retrieve Struct fields")?;
91                let mut fields: Vec<(String, DynType)> = Vec::new();
92                for field in py_fields.try_iter()? {
93                    match field {
94                        Ok(field) => {
95                            let name = field
96                                .getattr("name")
97                                .context("Failed to retrieve Struct field name")?
98                                .to_string();
99                            let param_type = field
100                                .getattr("element_type")
101                                .context("Failed to retrieve Struct field type")?
102                                .extract::<DynType>()?;
103                            fields.push((name, param_type));
104                        }
105                        Err(e) => {
106                            return Err(anyhow!(
107                                "Could not convert Struct fields into an iterator. Error: {e:?}"
108                            )
109                            .into())
110                        }
111                    }
112                }
113                Ok(DynType::Struct(fields))
114            }
115            "Enum" => {
116                let py_variants = ob
117                    .getattr("variants")
118                    .context("Failed to retrieve Enum variants")?;
119                let mut variants: Vec<(String, Option<DynType>)> = Vec::new();
120                for variant in py_variants.try_iter()? {
121                    match variant {
122                        Ok(variant) => {
123                            let name = variant
124                                .getattr("name")
125                                .context("Failed to retrieve Enum variant name")?
126                                .to_string();
127                            let param_type = variant
128                                .getattr("element_type")
129                                .context("Failed to retrieve Enum variant type")?;
130                            if param_type.to_string().as_str() == "None" {
131                                variants.push((name, None));
132                            } else {
133                                let param_type = param_type.extract::<DynType>()?;
134                                variants.push((name, Some(param_type)));
135                            }
136                        }
137                        Err(e) => {
138                            return Err(anyhow!(
139                                "Could not convert Enum variants into an iterator. Error: {e:?}"
140                            ))
141                            .map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(e.to_string()))
142                        }
143                    }
144                }
145                Ok(DynType::Enum(variants))
146            }
147            "Option" => {
148                let inner_bound = ob
149                    .getattr("element_type")
150                    .context("Failed to retrieve Option element type")?;
151                let inner_type = inner_bound.extract::<DynType>()?;
152                Ok(DynType::Option(Box::new(inner_type)))
153            }
154            _ => Err(anyhow!("Not yet implemented type: {variant_str}").into()),
155        }
156    }
157}
158
159/// Represents a dynamically deserialized value
160#[derive(Debug, Clone)]
161pub enum DynValue {
162    I8(i8),
163    I16(i16),
164    I32(i32),
165    I64(i64),
166    I128(i128),
167    U8(u8),
168    U16(u16),
169    U32(u32),
170    U64(u64),
171    U128(u128),
172    Bool(bool),
173    /// Complex values
174    Array(Vec<DynValue>),
175    Struct(Vec<(String, DynValue)>),
176    Enum(String, Option<Box<DynValue>>),
177    Option(Option<Box<DynValue>>),
178}
179
180/// Deserializes binary data into a vector of dynamic values based on the provided parameter types
181///
182/// # Arguments
183/// * `data` - The binary data to deserialize
184/// * `params` - The parameter types that define the structure of the data
185/// * `error_on_remaining` - Whether to error if there is remaining data in the buffer after parsing
186/// * given params.
187///
188/// # Returns
189/// A vector of deserialized values matching the parameter types
190///
191/// # Errors
192/// Returns an error if:
193/// * `error_on_remaining` is `true` and there is not enough data to deserialize all parameters
194/// * The data format doesn't match the expected parameter types
195/// * There is remaining data after deserializing all parameters
196pub fn deserialize_data(
197    data: &[u8],
198    params: &[ParamInput],
199    error_on_remaining: bool,
200) -> Result<Vec<DynValue>> {
201    let mut ix_values = Vec::with_capacity(params.len());
202    let mut remaining_data = data;
203
204    for param in params {
205        let (value, new_data) = deserialize_value(&param.param_type, remaining_data)?;
206        ix_values.push(value);
207        remaining_data = new_data;
208    }
209
210    if error_on_remaining && !remaining_data.is_empty() {
211        return Err(anyhow!(
212            "Remaining data after deserialization: {remaining_data:?}"
213        ));
214    }
215
216    Ok(ix_values)
217}
218
219/// Deserializes a single value of the specified type from binary data
220///
221/// # Arguments
222/// * `param_type` - The type of value to deserialize
223/// * `data` - The binary data to deserialize from
224///
225/// # Returns
226/// A tuple containing:
227/// * The deserialized value
228/// * The remaining data after deserialization
229///
230/// # Errors
231/// Returns an error if:
232/// * There is not enough data to deserialize the value
233/// * The data format doesn't match the expected type
234fn deserialize_value<'a>(param_type: &DynType, data: &'a [u8]) -> Result<(DynValue, &'a [u8])> {
235    match param_type {
236        DynType::Option(inner_type) => {
237            let value = data.first().context("Not enough data for option")?;
238            match value {
239                0 => Ok((DynValue::Option(None), &data[1..])),
240                1 => {
241                    let (value, new_data) = deserialize_value(inner_type, &data[1..])?;
242                    Ok((DynValue::Option(Some(Box::new(value))), new_data))
243                }
244                _ => Err(anyhow!("Invalid option value: {value}")),
245            }
246        }
247        DynType::I8 => {
248            if data.is_empty() {
249                return Err(anyhow!(
250                    "Not enough data for i8: expected 1 byte, got {}",
251                    data.len()
252                ));
253            }
254            let value = i8::from_le_bytes(data[..1].try_into().context("i8 conversion")?);
255
256            Ok((DynValue::I8(value), &data[1..]))
257        }
258        DynType::I16 => {
259            if data.len() < 2 {
260                return Err(anyhow!(
261                    "Not enough data for i16: expected 2 bytes, got {}",
262                    data.len()
263                ));
264            }
265            let value = i16::from_le_bytes(data[..2].try_into().context("i16 conversion")?);
266
267            Ok((DynValue::I16(value), &data[2..]))
268        }
269        DynType::I32 => {
270            if data.len() < 4 {
271                return Err(anyhow!(
272                    "Not enough data for i32: expected 4 bytes, got {}",
273                    data.len()
274                ));
275            }
276            let value = i32::from_le_bytes(data[..4].try_into().context("i32 conversion")?);
277
278            Ok((DynValue::I32(value), &data[4..]))
279        }
280        DynType::I64 => {
281            if data.len() < 8 {
282                return Err(anyhow!(
283                    "Not enough data for i64: expected 8 bytes, got {}",
284                    data.len()
285                ));
286            }
287            let value = i64::from_le_bytes(data[..8].try_into().context("i64 conversion")?);
288
289            Ok((DynValue::I64(value), &data[8..]))
290        }
291        DynType::I128 => {
292            if data.len() < 16 {
293                return Err(anyhow!(
294                    "Not enough data for i128: expected 16 bytes, got {}",
295                    data.len()
296                ));
297            }
298            let value = i128::from_le_bytes(data[..16].try_into().context("i128 conversion")?);
299
300            Ok((DynValue::I128(value), &data[16..]))
301        }
302        DynType::U8 => {
303            if data.is_empty() {
304                return Err(anyhow!("Not enough data for u8: expected 1 byte, got 0"));
305            }
306            let value = data[0];
307            Ok((DynValue::U8(value), &data[1..]))
308        }
309        DynType::U16 => {
310            if data.len() < 2 {
311                return Err(anyhow!(
312                    "Not enough data for u16: expected 2 bytes, got {}",
313                    data.len()
314                ));
315            }
316            let value = u16::from_le_bytes(data[..2].try_into().context("u16 conversion")?);
317
318            Ok((DynValue::U16(value), &data[2..]))
319        }
320        DynType::U32 => {
321            if data.len() < 4 {
322                return Err(anyhow!(
323                    "Not enough data for u32: expected 4 bytes, got {}",
324                    data.len()
325                ));
326            }
327            let value = u32::from_le_bytes(data[..4].try_into().context("u32 conversion")?);
328
329            Ok((DynValue::U32(value), &data[4..]))
330        }
331        DynType::U64 => {
332            if data.len() < 8 {
333                return Err(anyhow!(
334                    "Not enough data for u64: expected 8 bytes, got {}",
335                    data.len()
336                ));
337            }
338            let value = u64::from_le_bytes(data[..8].try_into().context("u64 conversion")?);
339
340            Ok((DynValue::U64(value), &data[8..]))
341        }
342        DynType::U128 => {
343            if data.len() < 16 {
344                return Err(anyhow!(
345                    "Not enough data for u128: expected 16 bytes, got {}",
346                    data.len()
347                ));
348            }
349            let value = u128::from_le_bytes(data[..16].try_into().context("u128 conversion")?);
350
351            Ok((DynValue::U128(value), &data[16..]))
352        }
353        DynType::Bool => {
354            if data.is_empty() {
355                return Err(anyhow!("Not enough data for bool: expected 1 byte, got 0"));
356            }
357            let value = data[0] != 0;
358            Ok((DynValue::Bool(value), &data[1..]))
359        }
360        DynType::FixedArray(inner_type, size) => {
361            let inner_type_size = check_type_size(inner_type)?;
362            let total_size = inner_type_size * size;
363
364            if data.len() < total_size {
365                return Err(anyhow!(
366                    "Not enough data for fixed array: expected {} bytes, got {}",
367                    total_size,
368                    data.len()
369                ));
370            }
371            let value = data[..total_size]
372                .to_vec()
373                .chunks(inner_type_size)
374                .map(|chunk| {
375                    let (value, _) = deserialize_value(inner_type, chunk)?;
376                    Ok(value)
377                })
378                .collect::<Result<Vec<DynValue>>>()?;
379            Ok((DynValue::Array(value), &data[total_size..]))
380        }
381        DynType::Array(inner_type) => {
382            if data.len() < 4 {
383                return Err(anyhow!(
384                    "Not enough data for vector length: expected 4 bytes, got {}",
385                    data.len()
386                ));
387            }
388            let length =
389                u32::from_le_bytes(data[..4].try_into().context("array length conversion")?)
390                    as usize;
391            let mut remaining_data = &data[4..];
392
393            let mut values = Vec::with_capacity(length);
394            for _ in 0..length {
395                let (value, new_data) = deserialize_value(inner_type, remaining_data)?;
396                values.push(value);
397                remaining_data = new_data;
398            }
399
400            Ok((DynValue::Array(values), remaining_data))
401        }
402        DynType::Struct(fields) => {
403            let mut values = Vec::new();
404            let mut remaining_data = data;
405            for field in fields {
406                let (value, new_data) = deserialize_value(&field.1, remaining_data)?;
407                values.push((field.0.clone(), value));
408                remaining_data = new_data;
409            }
410            Ok((DynValue::Struct(values), remaining_data))
411        }
412        DynType::Enum(variants) => {
413            if data.is_empty() {
414                return Err(anyhow!(
415                    "Not enough data for enum: expected at least 1 byte for variant index"
416                ));
417            }
418            let variant_index = data[0] as usize;
419            let remaining_data = &data[1..];
420
421            if variant_index >= variants.len() {
422                return Err(anyhow!("Invalid enum variant index: {variant_index}"));
423            }
424
425            let (variant_name, variant_type) = &variants[variant_index];
426
427            if let Some(variant_type) = variant_type {
428                let (variant_value, new_data) = deserialize_value(variant_type, remaining_data)?;
429                Ok((
430                    DynValue::Enum(variant_name.clone(), Some(Box::new(variant_value))),
431                    new_data,
432                ))
433            } else {
434                Ok((DynValue::Enum(variant_name.clone(), None), remaining_data))
435            }
436        }
437    }
438}
439
440fn check_type_size(param_type: &DynType) -> Result<usize> {
441    match param_type {
442        DynType::U8 | DynType::I8 | DynType::Bool => Ok(1),
443        DynType::U16 | DynType::I16 => Ok(2),
444        DynType::U32 | DynType::I32 => Ok(4),
445        DynType::U64 | DynType::I64 => Ok(8),
446        DynType::U128 | DynType::I128 => Ok(16),
447        _ => Err(anyhow!("Unsupported primitive type for fixed array")),
448    }
449}