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);
}
}
}
}
}