Skip to main content

isr_core/
visit.rs

1//! Struct field decoding.
2//!
3//! Walks a [`Struct`] over a byte slice and produces a [`FieldValue`] (type
4//! plus decoded value) or [`FieldValueKind`] (type only) per field.
5
6use crate::{Base, Profile, Struct, Type};
7
8/// A struct field's decoded type and value.
9#[allow(missing_docs)]
10pub enum FieldValue<'a> {
11    Void,
12    Bool(bool),
13    Char8(u8),
14    Char16(u16),
15    Char32(u32),
16
17    I8(i8),
18    I16(i16),
19    I32(i32),
20    I64(i64),
21    I128(i128),
22
23    U8(u8),
24    U16(u16),
25    U32(u32),
26    U64(u64),
27    U128(u128),
28
29    F8(u8),
30    F16(u16),
31    F32(f32),
32    F64(f64),
33    F128(u128),
34
35    Enum {
36        type_name: &'a str,
37        size: u64,
38        value: u64,
39    },
40    Struct {
41        type_name: &'a str,
42        size: u64,
43        data: &'a [u8],
44    },
45    Array {
46        subtype: Box<FieldValueKind<'a>>,
47        dims: Vec<u64>,
48        size: u64,
49        data: &'a [u8],
50    },
51    Pointer {
52        subtype: Box<FieldValueKind<'a>>,
53        size: u64,
54        value: u64,
55    },
56    Bitfield {
57        bit_position: u64,
58        bit_length: u64,
59        size: u64,
60        value: u64,
61    },
62    Function {
63        value: u64,
64    },
65}
66
67impl FieldValue<'_> {
68    /// Returns the size of the field value in bytes.
69    pub fn size(&self) -> u64 {
70        match self {
71            Self::Void => 0,
72            Self::Char8(_) | Self::I8(_) | Self::U8(_) | Self::F8(_) | Self::Bool(_) => 1,
73            Self::Char16(_) | Self::I16(_) | Self::U16(_) | Self::F16(_) => 2,
74            Self::Char32(_) | Self::I32(_) | Self::U32(_) | Self::F32(_) => 4,
75            Self::I64(_) | Self::U64(_) | Self::F64(_) => 8,
76            Self::I128(_) | Self::U128(_) | Self::F128(_) => 16,
77            Self::Enum { size, .. } => *size,
78            Self::Struct { size, .. } => *size,
79            Self::Array { size, .. } => *size,
80            Self::Pointer { size, .. } => *size,
81            Self::Bitfield { size, .. } => *size,
82            Self::Function { .. } => 0,
83        }
84    }
85}
86
87/// A struct field's type, without a decoded value.
88#[allow(missing_docs)]
89pub enum FieldValueKind<'a> {
90    Void,
91    Bool,
92    Char8,
93    Char16,
94    Char32,
95
96    I8,
97    I16,
98    I32,
99    I64,
100    I128,
101
102    U8,
103    U16,
104    U32,
105    U64,
106    U128,
107
108    F8,
109    F16,
110    F32,
111    F64,
112    F128,
113
114    Enum {
115        type_name: &'a str,
116        size: u64,
117    },
118    Struct {
119        type_name: &'a str,
120        size: u64,
121    },
122    Array {
123        subtype: Box<FieldValueKind<'a>>,
124        dims: Vec<u64>,
125        size: u64,
126    },
127    Pointer {
128        subtype: Box<FieldValueKind<'a>>,
129        size: u64,
130    },
131    Bitfield {
132        bit_position: u64,
133        bit_length: u64,
134        size: u64,
135    },
136    Function,
137}
138
139impl FieldValueKind<'_> {
140    /// Returns the size of the field value kind in bytes.
141    pub fn size(&self) -> u64 {
142        match self {
143            Self::Void => 0,
144            Self::Char8 | Self::I8 | Self::U8 | Self::F8 | Self::Bool => 1,
145            Self::Char16 | Self::I16 | Self::U16 | Self::F16 => 2,
146            Self::Char32 | Self::I32 | Self::U32 | Self::F32 => 4,
147            Self::I64 | Self::U64 | Self::F64 => 8,
148            Self::I128 | Self::U128 | Self::F128 => 16,
149            Self::Enum { size, .. } => *size,
150            Self::Struct { size, .. } => *size,
151            Self::Array { size, .. } => *size,
152            Self::Pointer { size, .. } => *size,
153            Self::Bitfield { size, .. } => *size,
154            Self::Function { .. } => 0,
155        }
156    }
157}
158
159impl<'a> From<FieldValue<'a>> for FieldValueKind<'a> {
160    fn from(value: FieldValue<'a>) -> Self {
161        match value {
162            FieldValue::Void => Self::Void,
163            FieldValue::Bool(_) => Self::Bool,
164            FieldValue::Char8(_) => Self::Char8,
165            FieldValue::Char16(_) => Self::Char16,
166            FieldValue::Char32(_) => Self::Char32,
167
168            FieldValue::I8(_) => Self::I8,
169            FieldValue::I16(_) => Self::I16,
170            FieldValue::I32(_) => Self::I32,
171            FieldValue::I64(_) => Self::I64,
172            FieldValue::I128(_) => Self::I128,
173
174            FieldValue::U8(_) => Self::U8,
175            FieldValue::U16(_) => Self::U16,
176            FieldValue::U32(_) => Self::U32,
177            FieldValue::U64(_) => Self::U64,
178            FieldValue::U128(_) => Self::U128,
179
180            FieldValue::F8(_) => Self::F8,
181            FieldValue::F16(_) => Self::F16,
182            FieldValue::F32(_) => Self::F32,
183            FieldValue::F64(_) => Self::F64,
184            FieldValue::F128(_) => Self::F128,
185
186            FieldValue::Enum {
187                type_name, size, ..
188            } => Self::Enum { type_name, size },
189            FieldValue::Struct {
190                type_name, size, ..
191            } => Self::Struct { type_name, size },
192            FieldValue::Array {
193                subtype,
194                dims,
195                size,
196                ..
197            } => Self::Array {
198                subtype: Box::new(*subtype),
199                dims,
200                size,
201            },
202            FieldValue::Pointer { subtype, size, .. } => Self::Pointer {
203                subtype: Box::new(*subtype),
204                size,
205            },
206            FieldValue::Bitfield {
207                bit_position,
208                bit_length,
209                size,
210                ..
211            } => Self::Bitfield {
212                bit_position,
213                bit_length,
214                size,
215            },
216            FieldValue::Function { .. } => Self::Function,
217        }
218    }
219}
220
221/// A struct field with its decoded value.
222pub struct StructField<'a> {
223    /// Field name.
224    pub name: &'a str,
225
226    /// Field offset from the start of the struct, in bytes.
227    pub offset: usize,
228
229    /// Decoded type and value at `offset`.
230    pub value: FieldValue<'a>,
231}
232
233/// A struct field described by its type only.
234pub struct StructFieldKind<'a> {
235    /// Field name.
236    pub name: &'a str,
237
238    /// Field offset from the start of the struct, in bytes.
239    pub offset: usize,
240
241    /// Type at `offset`.
242    pub value: FieldValueKind<'a>,
243}
244
245/// Decodes each field of a struct from `data` and invokes `visitor`.
246///
247/// `visitor` can return an error to stop visiting early. Returns `Ok(())` if
248/// all fields were visited.
249pub fn visit_struct<E>(
250    profile: &Profile,
251    struct_def: &Struct,
252    mut visitor: impl FnMut(&StructField) -> Result<(), E>,
253    data: &[u8],
254) -> Result<(), E> {
255    for field in struct_def.fields() {
256        let offset = field.offset() as usize;
257        let value = make_value(profile, field.ty(), data, offset);
258
259        visitor(&StructField {
260            name: field.name(),
261            offset,
262            value,
263        })?;
264    }
265
266    Ok(())
267}
268
269/// Walks each field of a struct by type only, without reading data.
270///
271/// `visitor` can return an error to stop vi+siting early. Returns `Ok(())` if
272/// all fields were visited.
273pub fn visit_struct_schema<E>(
274    profile: &Profile,
275    struct_def: Struct,
276    mut visitor: impl FnMut(&StructFieldKind) -> Result<(), E>,
277) -> Result<(), E> {
278    for field in struct_def.fields() {
279        let offset = field.offset() as usize;
280        let value = make_value_kind(profile, field.ty());
281
282        visitor(&StructFieldKind {
283            name: field.name(),
284            offset,
285            value,
286        })?;
287    }
288
289    Ok(())
290}
291
292#[expect(clippy::unnecessary_cast)]
293fn make_value<'a>(
294    profile: &Profile,
295    ty: Type<'a>,
296    data: &'a [u8],
297    offset: usize,
298) -> FieldValue<'a> {
299    match &ty {
300        Type::Base(base) => match base {
301            Base::Void => FieldValue::Void,
302            Base::Bool => FieldValue::Bool(read_uint(data, offset, 1) != 0),
303            Base::Char8 => FieldValue::Char8(read_uint(data, offset, 1) as u8),
304            Base::Char16 => FieldValue::Char16(read_uint(data, offset, 2) as u16),
305            Base::Char32 => FieldValue::Char32(read_uint(data, offset, 4) as u32),
306
307            Base::I8 => FieldValue::I8(read_uint(data, offset, 1) as i8),
308            Base::I16 => FieldValue::I16(read_uint(data, offset, 2) as u16 as i16),
309            Base::I32 => FieldValue::I32(read_uint(data, offset, 4) as u32 as i32),
310            Base::I64 => FieldValue::I64(read_uint(data, offset, 8) as i64),
311            Base::I128 => FieldValue::I128(read_uint(data, offset, 16) as i128),
312
313            Base::U8 => FieldValue::U8(read_uint(data, offset, 1) as u8),
314            Base::U16 => FieldValue::U16(read_uint(data, offset, 2) as u16),
315            Base::U32 => FieldValue::U32(read_uint(data, offset, 4) as u32),
316            Base::U64 => FieldValue::U64(read_uint(data, offset, 8) as u64),
317            Base::U128 => FieldValue::U128(read_uint(data, offset, 16) as u128),
318            _ => {
319                let size = base.size() as usize;
320                let val = read_uint(data, offset, size);
321                match size {
322                    1 => FieldValue::U8(val as u8),
323                    2 => FieldValue::U16(val as u16),
324                    4 => FieldValue::U32(val as u32),
325                    8 => FieldValue::U64(val as u64),
326                    16 => FieldValue::U128(val as u128),
327                    _ => FieldValue::Void,
328                }
329            }
330        },
331        Type::Struct(sref) => FieldValue::Struct {
332            type_name: sref.name(),
333            size: profile.type_size(ty).unwrap_or(0),
334            data: &data[offset..],
335        },
336        Type::Pointer(ptr) => FieldValue::Pointer {
337            subtype: Box::new(make_value_kind(profile, ptr.subtype())),
338            size: profile.type_size(ty).unwrap_or(0),
339            value: read_uint(data, offset, ptr.size() as usize) as u64,
340        },
341        Type::Array(arr) => FieldValue::Array {
342            subtype: Box::new(make_value_kind(profile, arr.subtype())),
343            dims: arr.dims().collect(),
344            size: profile.type_size(ty).unwrap_or(0),
345            data: &data[offset..],
346        },
347        Type::Bitfield(bf) => {
348            let base_size = match bf.subtype() {
349                Type::Base(base) => base.size() as usize,
350                _ => (bf.bit_position() + bf.bit_length()).div_ceil(8) as usize,
351            };
352            FieldValue::Bitfield {
353                bit_position: bf.bit_position(),
354                bit_length: bf.bit_length(),
355                size: profile.type_size(ty).unwrap_or(0),
356                value: read_uint(data, offset, base_size) as u64,
357            }
358        }
359        Type::Enum(eref) => FieldValue::Enum {
360            type_name: eref.name(),
361            size: profile.type_size(ty).unwrap_or(0),
362            value: read_uint(data, offset, 8) as u64,
363        },
364        Type::Function => FieldValue::Function {
365            value: read_uint(data, offset, 8) as u64,
366        },
367    }
368}
369
370fn make_value_kind<'a>(profile: &Profile, ty: Type<'a>) -> FieldValueKind<'a> {
371    match &ty {
372        Type::Base(base) => match base {
373            Base::Void => FieldValueKind::Void,
374            Base::Bool => FieldValueKind::Bool,
375            Base::Char8 => FieldValueKind::Char8,
376            Base::Char16 => FieldValueKind::Char16,
377            Base::Char32 => FieldValueKind::Char32,
378
379            Base::I8 => FieldValueKind::I8,
380            Base::I16 => FieldValueKind::I16,
381            Base::I32 => FieldValueKind::I32,
382            Base::I64 => FieldValueKind::I64,
383            Base::I128 => FieldValueKind::I128,
384
385            Base::U8 => FieldValueKind::U8,
386            Base::U16 => FieldValueKind::U16,
387            Base::U32 => FieldValueKind::U32,
388            Base::U64 => FieldValueKind::U64,
389            Base::U128 => FieldValueKind::U128,
390            _ => {
391                let size = base.size() as usize;
392                match size {
393                    1 => FieldValueKind::U8,
394                    2 => FieldValueKind::U16,
395                    4 => FieldValueKind::U32,
396                    8 => FieldValueKind::U64,
397                    16 => FieldValueKind::U128,
398                    _ => FieldValueKind::Void,
399                }
400            }
401        },
402        Type::Struct(sref) => FieldValueKind::Struct {
403            type_name: sref.name(),
404            size: profile.struct_size(sref.name()).unwrap_or(0),
405        },
406        Type::Pointer(ptr) => FieldValueKind::Pointer {
407            subtype: Box::new(make_value_kind(profile, ptr.subtype())),
408            size: profile.type_size(ty).unwrap_or(0),
409        },
410        Type::Array(arr) => FieldValueKind::Array {
411            subtype: Box::new(make_value_kind(profile, arr.subtype())),
412            dims: arr.dims().collect(),
413            size: profile.type_size(ty).unwrap_or(0),
414        },
415        Type::Bitfield(bf) => FieldValueKind::Bitfield {
416            bit_position: bf.bit_position(),
417            bit_length: bf.bit_length(),
418            size: profile.type_size(bf.subtype()).unwrap_or(0),
419        },
420        Type::Enum(eref) => FieldValueKind::Enum {
421            type_name: eref.name(),
422            size: profile.type_size(ty).unwrap_or(0),
423        },
424        Type::Function => FieldValueKind::Function,
425    }
426}
427
428/// Reads a little-endian unsigned integer from a byte slice.
429fn read_uint(data: &[u8], offset: usize, size: usize) -> u128 {
430    if offset + size > data.len() {
431        return 0;
432    }
433
434    let slice = &data[offset..offset + size];
435    match size {
436        1 => slice[0] as u128,
437        2 => u16::from_le_bytes(slice.try_into().unwrap()) as u128,
438        4 => u32::from_le_bytes(slice.try_into().unwrap()) as u128,
439        8 => u64::from_le_bytes(slice.try_into().unwrap()) as u128,
440        16 => u128::from_le_bytes(slice.try_into().unwrap()),
441        _ => 0,
442    }
443}