Skip to main content

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