use crate::error::{Error, Result};
use std::collections::HashMap;
#[derive(Clone, Copy, Debug, Default)]
pub struct Integer {
pub used_bits: u32,
pub bits: u32,
pub is_signed: bool,
}
impl Integer {
pub fn get_size(&self) -> u32 {
self.used_bits / 8
}
}
#[derive(Clone, Copy, Debug, Default)]
pub struct Float {
pub bits: u32,
}
impl Float {
pub fn get_size(&self) -> u32 {
self.bits / 8
}
}
#[derive(Clone, Copy, Debug, Default)]
pub struct Array {
pub element_type_id: usize,
pub num_elements: u32,
pub size: u32,
}
impl Array {
pub fn create(
database: &TypeDatabase,
element_type_id: usize,
num_elements: u32,
) -> Result<Self> {
let element_type = database
.get_type_by_id(element_type_id)
.ok_or(Error::InvalidTypeId)?;
let size = element_type.get_size() * num_elements;
Ok(Self {
element_type_id,
num_elements,
size,
})
}
pub fn get_size(&self) -> u32 {
self.size
}
}
#[derive(Clone, Copy, Debug, Default)]
pub struct Field {
pub offset: u32,
pub type_id: usize,
}
impl Field {
pub fn get_type<'a>(&self, database: &'a TypeDatabase) -> Option<&'a Type> {
database.get_type_by_id(self.type_id)
}
}
#[derive(Clone, Debug, Default)]
pub struct Struct {
pub fields: HashMap<String, Field>,
pub size: u32,
}
impl Struct {
pub fn create(database: &TypeDatabase, fields: &[(&str, Field)]) -> Result<Self> {
let mut new_fields = HashMap::with_capacity(fields.len());
let mut bits = 0;
for (name, field) in fields {
let field_type = database
.get_type_by_id(field.type_id)
.ok_or(Error::InvalidTypeId)?;
let reach = field.offset + field_type.get_size() * 8;
if reach > bits {
bits = reach
}
new_fields.insert(name.to_string(), *field);
}
Ok(Self {
fields: new_fields,
size: bits / 8,
})
}
pub fn get_size(&self) -> u32 {
self.size
}
}
#[derive(Clone, Debug, Default)]
pub struct Enum {
pub bits: u32,
pub values: Vec<(String, i64)>,
}
impl Enum {
pub fn get_size(&self) -> u32 {
self.bits / 8
}
}
#[derive(Clone, Debug, Default)]
pub struct Function {
pub param_type_ids: Vec<usize>,
}
impl Function {
pub fn create(param_type_ids: &[usize]) -> Self {
Self {
param_type_ids: param_type_ids.to_vec(),
}
}
}
#[derive(Clone, Debug, Default)]
pub enum BaseType {
#[default]
Void,
Integer(Integer),
Float(Float),
Array(Array),
Struct(Struct),
Enum(Enum),
Function(Function),
}
impl BaseType {
pub fn get_size(&self) -> u32 {
match self {
BaseType::Void => 0,
BaseType::Integer(t) => t.get_size(),
BaseType::Float(t) => t.get_size(),
BaseType::Array(t) => t.get_size(),
BaseType::Struct(t) => t.get_size(),
BaseType::Enum(t) => t.get_size(),
BaseType::Function(_) => 0,
}
}
}
#[derive(Clone, Debug, Default)]
pub struct Type {
pub base_type: BaseType,
pub num_refs: u32,
}
impl Type {
pub fn is_pointer(&self) -> bool {
self.num_refs > 0
}
pub fn get_size(&self) -> u32 {
if self.num_refs > 0 {
return 8;
}
self.base_type.get_size()
}
}
impl From<BaseType> for Type {
fn from(base_type: BaseType) -> Self {
Self {
base_type,
num_refs: 0,
}
}
}
#[derive(Clone, Debug, Default)]
pub struct TypeDatabase {
types: Vec<Type>,
name_map: HashMap<String, usize>,
}
impl TypeDatabase {
pub fn add_type(&mut self, name: Option<&str>, ty: &Type) -> Result<usize> {
if let Some(name) = name {
if let Some(index) = self.name_map.get(name) {
self.types[*index] = ty.clone();
Ok(*index)
} else {
let index = self.types.len();
self.types.push(ty.clone());
self.name_map.insert(name.to_string(), index);
Ok(index)
}
} else {
self.types.push(ty.clone());
Ok(self.types.len() - 1)
}
}
pub fn get_type_by_name(&self, name: &str) -> Option<&Type> {
let index = self.name_map.get(name)?;
self.types.get(*index)
}
pub fn get_type_by_id(&self, id: usize) -> Option<&Type> {
self.types.get(id)
}
pub fn get_type_id_by_name(&self, name: &str) -> Option<usize> {
Some(*self.name_map.get(name)?)
}
pub fn add_integer(
&mut self,
name: Option<&str>,
bytes: u32,
is_signed: bool,
) -> Result<usize> {
let bits = bytes * 8;
let new_integer = Integer {
used_bits: bits,
bits,
is_signed,
};
self.add_type(name, &BaseType::Integer(new_integer).into())
}
pub fn add_float(&mut self, name: Option<&str>, bits: u32) -> Result<usize> {
let new_float = Float { bits };
self.add_type(name, &BaseType::Float(new_float).into())
}
pub fn add_array(
&mut self,
name: Option<&str>,
element_type_id: usize,
num_elements: u32,
) -> Result<usize> {
let new_array = Array::create(self, element_type_id, num_elements)?;
self.add_type(name, &BaseType::Array(new_array).into())
}
pub fn add_struct(&mut self, name: Option<&str>, fields: &[(&str, Field)]) -> Result<usize> {
let new_struct = Struct::create(self, fields)?;
self.add_type(name, &BaseType::Struct(new_struct).into())
}
pub fn add_struct_by_ids(
&mut self,
name: Option<&str>,
fields: &[(&str, usize)],
) -> Result<usize> {
let mut new_fields = Vec::with_capacity(fields.len());
let mut offset = 0;
for (field_name, type_id) in fields {
let field_type = self
.get_type_by_id(*type_id)
.ok_or(Error::InvalidTypeName)?;
let field = Field {
offset,
type_id: *type_id,
};
offset += field_type.get_size() * 8;
new_fields.push((*field_name, field));
}
let new_struct = Struct::create(self, new_fields.as_slice())?;
self.add_type(name, &BaseType::Struct(new_struct).into())
}
pub fn add_struct_by_names(
&mut self,
name: Option<&str>,
fields: &[(&str, &str)],
) -> Result<usize> {
let mut new_fields = Vec::with_capacity(fields.len());
let mut offset = 0;
for (field_name, type_name) in fields {
let field_type = self
.get_type_by_name(type_name)
.ok_or(Error::InvalidTypeName)?;
let type_id = self
.get_type_id_by_name(type_name)
.ok_or(Error::InvalidTypeName)?;
let field = Field { offset, type_id };
offset += field_type.get_size() * 8;
new_fields.push((*field_name, field));
}
let new_struct = Struct::create(self, new_fields.as_slice())?;
self.add_type(name, &BaseType::Struct(new_struct).into())
}
}
pub trait AddToTypeDatabase {
fn add_to_database(database: &mut TypeDatabase) -> Result<usize>;
}
impl AddToTypeDatabase for u8 {
fn add_to_database(database: &mut TypeDatabase) -> Result<usize> {
database.add_integer(Some("u8"), 1, false)
}
}
impl AddToTypeDatabase for u16 {
fn add_to_database(database: &mut TypeDatabase) -> Result<usize> {
database.add_integer(Some("u16"), 2, false)
}
}
impl AddToTypeDatabase for u32 {
fn add_to_database(database: &mut TypeDatabase) -> Result<usize> {
database.add_integer(Some("u32"), 4, false)
}
}
impl AddToTypeDatabase for u64 {
fn add_to_database(database: &mut TypeDatabase) -> Result<usize> {
database.add_integer(Some("u64"), 8, false)
}
}
impl AddToTypeDatabase for i8 {
fn add_to_database(database: &mut TypeDatabase) -> Result<usize> {
database.add_integer(Some("i8"), 1, true)
}
}
impl AddToTypeDatabase for i16 {
fn add_to_database(database: &mut TypeDatabase) -> Result<usize> {
database.add_integer(Some("i16"), 2, true)
}
}
impl AddToTypeDatabase for i32 {
fn add_to_database(database: &mut TypeDatabase) -> Result<usize> {
database.add_integer(Some("i32"), 4, true)
}
}
impl AddToTypeDatabase for i64 {
fn add_to_database(database: &mut TypeDatabase) -> Result<usize> {
database.add_integer(Some("i64"), 8, true)
}
}
impl<T: AddToTypeDatabase, const N: usize> AddToTypeDatabase for [T; N] {
fn add_to_database(database: &mut TypeDatabase) -> Result<usize> {
let type_id = T::add_to_database(database)?;
database.add_array(
Some(std::any::type_name::<[T; N]>()),
type_id,
N.try_into()?,
)
}
}