Skip to main content

micromegas_transit/
parser.rs

1use crate::{
2    UserDefinedType, advance_window, read_advance_string, read_any, read_consume_pod,
3    value::{Object, Value},
4};
5use anyhow::{Context, Result, bail};
6use std::{collections::HashMap, sync::Arc};
7
8pub type CustomReader =
9    Arc<dyn Fn(&UserDefinedType, &[UserDefinedType], &HashMap<u64, Value>, &[u8]) -> Result<Value>>;
10pub type CustomReaderMap = HashMap<String, CustomReader>;
11
12pub fn read_dependencies(
13    custom_readers: &CustomReaderMap,
14    udts: &[UserDefinedType],
15    buffer: &[u8],
16) -> Result<HashMap<u64, Value>> {
17    let mut hash = HashMap::new();
18    let mut offset = 0;
19    while offset < buffer.len() {
20        let type_index = buffer[offset] as usize;
21        if type_index >= udts.len() {
22            bail!(
23                "Invalid type index parsing transit dependencies: {}",
24                type_index
25            );
26        }
27        offset += 1;
28        let udt = &udts[type_index];
29        let object_size = match udt.size {
30            0 => {
31                //dynamic size
32                unsafe {
33                    let size_ptr = buffer.as_ptr().add(offset);
34                    let obj_size = read_any::<u32>(size_ptr);
35                    offset += std::mem::size_of::<u32>();
36                    obj_size as usize
37                }
38            }
39            static_size => static_size,
40        };
41
42        match udt.name.as_str() {
43            "StaticString" => unsafe {
44                let id_ptr = buffer.as_ptr().add(offset);
45                let string_id = read_any::<u64>(id_ptr);
46                let nb_utf8_bytes = object_size - std::mem::size_of::<usize>();
47                let utf8_ptr = buffer.as_ptr().add(offset + std::mem::size_of::<usize>());
48                let slice = std::ptr::slice_from_raw_parts(utf8_ptr, nb_utf8_bytes);
49                let string =
50                    String::from(std::str::from_utf8(&*slice).with_context(|| "str::from_utf8")?);
51                let insert_res = hash.insert(string_id, Value::String(Arc::new(string)));
52                assert!(insert_res.is_none());
53            },
54            "StaticStringDependency" => {
55                let mut window = advance_window(buffer, offset);
56                let string_id: u64 = read_consume_pod(&mut window);
57                let string = read_advance_string(&mut window).with_context(|| "parsing string")?;
58
59                let insert_res = hash.insert(string_id, Value::String(Arc::new(string)));
60                assert!(insert_res.is_none());
61            }
62            type_name => {
63                if let Some(reader) = custom_readers.get(type_name) {
64                    let window = advance_window(buffer, offset);
65                    if let Value::Object(obj) = (*reader)(udt, udts, &hash, window)
66                        .with_context(|| "parsing custom dependency")?
67                    {
68                        let id: u64 = obj
69                            .get("id")
70                            .with_context(|| "reading id of custom dependency")?;
71                        let value = obj
72                            .get_ref("value")
73                            .with_context(|| "reading value of custom dependency")?
74                            .clone();
75                        let insert_res = hash.insert(id, value);
76                        assert!(insert_res.is_none());
77                    } else {
78                        anyhow::bail!("custom dependency is not an object");
79                    }
80                } else {
81                    if udt.size == 0 {
82                        anyhow::bail!("invalid user-defined type {:?}", udt);
83                    }
84                    let instance =
85                        parse_pod_instance(udt, udts, &hash, &buffer[offset..offset + udt.size])
86                            .with_context(|| "parse_pod_instance")?;
87                    if let Value::Object(obj) = instance {
88                        let insert_res = hash.insert(obj.get::<u64>("id")?, Value::Object(obj));
89                        assert!(insert_res.is_none());
90                    }
91                }
92            }
93        }
94        offset += object_size;
95    }
96
97    Ok(hash)
98}
99
100fn parse_custom_instance(
101    custom_readers: &CustomReaderMap,
102    udt: &UserDefinedType,
103    udts: &[UserDefinedType],
104    dependencies: &HashMap<u64, Value>,
105    object_window: &[u8],
106) -> Result<Value> {
107    if let Some(reader) = custom_readers.get(&*udt.name) {
108        (*reader)(udt, udts, dependencies, object_window)
109    } else {
110        log::warn!("unknown custom object {}", &udt.name);
111        Ok(Value::Object(Arc::new(Object {
112            type_name: udt.name.clone(),
113            members: vec![],
114        })))
115    }
116}
117
118pub fn parse_pod_instance(
119    udt: &UserDefinedType,
120    udts: &[UserDefinedType],
121    dependencies: &HashMap<u64, Value>,
122    object_window: &[u8],
123) -> Result<Value> {
124    let mut members: Vec<(Arc<String>, Value)> = vec![];
125    for member_meta in &udt.members {
126        let name = member_meta.name.clone();
127        let type_name = member_meta.type_name.clone();
128        let value = if member_meta.is_reference {
129            if member_meta.size < std::mem::size_of::<u64>() {
130                bail!(
131                    "member references have to be at least 8 bytes {:?}",
132                    member_meta
133                );
134            }
135            let key = unsafe { read_any::<u64>(object_window.as_ptr().add(member_meta.offset)) };
136            if let Some(v) = dependencies.get(&key) {
137                v.clone()
138            } else {
139                bail!("dependency not found: member={member_meta:?} key={key}");
140            }
141        } else {
142            match type_name.as_str() {
143                "u8" | "uint8" => {
144                    assert_eq!(std::mem::size_of::<u8>(), member_meta.size);
145                    unsafe {
146                        Value::U8(read_any::<u8>(
147                            object_window.as_ptr().add(member_meta.offset),
148                        ))
149                    }
150                }
151                "u32" | "uint32" => {
152                    assert_eq!(std::mem::size_of::<u32>(), member_meta.size);
153                    unsafe {
154                        Value::U32(read_any::<u32>(
155                            object_window.as_ptr().add(member_meta.offset),
156                        ))
157                    }
158                }
159                "u64" | "uint64" => {
160                    assert_eq!(std::mem::size_of::<u64>(), member_meta.size);
161                    unsafe {
162                        Value::U64(read_any::<u64>(
163                            object_window.as_ptr().add(member_meta.offset),
164                        ))
165                    }
166                }
167                "i64" | "int64" => {
168                    assert_eq!(std::mem::size_of::<i64>(), member_meta.size);
169                    unsafe {
170                        Value::I64(read_any::<i64>(
171                            object_window.as_ptr().add(member_meta.offset),
172                        ))
173                    }
174                }
175                "f64" => {
176                    assert_eq!(std::mem::size_of::<f64>(), member_meta.size);
177                    unsafe {
178                        Value::F64(read_any::<f64>(
179                            object_window.as_ptr().add(member_meta.offset),
180                        ))
181                    }
182                }
183                non_intrinsic_member_type_name => {
184                    if let Some(index) = udts
185                        .iter()
186                        .position(|t| *t.name == non_intrinsic_member_type_name)
187                    {
188                        let member_udt = &udts[index];
189                        parse_pod_instance(
190                            member_udt,
191                            udts,
192                            dependencies,
193                            &object_window
194                                [member_meta.offset..member_meta.offset + member_udt.size],
195                        )
196                        .with_context(|| "parse_pod_instance")?
197                    } else {
198                        bail!("unknown member type {}", non_intrinsic_member_type_name);
199                    }
200                }
201            }
202        };
203        members.push((name, value));
204    }
205
206    if udt.is_reference {
207        // reference objects need a member called 'id' which is the key to the dependency
208        if let Some(id_index) = members.iter().position(|m| *m.0 == "id") {
209            return Ok(members[id_index].1.clone());
210        }
211        bail!("reference object has no 'id' member");
212    }
213
214    Ok(Value::Object(Arc::new(Object {
215        type_name: udt.name.clone(),
216        members,
217    })))
218}
219
220// parse_object_buffer calls fun for each object in the buffer until fun returns
221// `false`
222pub fn parse_object_buffer<F>(
223    custom_readers: &CustomReaderMap,
224    dependencies: &HashMap<u64, Value>,
225    udts: &[UserDefinedType],
226    buffer: &[u8],
227    mut fun: F,
228) -> Result<bool>
229where
230    F: FnMut(Value) -> Result<bool>,
231{
232    let mut offset = 0;
233    while offset < buffer.len() {
234        let type_index = buffer[offset] as usize;
235        if type_index >= udts.len() {
236            bail!("Invalid type index parsing transit objects: {}", type_index);
237        }
238        offset += 1;
239        let udt = &udts[type_index];
240        let (object_size, is_size_dynamic) = match udt.size {
241            0 => {
242                //dynamic size
243                unsafe {
244                    let size_ptr = buffer.as_ptr().add(offset);
245                    let obj_size = read_any::<u32>(size_ptr);
246                    offset += std::mem::size_of::<u32>();
247                    (obj_size as usize, true)
248                }
249            }
250            static_size => (static_size, false),
251        };
252        let instance = if is_size_dynamic {
253            parse_custom_instance(
254                custom_readers,
255                udt,
256                udts,
257                dependencies,
258                &buffer[offset..offset + object_size],
259            )
260            .with_context(|| "parse_custom_instance")?
261        } else {
262            parse_pod_instance(udt, udts, dependencies, &buffer[offset..offset + udt.size])
263                .with_context(|| "parse_pod_instance")?
264        };
265        if !fun(instance)? {
266            return Ok(false);
267        }
268        offset += object_size;
269    }
270    Ok(true)
271}