use crate::get_32_le; use std::collections::HashMap; use std::io::Read; pub const DEIMOS_MAGIC_NUMBER: u64 = 0x00_00_64_65_69_6d_6f_73; // ascii 'deimos' const DEIMOS_MAGIC_STRING: [u8; 6] = [0x64, 0x65, 0x69, 0x6d, 0x6f, 0x73]; // ascii 'deimos' pub const VERSION_STRING: &str = "0.1.0"; pub struct DmModule { name: String, version: String, constants: HashMap, functions: HashMap, } pub enum DmConstant { Int(i32), Long(i64), Double(f64), String(String), } pub struct DmFunction { pub name: String, pub byte_code: Vec, pub num_registers: usize, } const CONST_SYMBOL: u8 = 0x01; const FUNCTION_SYMBOL: u8 = 0x02; enum SymbolType { Constant, Function, } struct DmSymbol { name: String, symbol_type: SymbolType, address: u32, } macro_rules! push_byte_array { ( $dest: expr, $arr: expr ) => { for b in $arr { $dest.push(b); } }; } macro_rules! push_number_le { ( $dest: expr, $num: expr ) => { for b in $num.to_le_bytes() { $dest.push(b); } }; } macro_rules! push_string { ( $dest: expr, $s: expr ) => { for b in $s.len().to_le_bytes() { $dest.push(b); } for b in $s.bytes() { $dest.push(b); } }; } pub fn write_module(module: DmModule) -> Vec { // Push magic number let mut result: Vec = Vec::new(); push_byte_array!(result, DEIMOS_MAGIC_STRING); // Push version string push_string!(result, module.version); // Push module name length, little endian push_string!(result, module.name); result } pub fn load_module(bytes: &[u8]) -> Result { let mut ip: usize = 0; // Check for magic number at bytes 0..5 if !check_deimos(&bytes) { return Err(String::from("Not a valid Deimos module.")); } ip = 6; // Get version string length from bytes 6..9 let version_string_length = get_32_le!(bytes, 6, 0, usize); ip = 10; // Bytes 10..(10 + version_string_length) are the version string, in utf8 let mut version_string_raw: Vec = Vec::with_capacity(version_string_length); let version_string_end = ip + version_string_length; while ip < version_string_end { version_string_raw.push(bytes[ip]); ip += 1; } let version_string = String::from_utf8(version_string_raw).unwrap(); // Check version string. We'll use this in the future to not load modules compiled later than // current version. if version_string != "0.1.0" { return Err(String::from("Invalid Deimos module version.")); } // TODO: extract module name // Holder for Symbols we will extract from the symbol table bytes let mut symbols: Vec = Vec::new(); // Get the symbol table length and calculate how far we need to read let symbol_table_length = get_32_le!(bytes, 10 + version_string_length, 0, usize); let symbol_table_end = ip + symbol_table_length; // For each "row" in the symbol table, // 1. Get the type // 2. Obtain the name's length and then get the name in utf8 // 3. Grab the address to the actual symbol in the module bytes while ip < symbol_table_end { let type_byte = bytes[ip]; ip += 1; let name_string_length = get_32_le!(bytes, ip, 0, usize); ip += 4; let name_string_raw = bytes[ip..ip + name_string_length].to_vec(); let name = String::from_utf8(name_string_raw).unwrap(); ip += name_string_length; let address = get_32_le!(bytes, ip, 0, u32); ip += 4; let symbol_type = match type_byte { CONST_SYMBOL => SymbolType::Constant, FUNCTION_SYMBOL => SymbolType::Function, _ => return Err(String::from("Invalid Deimos symbol type.")), }; symbols.push(DmSymbol { name, address, symbol_type, }); } todo!() } fn check_deimos(bytes: &[u8]) -> bool { if bytes.len() < 6 { return false; } let first_six = read_as_u64(&bytes[0..6]); first_six == DEIMOS_MAGIC_NUMBER } fn read_as_u64(bytes: &[u8]) -> u64 { let mut result = 0u64; let max_shift = (bytes.len() - 1) * 8; for i in 0..bytes.len() { result |= (bytes[i] as u64) << (max_shift - i * 8); } result } #[cfg(test)] mod read_as_u64_tests { use crate::vm::module::{read_as_u64, DEIMOS_MAGIC_NUMBER}; #[test] fn read_6_bytes() { let bytes = vec![0x64u8, 0x65u8, 0x69u8, 0x6du8, 0x6fu8, 0x73u8]; // ascii 'deimos' let result = read_as_u64(&bytes); assert_eq!(DEIMOS_MAGIC_NUMBER, result); } }