Skip to main content

cherry_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        // If the type name is str, it means it's a custom type, and we need to get the actual DynType value
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: {:?}",
108                                e
109                            )
110                            .into())
111                        }
112                    }
113                }
114                Ok(DynType::Struct(fields))
115            }
116            "Enum" => {
117                let py_variants = ob
118                    .getattr("variants")
119                    .context("Failed to retrieve Enum variants")?;
120                let mut variants: Vec<(String, Option<DynType>)> = Vec::new();
121                for variant in py_variants.try_iter()? {
122                    match variant {
123                        Ok(variant) => {
124                            let name = variant
125                                .getattr("name")
126                                .context("Failed to retrieve Enum variant name")?
127                                .to_string();
128                            let param_type = variant
129                                .getattr("element_type")
130                                .context("Failed to retrieve Enum variant type")?;
131                            match param_type.to_string().as_str() {
132                                "None" => variants.push((name, None)),
133                                _ => {
134                                    let param_type = param_type.extract::<DynType>()?;
135                                    variants.push((name, Some(param_type)));
136                                }
137                            }
138                        }
139                        Err(e) => {
140                            return Err(anyhow!(
141                                "Could not convert Enum variants into an iterator. Error: {:?}",
142                                e
143                            )
144                            .into())
145                        }
146                    }
147                }
148                Ok(DynType::Enum(variants))
149            }
150            "Option" => {
151                let inner_bound = ob
152                    .getattr("element_type")
153                    .context("Failed to retrieve Option element type")?;
154                let inner_type = inner_bound.extract::<DynType>()?;
155                Ok(DynType::Option(Box::new(inner_type)))
156            }
157            _ => Err(anyhow!("Not yet implemented type: {}", variant_str).into()),
158        }
159    }
160}
161
162/// Represents a dynamically deserialized value
163#[derive(Debug, Clone)]
164pub enum DynValue {
165    I8(i8),
166    I16(i16),
167    I32(i32),
168    I64(i64),
169    I128(i128),
170    U8(u8),
171    U16(u16),
172    U32(u32),
173    U64(u64),
174    U128(u128),
175    Bool(bool),
176    /// Complex values
177    Array(Vec<DynValue>),
178    Struct(Vec<(String, DynValue)>),
179    Enum(String, Option<Box<DynValue>>),
180    Option(Option<Box<DynValue>>),
181}
182
183/// Deserializes binary data into a vector of dynamic values based on the provided parameter types
184///
185/// # Arguments
186/// * `data` - The binary data to deserialize
187/// * `params` - The parameter types that define the structure of the data
188/// * `error_on_remaining` - Weather to error if there is remaining data in the buffer after parsing
189/// * given params.
190///
191/// # Returns
192/// A vector of deserialized values matching the parameter types
193///
194/// # Errors
195/// Returns an error if:
196/// * `error_on_remaining` is `true` and there is not enough data to deserialize all parameters
197/// * The data format doesn't match the expected parameter types
198/// * There is remaining data after deserializing all parameters
199pub fn deserialize_data(
200    data: &[u8],
201    params: &[ParamInput],
202    error_on_remaining: bool,
203) -> Result<Vec<DynValue>> {
204    let mut ix_values = Vec::with_capacity(params.len());
205    let mut remaining_data = data;
206
207    for param in params {
208        // Deserialize value based on type
209        let (value, new_data) = deserialize_value(&param.param_type, remaining_data)?;
210        ix_values.push(value);
211        remaining_data = new_data;
212    }
213
214    if error_on_remaining && !remaining_data.is_empty() {
215        return Err(anyhow!(
216            "Remaining data after deserialization: {:?}",
217            remaining_data
218        ));
219    }
220
221    Ok(ix_values)
222}
223
224/// Deserializes a single value of the specified type from binary data
225///
226/// # Arguments
227/// * `param_type` - The type of value to deserialize
228/// * `data` - The binary data to deserialize from
229///
230/// # Returns
231/// A tuple containing:
232/// * The deserialized value
233/// * The remaining data after deserialization
234///
235/// # Errors
236/// Returns an error if:
237/// * There is not enough data to deserialize the value
238/// * The data format doesn't match the expected type
239fn deserialize_value<'a>(param_type: &DynType, data: &'a [u8]) -> Result<(DynValue, &'a [u8])> {
240    match param_type {
241        DynType::Option(inner_type) => {
242            let value = data.first().context("Not enough data for option")?;
243            match value {
244                0 => Ok((DynValue::Option(None), &data[1..])),
245                1 => {
246                    let (value, new_data) = deserialize_value(inner_type, &data[1..])?;
247                    Ok((DynValue::Option(Some(Box::new(value))), new_data))
248                }
249                _ => Err(anyhow!("Invalid option value: {}", value)),
250            }
251        }
252        DynType::I8 => {
253            if data.is_empty() {
254                return Err(anyhow!(
255                    "Not enough data for i8: expected 1 byte, got {}",
256                    data.len()
257                ));
258            }
259            let value = i8::from_le_bytes(data[..1].try_into().unwrap());
260            Ok((DynValue::I8(value), &data[1..]))
261        }
262        DynType::I16 => {
263            if data.len() < 2 {
264                return Err(anyhow!(
265                    "Not enough data for i16: expected 2 bytes, got {}",
266                    data.len()
267                ));
268            }
269            let value = i16::from_le_bytes(data[..2].try_into().unwrap());
270            Ok((DynValue::I16(value), &data[2..]))
271        }
272        DynType::I32 => {
273            if data.len() < 4 {
274                return Err(anyhow!(
275                    "Not enough data for i32: expected 4 bytes, got {}",
276                    data.len()
277                ));
278            }
279            let value = i32::from_le_bytes(data[..4].try_into().unwrap());
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().unwrap());
290            Ok((DynValue::I64(value), &data[8..]))
291        }
292        DynType::I128 => {
293            if data.len() < 16 {
294                return Err(anyhow!(
295                    "Not enough data for i128: expected 16 bytes, got {}",
296                    data.len()
297                ));
298            }
299            let value = i128::from_le_bytes(data[..16].try_into().unwrap());
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().unwrap());
317            Ok((DynValue::U16(value), &data[2..]))
318        }
319        DynType::U32 => {
320            if data.len() < 4 {
321                return Err(anyhow!(
322                    "Not enough data for u32: expected 4 bytes, got {}",
323                    data.len()
324                ));
325            }
326            let value = u32::from_le_bytes(data[..4].try_into().unwrap());
327            Ok((DynValue::U32(value), &data[4..]))
328        }
329        DynType::U64 => {
330            if data.len() < 8 {
331                return Err(anyhow!(
332                    "Not enough data for u64: expected 8 bytes, got {}",
333                    data.len()
334                ));
335            }
336            let value = u64::from_le_bytes(data[..8].try_into().unwrap());
337            Ok((DynValue::U64(value), &data[8..]))
338        }
339        DynType::U128 => {
340            if data.len() < 16 {
341                return Err(anyhow!(
342                    "Not enough data for u128: expected 16 bytes, got {}",
343                    data.len()
344                ));
345            }
346            let value = u128::from_le_bytes(data[..16].try_into().unwrap());
347            Ok((DynValue::U128(value), &data[16..]))
348        }
349        DynType::Bool => {
350            if data.is_empty() {
351                return Err(anyhow!("Not enough data for bool: expected 1 byte, got 0"));
352            }
353            let value = data[0] != 0;
354            Ok((DynValue::Bool(value), &data[1..]))
355        }
356        DynType::FixedArray(inner_type, size) => {
357            let inner_type_size = check_type_size(inner_type)?;
358            let total_size = inner_type_size * size;
359
360            if data.len() < total_size {
361                return Err(anyhow!(
362                    "Not enough data for fixed array: expected {} bytes, got {}",
363                    total_size,
364                    data.len()
365                ));
366            }
367            let value = data[..total_size]
368                .to_vec()
369                .chunks(inner_type_size)
370                .map(|chunk| {
371                    let (value, _) = deserialize_value(inner_type, chunk)?;
372                    Ok(value)
373                })
374                .collect::<Result<Vec<DynValue>>>()?;
375            Ok((DynValue::Array(value), &data[total_size..]))
376        }
377        DynType::Array(inner_type) => {
378            if data.len() < 4 {
379                return Err(anyhow!(
380                    "Not enough data for vector length: expected 4 bytes, got {}",
381                    data.len()
382                ));
383            }
384            let length = u32::from_le_bytes(data[..4].try_into().unwrap()) as usize;
385            let mut remaining_data = &data[4..];
386
387            let mut values = Vec::with_capacity(length);
388            for _ in 0..length {
389                let (value, new_data) = deserialize_value(inner_type, remaining_data)?;
390                values.push(value);
391                remaining_data = new_data;
392            }
393
394            Ok((DynValue::Array(values), remaining_data))
395        }
396        DynType::Struct(fields) => {
397            let mut values = Vec::new();
398            let mut remaining_data = data;
399            for field in fields {
400                let (value, new_data) = deserialize_value(&field.1, remaining_data)?;
401                values.push((field.0.clone(), value));
402                remaining_data = new_data;
403            }
404            Ok((DynValue::Struct(values), remaining_data))
405        }
406        DynType::Enum(variants) => {
407            if data.is_empty() {
408                return Err(anyhow!(
409                    "Not enough data for enum: expected at least 1 byte for variant index"
410                ));
411            }
412            let variant_index = data[0] as usize;
413            let remaining_data = &data[1..];
414
415            if variant_index >= variants.len() {
416                return Err(anyhow!("Invalid enum variant index: {}", variant_index));
417            }
418
419            let (variant_name, variant_type) = &variants[variant_index];
420
421            if let Some(variant_type) = variant_type {
422                let (variant_value, new_data) = deserialize_value(variant_type, remaining_data)?;
423                Ok((
424                    DynValue::Enum(variant_name.clone(), Some(Box::new(variant_value))),
425                    new_data,
426                ))
427            } else {
428                Ok((DynValue::Enum(variant_name.clone(), None), remaining_data))
429            }
430        }
431    }
432}
433
434fn check_type_size(param_type: &DynType) -> Result<usize> {
435    match param_type {
436        DynType::U8 => Ok(1),
437        DynType::U16 => Ok(2),
438        DynType::U32 => Ok(4),
439        DynType::U64 => Ok(8),
440        DynType::U128 => Ok(16),
441        DynType::I8 => Ok(1),
442        DynType::I16 => Ok(2),
443        DynType::I32 => Ok(4),
444        DynType::I64 => Ok(8),
445        DynType::I128 => Ok(16),
446        DynType::Bool => Ok(1),
447        _ => Err(anyhow!("Unsupported primitive type for fixed array")),
448    }
449}