vermilion-object 0.1.0

A library for packaging Vermilion bytecode.
Documentation
//! The top-level lib.rs for vermilion-object

use vermilion_vm::{VirtualMachine, VermilionSection, VermilionSymbol};
use std::collections::HashMap;

/// Symbols that can be defined in an Artifact.
#[derive(Clone)]
pub enum Symbol {

    /// A declared but undefined external symbol.
    External(String),

    /// A local symbol.
    Local(String, usize),

}

/// An Artifact that can be loaded from and to bytes.
#[derive(Clone)]
pub struct Artifact {

    /// The program's static data.
    pub data: Vec<u8>,

    /// The program itself
    pub program: Vec<u8>,

    /// A list of symbols defined in the Artifact.
    pub symbols: HashMap<i64, Symbol>,

}

impl Artifact {

    /// Creates a new, empty Artifact.
    pub fn new() -> Self {
        Artifact {
            data: vec![],
            program: vec![],
            symbols: HashMap::new(),
        }
    }

    /// Defines a function and appends it to the program.
    pub fn define_function(&mut self, id: i64, name: String, bytes: Vec<u8>) {
        let start = self.program.len();
        self.symbols.insert(id, Symbol::Local(name, start));
        self.program.append(&mut bytes.clone());
    }

    /// Declares an external function.
    pub fn declare_function(&mut self, id: i64, name: String) {
        self.symbols.insert(id, Symbol::External(name));
    }

    /// Creates a new virtual machine based on the contents of the artifact.
    pub fn new_vm(&mut self) -> VirtualMachine {
        let mut vm = VirtualMachine::new();
        self.load_vm(&mut vm);

        vm
    }

    /// Loads the artifact into a virtual machine instance.
    pub fn load_vm(&mut self, vm: &mut VirtualMachine) {
        // Load data into the heap
        let mut program = self.program.clone();
        let mut data = self.data.clone();
        vm.heap.append(&mut program);
        vm.heap.append(&mut data);

        // Create sections
        vm.sections.insert(0 as i64, VermilionSection(0, program.len()));
        vm.sections.insert(1 as i64, VermilionSection(program.len(), program.len() + data.len()));

        // Load symbols
        for symbol in self.symbols.clone() {
            match symbol.1 {
                Symbol::Local(name, home) => {
                    vm.symbols.insert(symbol.0, VermilionSymbol::Internal(name, home as i64));
                },
                Symbol::External(name) => {
                    vm.symbols.insert(symbol.0, VermilionSymbol::UndefinedExternal(name));
                }
            }
        }
    }

    /// Decodes an Artifact.
    pub fn from_bytes(bytes: Vec<u8>) -> Result<Artifact, String> {
        if bytes.len() < 5 {
            return Err("Invalid magic word.".to_string());
        }

        if bytes.len() < 14 {
            return Err("Invalid symbol count.".to_string());
        }

        let s = bytes[0..4].to_vec();
        let expected = vec![
            '_' as u8,
            '_' as u8,
            'v' as u8,
            'r' as u8,
            'm' as u8,
        ];

        if s != expected {
            return Err("Invalid magic word.".to_string());
        }

        let st_length_bytes = &bytes[5..13];
        let st_length = i64::from_le_bytes([
            st_length_bytes[0],
            st_length_bytes[1],
            st_length_bytes[2],
            st_length_bytes[3],
            st_length_bytes[4],
            st_length_bytes[5],
            st_length_bytes[6],
            st_length_bytes[7],
        ]);

        let mut start_byte = 14;
        let mut i = 0;

        let mut artifact = Artifact::new();

        while i < st_length {
            if bytes.len() < st_length as usize + 8 {
                return Err("Invalid symbol id.".to_string());
            }

            let id_bytes = &bytes[start_byte..start_byte + 7];
            let id = i64::from_le_bytes([
                id_bytes[0],
                id_bytes[1],
                id_bytes[2],
                id_bytes[3],
                id_bytes[4],
                id_bytes[5],
                id_bytes[6],
                id_bytes[7],
            ]);

            start_byte += 8;

            if bytes.len() < st_length as usize + 8 {
                return Err("Invalid symbol address.".to_string());
            }

            let home_bytes = &bytes[start_byte..start_byte + 7];
            let home = i64::from_le_bytes([
                home_bytes[0],
                home_bytes[1],
                home_bytes[2],
                home_bytes[3],
                home_bytes[4],
                home_bytes[5],
                home_bytes[6],
                home_bytes[7],
            ]);

            start_byte += 8;

            if bytes.len() < st_length as usize + 1 {
                return Err("Invalid symbol address.".to_string());
            }

            let type_byte = bytes[start_byte];

            start_byte += 1;

            let mut name_bytes = Vec::new();
            let mut ended = false;

            while start_byte < bytes.len() {
                if bytes[start_byte] == 0 {
                    ended = true;
                    break;
                }

                name_bytes.push(bytes[start_byte]);

                start_byte += 1;
            }

            if !ended {
                return Err("Unterminated string.".to_string());
            }

            match std::str::from_utf8(&name_bytes) {
                Ok(name) => {
                    if type_byte == 0 {
                        // internal
                        artifact.symbols.insert(id, Symbol::Local(name.to_string(), home as usize));
                    } else {
                        // external
                        artifact.symbols.insert(id, Symbol::External(name.to_string()));
                    }
                },
                Err(_) => {
                    return Err("Unable to convert string to utf8.".to_string());
                }
            }

            // next symbol
            start_byte += 1;
            i += 1;
        }

        if bytes.len() < start_byte + 8 {
            return Err("Unable to get size of data section.".to_string());
        }

        let data_bytes = &bytes[start_byte..start_byte + 7];
        let data_size = i64::from_le_bytes([
            data_bytes[0],
            data_bytes[1],
            data_bytes[2],
            data_bytes[3],
            data_bytes[4],
            data_bytes[5],
            data_bytes[6],
            data_bytes[7],
        ]);

        if bytes.len() < (start_byte + data_size as usize + 1) {
            return Err("File is too small for data section.".to_string());
        }

        let data = &bytes[start_byte..data_size as usize];
        artifact.data = data.to_vec();

        start_byte += data_size as usize;

        let program = &bytes[start_byte..bytes.len() - 1];
        artifact.program = program.to_vec();

        Ok(artifact)
    }

    /// Converts the Artifact into its byte format, defined in
    /// Vermilion 2021 r2.
    pub fn into_bytes(&mut self) -> Vec<u8> {
        let mut bytes: Vec<u8> = vec![
            '_' as u8,
            '_' as u8,
            'v' as u8,
            'r' as u8,
            'm' as u8,
        ];

        // Symbol table
        let l = self.symbols.len() as i64;
        let le_bytes = l.to_le_bytes();
        bytes.append(&mut le_bytes.to_vec());

        for symbol in self.symbols.clone() {
            let mut id_bytes = symbol.0.to_le_bytes().to_vec();
            let mut home_bytes;
            let type_byte;
            let mut name_bytes;

            match symbol.1 {
                Symbol::External(n) => {
                    name_bytes = n.as_bytes().to_vec();
                    name_bytes.append(&mut vec![0]); // null byte
                    home_bytes = vec![0, 0, 0, 0, 0, 0, 0, 0];
                    type_byte = 1;
                },
                Symbol::Local(n, home) => {
                    name_bytes = n.as_bytes().to_vec();
                    name_bytes.append(&mut vec![0]); // null byte
                    home_bytes = home.to_le_bytes().to_vec();
                    type_byte = 0;
                },
            }

            bytes.append(&mut id_bytes);
            bytes.append(&mut home_bytes);
            bytes.push(type_byte);
            bytes.append(&mut name_bytes);
        }

        let mut data_len_bytes = self.data.len().to_le_bytes().to_vec();
        bytes.append(&mut data_len_bytes);
        bytes.append(&mut self.data);

        bytes.append(&mut self.program);

        bytes
    }

}