1use anyhow::{Context, Result};
3use bumpalo::Bump;
4use micromegas_transit::{
5 CustomReaderMap, UserDefinedType, parse_pod_instance, read_advance_string_in,
6 try_advance_window, try_read_consume_pod,
7 value::{Object, Value},
8};
9use std::{collections::HashMap, sync::Arc};
10
11use crate::property_set::PROPERTY_SET_DEP_TYPE_NAME;
12
13const DATA: &str = "data";
15const DESC: &str = "desc";
16const FORMAT: &str = "format";
17const ID: &str = "id";
18const LEVEL: &str = "level";
19const MSG: &str = "msg";
20const NAME: &str = "name";
21const PROPERTIES: &str = "properties";
22const TARGET: &str = "target";
23const TIME: &str = "time";
24const VALUE: &str = "value";
25
26const PROPERTY_SET_TYPE_NAME: &str = "property_set";
27
28fn parse_log_string_event<'a>(
29 bump: &'a Bump,
30 udt: &'a UserDefinedType,
31 _udts: &'a [UserDefinedType],
32 dependencies: &HashMap<u64, Value<'a>>,
33 mut object_window: &'a [u8],
34) -> Result<Value<'a>> {
35 let desc_id: u64 = try_read_consume_pod(&mut object_window).with_context(|| "desc_id")?;
36 let time: i64 = try_read_consume_pod(&mut object_window).with_context(|| "time")?;
37 let msg = std::str::from_utf8(object_window).with_context(|| "parsing legacy string")?;
39 let desc = *dependencies
40 .get(&desc_id)
41 .with_context(|| format!("desc member {desc_id} of LogStringEvent not found"))?;
42 let members = bump.alloc_slice_copy(&[
43 (TIME, Value::I64(time)),
44 (MSG, Value::String(msg)),
45 (DESC, desc),
46 ]);
47 Ok(Value::Object(bump.alloc(Object {
48 type_name: udt.name.as_str(),
49 members,
50 })))
51}
52
53fn parse_log_string_event_v2<'a>(
54 bump: &'a Bump,
55 udt: &'a UserDefinedType,
56 _udts: &'a [UserDefinedType],
57 dependencies: &HashMap<u64, Value<'a>>,
58 mut object_window: &'a [u8],
59) -> Result<Value<'a>> {
60 let desc_id: u64 = try_read_consume_pod(&mut object_window).with_context(|| "desc_id")?;
61 let time: i64 = try_read_consume_pod(&mut object_window).with_context(|| "time")?;
62 let msg = read_advance_string_in(bump, &mut object_window).with_context(|| "parsing string")?;
63 let desc = *dependencies
64 .get(&desc_id)
65 .with_context(|| format!("desc member {desc_id} of LogStringEvent not found"))?;
66 let members = bump.alloc_slice_copy(&[
67 (TIME, Value::I64(time)),
68 (MSG, Value::String(msg)),
69 (DESC, desc),
70 ]);
71 Ok(Value::Object(bump.alloc(Object {
72 type_name: udt.name.as_str(),
73 members,
74 })))
75}
76
77fn parse_log_string_interop_event_v3<'a>(
78 bump: &'a Bump,
79 udt: &'a UserDefinedType,
80 udts: &'a [UserDefinedType],
81 dependencies: &HashMap<u64, Value<'a>>,
82 mut object_window: &'a [u8],
83) -> Result<Value<'a>> {
84 let string_ref_metadata = udts
85 .iter()
86 .find(|t| *t.name == "StaticStringRef")
87 .with_context(
88 || "Can't parse log string interop event with no metadata for StaticStringRef",
89 )?;
90 let time: i64 = try_read_consume_pod(&mut object_window).with_context(|| "time")?;
91 let level: u8 = try_read_consume_pod(&mut object_window).with_context(|| "level")?;
92 let target_window = object_window
93 .get(0..string_ref_metadata.size)
94 .with_context(|| "object window shorter than StaticStringRef metadata")?;
95 let target = parse_pod_instance(bump, string_ref_metadata, udts, dependencies, target_window)
96 .with_context(|| "parse_pod_instance")?;
97 object_window = try_advance_window(object_window, string_ref_metadata.size)?;
98 let msg = read_advance_string_in(bump, &mut object_window)?;
99 let members = bump.alloc_slice_copy(&[
100 (TIME, Value::I64(time)),
101 (LEVEL, Value::U8(level)),
102 (TARGET, target),
103 (MSG, Value::String(msg)),
104 ]);
105 Ok(Value::Object(bump.alloc(Object {
106 type_name: udt.name.as_str(),
107 members,
108 })))
109}
110
111fn parse_tagged_log_interop_event<'a>(
112 bump: &'a Bump,
113 udt: &'a UserDefinedType,
114 udts: &'a [UserDefinedType],
115 dependencies: &HashMap<u64, Value<'a>>,
116 mut object_window: &'a [u8],
117) -> Result<Value<'a>> {
118 let string_ref_metadata = udts
119 .iter()
120 .find(|t| *t.name == "StaticStringRef")
121 .with_context(
122 || "Can't parse log string interop event with no metadata for StaticStringRef",
123 )?;
124 let time: i64 = try_read_consume_pod(&mut object_window).with_context(|| "time")?;
125 let level: u8 = try_read_consume_pod(&mut object_window).with_context(|| "level")?;
126 let target_window = object_window
127 .get(0..string_ref_metadata.size)
128 .with_context(|| "object window shorter than StaticStringRef metadata")?;
129 let target = parse_pod_instance(bump, string_ref_metadata, udts, dependencies, target_window)
130 .with_context(|| "parse_pod_instance")?;
131 object_window = try_advance_window(object_window, string_ref_metadata.size)?;
132 let properties_id: u64 =
133 try_read_consume_pod(&mut object_window).with_context(|| "properties_id")?;
134 let properties = *dependencies
135 .get(&properties_id)
136 .with_context(|| "fetching properties in parse_tagged_log_interop_event")?;
137 let msg = read_advance_string_in(bump, &mut object_window)?;
138 let members = bump.alloc_slice_copy(&[
139 (TIME, Value::I64(time)),
140 (LEVEL, Value::U8(level)),
141 (TARGET, target),
142 (PROPERTIES, properties),
143 (MSG, Value::String(msg)),
144 ]);
145 Ok(Value::Object(bump.alloc(Object {
146 type_name: udt.name.as_str(),
147 members,
148 })))
149}
150
151fn parse_tagged_log_string<'a>(
152 bump: &'a Bump,
153 udt: &'a UserDefinedType,
154 _udts: &'a [UserDefinedType],
155 dependencies: &HashMap<u64, Value<'a>>,
156 mut object_window: &'a [u8],
157) -> Result<Value<'a>> {
158 let desc_id: u64 = try_read_consume_pod(&mut object_window).with_context(|| "desc_id")?;
159 let desc = *dependencies
160 .get(&desc_id)
161 .with_context(|| "fetching desc in parse_tagged_log_string")?;
162 let properties_id: u64 =
163 try_read_consume_pod(&mut object_window).with_context(|| "properties_id")?;
164 let properties = *dependencies
165 .get(&properties_id)
166 .with_context(|| "fetching property set in parse_tagged_log_string")?;
167 let time: i64 = try_read_consume_pod(&mut object_window).with_context(|| "time")?;
168 let msg = read_advance_string_in(bump, &mut object_window)?;
169
170 let members = bump.alloc_slice_copy(&[
171 (TIME, Value::I64(time)),
172 (DESC, desc),
173 (PROPERTIES, properties),
174 (MSG, Value::String(msg)),
175 ]);
176 Ok(Value::Object(bump.alloc(Object {
177 type_name: udt.name.as_str(),
178 members,
179 })))
180}
181
182fn parse_log_string_interop_event<'a>(
183 bump: &'a Bump,
184 udt: &'a UserDefinedType,
185 udts: &'a [UserDefinedType],
186 dependencies: &HashMap<u64, Value<'a>>,
187 mut object_window: &'a [u8],
188) -> Result<Value<'a>> {
189 let stringid_metadata = udts
190 .iter()
191 .find(|t| *t.name == "StringId")
192 .with_context(|| "Can't parse log string interop event with no metadata for StringId")?;
193 let time: i64 = try_read_consume_pod(&mut object_window).with_context(|| "time")?;
194 let level: u32 = try_read_consume_pod(&mut object_window).with_context(|| "level")?;
195 let target_window = object_window
196 .get(0..stringid_metadata.size)
197 .with_context(|| "object window shorter than StringId metadata")?;
198 let target = parse_pod_instance(bump, stringid_metadata, udts, dependencies, target_window)
199 .with_context(|| "parse_pod_instance")?;
200 object_window = try_advance_window(object_window, stringid_metadata.size)?;
201 let msg =
203 std::str::from_utf8(object_window).with_context(|| "parsing legacy interop string")?;
204 let members = bump.alloc_slice_copy(&[
205 (TIME, Value::I64(time)),
206 (LEVEL, Value::U32(level)),
207 (TARGET, target),
208 (MSG, Value::String(msg)),
209 ]);
210 Ok(Value::Object(bump.alloc(Object {
211 type_name: udt.name.as_str(),
212 members,
213 })))
214}
215
216fn parse_property_set<'a>(
217 bump: &'a Bump,
218 _udt: &'a UserDefinedType,
219 udts: &'a [UserDefinedType],
220 dependencies: &HashMap<u64, Value<'a>>,
221 mut window: &'a [u8],
222) -> Result<Value<'a>> {
223 let property_layout = udts
224 .iter()
225 .find(|t| *t.name == "Property")
226 .with_context(|| "could not find Property layout")?;
227
228 let object_id: u64 = try_read_consume_pod(&mut window).with_context(|| "object_id")?;
229 let nb_properties =
230 try_read_consume_pod::<u32>(&mut window).with_context(|| "nb_properties")? as usize;
231 let property_size = property_layout.size;
232 if property_size == 0 || nb_properties > window.len() / property_size {
236 anyhow::bail!(
237 "invalid property_set: nb_properties={nb_properties} exceeds {}-byte window",
238 window.len()
239 );
240 }
241 let mut members = bumpalo::collections::Vec::with_capacity_in(nb_properties, bump);
242 for i in 0..nb_properties {
243 let begin = i * property_size;
244 let property_window = &window[begin..begin + property_size];
245 if let Value::Object(obj) =
246 parse_pod_instance(bump, property_layout, udts, dependencies, property_window)?
247 {
248 members.push((
249 obj.get::<&str>("name")?,
250 Value::String(obj.get::<&str>("value")?),
251 ));
252 } else {
253 anyhow::bail!("invalid property in propertyset");
254 }
255 }
256
257 let set: &Object = bump.alloc(Object {
258 type_name: PROPERTY_SET_TYPE_NAME,
259 members: members.into_bump_slice(),
260 });
261 let outer = bump.alloc_slice_copy(&[(ID, Value::U64(object_id)), (VALUE, Value::Object(set))]);
262 Ok(Value::Object(bump.alloc(Object {
263 type_name: PROPERTY_SET_DEP_TYPE_NAME.as_str(),
264 members: outer,
265 })))
266}
267
268fn parse_image_event<'a>(
269 bump: &'a Bump,
270 udt: &'a UserDefinedType,
271 _udts: &'a [UserDefinedType],
272 _dependencies: &HashMap<u64, Value<'a>>,
273 mut object_window: &'a [u8],
274) -> Result<Value<'a>> {
275 let time: i64 = try_read_consume_pod(&mut object_window).with_context(|| "time")?;
276 let name =
277 read_advance_string_in(bump, &mut object_window).with_context(|| "parsing image name")?;
278 let format =
279 read_advance_string_in(bump, &mut object_window).with_context(|| "parsing image format")?;
280 let len: u32 = try_read_consume_pod(&mut object_window).with_context(|| "len")?;
281 if len as usize > object_window.len() {
282 anyhow::bail!(
283 "invalid image blob: len={len} exceeds {}-byte window",
284 object_window.len()
285 );
286 }
287 let data: &[u8] = &object_window[..len as usize];
290 let members = bump.alloc_slice_copy(&[
291 (TIME, Value::I64(time)),
292 (NAME, Value::String(name)),
293 (FORMAT, Value::String(format)),
294 (DATA, Value::Bytes(data)),
295 ]);
296 Ok(Value::Object(bump.alloc(Object {
297 type_name: udt.name.as_str(),
298 members,
299 })))
300}
301
302pub fn make_custom_readers() -> CustomReaderMap {
304 let mut custom_readers: CustomReaderMap = HashMap::new();
305 custom_readers.insert("ImageEvent".into(), Arc::new(parse_image_event));
306 custom_readers.insert("LogStringEvent".into(), Arc::new(parse_log_string_event));
307 custom_readers.insert(
308 "LogStringEventV2".into(),
309 Arc::new(parse_log_string_event_v2),
310 );
311 custom_readers.insert(
312 "LogStringInteropEventV2".into(),
313 Arc::new(parse_log_string_interop_event),
314 );
315 custom_readers.insert(
316 "LogStringInteropEventV3".into(),
317 Arc::new(parse_log_string_interop_event_v3),
318 );
319 custom_readers.insert("TaggedLogString".into(), Arc::new(parse_tagged_log_string));
320 custom_readers.insert(
321 PROPERTY_SET_DEP_TYPE_NAME.to_string(),
322 Arc::new(parse_property_set),
323 );
324 custom_readers.insert(
325 "TaggedLogInteropEvent".into(),
326 Arc::new(parse_tagged_log_interop_event),
327 );
328 custom_readers
329}