use std::collections::HashMap;
use super::{EnumMember, TypeId, TypeMember, TypeShape, TypeTable, TypeValue};
mod scalar_kind {
pub const VOID: u32 = 1;
pub const BOOL: u32 = 2;
pub const INT: u32 = 3;
pub const FLOAT: u32 = 4;
}
fn opt_size(size: u64, has_size: u32) -> Option<u64> {
(has_size != 0).then_some(size)
}
#[derive(Debug)]
pub(crate) struct TypeBuilder {
table: TypeTable,
name2type: HashMap<Box<str>, TypeId>,
pending: HashMap<TypeId, Option<Box<str>>>,
too_wide: Option<u32>,
}
impl TypeBuilder {
pub(crate) fn new() -> Self {
Self {
table: TypeTable::new(),
name2type: HashMap::new(),
pending: HashMap::new(),
too_wide: None,
}
}
pub(crate) fn intern(&mut self, data: TypeValue) -> TypeId {
self.table.intern(data)
}
pub(crate) fn alloc_placeholder(&mut self) -> TypeId {
self.table.alloc_placeholder()
}
pub(crate) fn fill(&mut self, id: TypeId, data: TypeValue) {
self.table.fill(id, data);
}
pub(crate) fn type_size(&self, id: TypeId) -> Option<u64> {
self.table.get(id).size
}
pub(crate) fn scalar(
&mut self,
kind: u32,
bytes: u32,
signed: u32,
size: u64,
has_size: u32,
) -> TypeId {
let width = match u8::try_from(bytes) {
Ok(w) => w,
Err(_) => {
self.too_wide.get_or_insert(bytes);
0
}
};
let shape = match kind {
scalar_kind::VOID => TypeShape::Void,
scalar_kind::BOOL => TypeShape::Bool,
scalar_kind::INT => TypeShape::Int {
bytes: width,
signed: signed != 0,
},
scalar_kind::FLOAT => TypeShape::Float { bytes: width },
_ => TypeShape::Unknown,
};
self.intern(TypeValue {
shape,
size: opt_size(size, has_size),
})
}
pub(crate) fn ptr(&mut self, target: TypeId, size: u64, has_size: u32) -> TypeId {
self.intern(TypeValue {
shape: TypeShape::Ptr(target),
size: opt_size(size, has_size),
})
}
pub(crate) fn array(&mut self, elem: TypeId, nelems: u64, size: u64, has_size: u32) -> TypeId {
self.intern(TypeValue {
shape: TypeShape::Array { elem, len: nelems },
size: opt_size(size, has_size),
})
}
pub(crate) fn function(&mut self, ret: TypeId, params: Vec<TypeId>, vararg: u32) -> TypeId {
self.intern(TypeValue {
shape: TypeShape::Function {
ret,
params,
varargs: vararg != 0,
},
size: None,
})
}
pub(crate) fn opaque(&mut self, name: String) -> TypeId {
self.intern(TypeValue {
shape: TypeShape::Opaque(name),
size: None,
})
}
pub(crate) fn named_ref(&mut self, name: String) -> TypeId {
if let Some(&id) = self.name2type.get(name.as_str()) {
return id;
}
let id = self.alloc_placeholder();
let key: Box<str> = name.into_boxed_str();
self.name2type.insert(key.clone(), id);
self.pending.insert(id, Some(key));
id
}
pub(crate) fn anon(&mut self) -> TypeId {
let id = self.alloc_placeholder();
self.pending.insert(id, None);
id
}
fn take_name(&mut self, id: TypeId) -> Option<String> {
self.pending.remove(&id).flatten().map(String::from)
}
pub(crate) fn fill_struct(
&mut self,
id: TypeId,
is_union: bool,
members: Vec<TypeMember>,
size: u64,
has_size: u32,
) {
let name = self.take_name(id);
let shape = if is_union {
TypeShape::Union { name, members }
} else {
TypeShape::Struct { name, members }
};
self.fill(
id,
TypeValue {
shape,
size: opt_size(size, has_size),
},
);
}
pub(crate) fn fill_enum(
&mut self,
id: TypeId,
underlying: TypeId,
members: Vec<EnumMember>,
size: u64,
has_size: u32,
) {
let name = self.take_name(id);
self.fill(
id,
TypeValue {
shape: TypeShape::Enum {
name,
underlying,
members,
},
size: opt_size(size, has_size),
},
);
}
pub(crate) fn fill_typedef(&mut self, id: TypeId, underlying: TypeId) {
let name = self.take_name(id).unwrap_or_default();
let size = self.type_size(underlying);
self.fill(
id,
TypeValue {
shape: TypeShape::Typedef { name, underlying },
size,
},
);
}
pub(crate) fn too_wide(&self) -> Option<u32> {
self.too_wide
}
pub(crate) fn unfilled(&self) -> usize {
self.pending.len()
}
pub(crate) fn into_table(self) -> TypeTable {
self.table
}
}