178 lines
4.7 KiB
Rust
178 lines
4.7 KiB
Rust
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<String, DmConstant>,
|
|
functions: HashMap<String, DmFunction>,
|
|
}
|
|
|
|
pub enum DmConstant {
|
|
Int(i32),
|
|
Long(i64),
|
|
Double(f64),
|
|
String(String),
|
|
}
|
|
|
|
pub struct DmFunction {
|
|
pub name: String,
|
|
pub byte_code: Vec<u8>,
|
|
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<u8> {
|
|
// Push magic number
|
|
let mut result: Vec<u8> = 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<DmModule, String> {
|
|
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<u8> = 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<DmSymbol> = 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);
|
|
}
|
|
}
|