1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
use crate::ty::Type;
use std::fmt;

#[derive(Clone)]
pub enum Seg<'a> {
    NamedIndex(usize, &'a str),
    Index(usize),
}
impl<'a> fmt::Debug for Seg<'a> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            Seg::NamedIndex(_, name) => f.write_str(name),
            Seg::Index(i) => write!(f, "{}", i),
        }
    }
}

#[derive(Clone, Debug)]
pub struct MemberVariableRouting<'a> {
    pub sym: Vec<Seg<'a>>,
    pub offset: usize,
    pub ty: &'a Type,
}

struct WalkFrame<'a> {
    sym_stem: Option<Vec<Seg<'a>>>,
    base_offset: usize,
    ty: &'a Type,
    i: usize,
}
pub struct Walk<'a> {
    inner: Vec<WalkFrame<'a>>,
}
impl<'a> Walk<'a> {
    pub fn new(ty: &'a Type) -> Walk<'a> {
        let frame = WalkFrame {
            sym_stem: None,
            base_offset: 0,
            ty: ty,
            i: 0,
        };
        Walk { inner: vec![frame] }
    }
}
impl<'a> Iterator for Walk<'a> {
    type Item = MemberVariableRouting<'a>;
    fn next(&mut self) -> Option<MemberVariableRouting<'a>> {
        fn get_child_ty_offset_seg<'a>(
            ty: &'a Type,
            i: usize,
        ) -> Option<(&'a Type, usize, Seg<'a>)> {
            match ty {
                Type::Struct(struct_ty) => {
                    let member = struct_ty.members.get(i)?;
                    let seg = if let Some(name) = &member.name {
                        Seg::NamedIndex(i, &name)
                    } else {
                        Seg::Index(i)
                    };
                    Some((&member.ty, member.offset.unwrap_or_default(), seg))
                }
                Type::Array(arr_ty) => {
                    // Unsized buffer are treated as 0-sized.
                    if i < arr_ty.nelement.unwrap_or_default() as usize {
                        Some((
                            &arr_ty.element_ty,
                            arr_ty.stride.unwrap_or_default() * i,
                            Seg::Index(i),
                        ))
                    } else {
                        None
                    }
                }
                _ => None,
            }
        }
        enum LoopEnd<'a> {
            Push(WalkFrame<'a>),
            PopReturn(MemberVariableRouting<'a>),
        }
        loop {
            // If used, this field will be filled with the next frame to be
            // pushed at the back of the walk stack; or the last frame will be
            // popped if the field is kept `None`.
            let loop_end = if let Some(frame) = self.inner.last_mut() {
                if let Some((child_ty, offset, seg)) = get_child_ty_offset_seg(frame.ty, frame.i) {
                    frame.i += 1; // Step member.
                    let offset = frame.base_offset + offset;
                    let ty = child_ty;
                    let sym = if let Some(sym_stem) = &frame.sym_stem {
                        let mut sym = sym_stem.clone();
                        sym.push(seg);
                        sym
                    } else {
                        vec![seg]
                    };
                    if child_ty.is_struct() || child_ty.is_array() {
                        // Found composite type, step into it.
                        LoopEnd::Push(WalkFrame {
                            sym_stem: Some(sym),
                            base_offset: offset,
                            ty,
                            i: 0,
                        })
                    } else {
                        // Return directly if it's not a composite type.
                        return Some(MemberVariableRouting { sym, offset, ty });
                    }
                } else {
                    // Here can be reached only when the first frame's type is
                    // neither an array nor a struct; or a later frame's
                    // composite type's elements has been exhausted.
                    let ty = frame.ty;
                    let offset = frame.base_offset;
                    let sym = frame.sym_stem.clone().unwrap_or_default();
                    LoopEnd::PopReturn(MemberVariableRouting { sym, offset, ty })
                }
            } else {
                // We have exhausted all types we have, including the root type
                // of walk.
                return None;
            };
            match loop_end {
                LoopEnd::Push(frame) => self.inner.push(frame),
                LoopEnd::PopReturn(route) => {
                    self.inner.pop();
                    return Some(route);
                }
            }
        }
    }
}