Skip to content

Commit

Permalink
refactor: move most shared logic to executor.rs
Browse files Browse the repository at this point in the history
  • Loading branch information
CompeyDev committed Jan 13, 2024
1 parent 94fd549 commit 55fe033
Show file tree
Hide file tree
Showing 2 changed files with 164 additions and 163 deletions.
162 changes: 2 additions & 160 deletions src/cli/build.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
use console::Style;
use num_traits::{FromBytes, ToBytes};
use std::{env, ops::ControlFlow, path::Path, process::ExitCode};
use std::{env, path::Path, process::ExitCode};
use tokio::{
fs::{self, OpenOptions},
io::AsyncWriteExt,
Expand All @@ -9,164 +8,7 @@ use tokio::{
use anyhow::Result;
use mlua::Compiler as LuaCompiler;

// The signature which separates indicates the presence of bytecode to execute
// If a binary contains this magic signature as the last 8 bytes, that must mean
// it is a standalone binary
pub const MAGIC: &[u8; 8] = b"cr3sc3nt";

/// Utility struct to parse and generate bytes to the META chunk of standalone binaries.
#[derive(Debug, Clone)]
pub struct MetaChunk {
/// Compiled lua bytecode of the entrypoint script.
pub bytecode: Vec<u8>,
/// Offset to the the beginning of the bytecode from the start of the lune binary.
pub bytecode_offset: Option<u64>,
/// Number of files present, currently unused. **For future use**.
pub file_count: Option<u64>,
}

impl MetaChunk {
pub fn new() -> Self {
Self {
bytecode: Vec::new(),
bytecode_offset: None,
file_count: None,
}
}

pub fn with_bytecode(&mut self, bytecode: Vec<u8>) -> Self {
self.bytecode = bytecode;

self.clone()
}

pub fn with_bytecode_offset(&mut self, offset: u64) -> Self {
self.bytecode_offset = Some(offset);

self.clone()
}

pub fn with_file_count(&mut self, count: u64) -> Self {
self.file_count = Some(count);

self.clone()
}

pub fn build(self, endianness: &str) -> Vec<u8> {
match endianness {
"big" => self.to_be_bytes(),
"little" => self.to_le_bytes(),
&_ => panic!("unexpected endianness"),
}
}

fn from_bytes(bytes: &[u8], int_handler: fn([u8; 8]) -> u64) -> Result<Self> {
let mut bytecode_offset = 0;
let mut bytecode_size = 0;

// standalone binary structure (reversed, 8 bytes per field)
// [0] => magic signature
// ----------------
// -- META Chunk --
// [1] => file count
// [2] => bytecode size
// [3] => bytecode offset
// ----------------
// -- MISC Chunk --
// [4..n] => bytecode (variable size)
// ----------------
// NOTE: All integers are 8 byte, padded, unsigned & 64 bit (u64's).

// The rchunks will have unequally sized sections in the beginning
// but that doesn't matter to us because we don't need anything past the
// middle chunks where the bytecode is stored
bytes
.rchunks(MAGIC.len())
.enumerate()
.try_for_each(|(idx, chunk)| {
if bytecode_offset != 0 && bytecode_size != 0 {
return ControlFlow::Break(());
}

if idx == 0 && chunk != MAGIC {
// Binary is guaranteed to be standalone, we've confirmed this before
unreachable!("expected proper magic signature for standalone binary")
}

if idx == 3 {
bytecode_offset = int_handler(chunk.try_into().unwrap());
}

if idx == 2 {
bytecode_size = int_handler(chunk.try_into().unwrap());
}

ControlFlow::Continue(())
});

println!("size: {}", bytecode_size);
println!("offset: {}", bytecode_offset);

Ok(Self {
bytecode: bytes[usize::try_from(bytecode_offset)?
..usize::try_from(bytecode_offset + bytecode_size)?]
.to_vec(),
bytecode_offset: Some(bytecode_offset),
file_count: Some(1),
})
}
}

impl Default for MetaChunk {
fn default() -> Self {
Self {
bytecode: Vec::new(),
bytecode_offset: Some(0),
file_count: Some(1),
}
}
}

impl ToBytes for MetaChunk {
type Bytes = Vec<u8>;

fn to_be_bytes(&self) -> Self::Bytes {
// We start with the bytecode offset as the first field already filled in
let mut tmp = self.bytecode_offset.unwrap().to_be_bytes().to_vec();

// NOTE: The order of the fields here are reversed, which is on purpose
tmp.extend(self.bytecode.len().to_be_bytes());
tmp.extend(self.file_count.unwrap().to_be_bytes());

tmp
}

fn to_le_bytes(&self) -> Self::Bytes {
// We start with the bytecode offset as the first field already filled in
let mut tmp = self.bytecode_offset.unwrap().to_le_bytes().to_vec();

// NOTE: The order of the fields here are reversed, which is on purpose
tmp.extend(self.bytecode.len().to_le_bytes());
tmp.extend(self.file_count.unwrap().to_le_bytes());

println!("size: {}", self.bytecode.len());
println!("offset: {:?}", self.bytecode_offset);

tmp
}
}

impl FromBytes for MetaChunk {
type Bytes = Vec<u8>;

fn from_be_bytes(bytes: &Self::Bytes) -> Self {
Self::from_bytes(bytes, u64::from_be_bytes).unwrap()
}

fn from_le_bytes(bytes: &Self::Bytes) -> Self {
Self::from_bytes(bytes, u64::from_le_bytes).unwrap()
}
}
use crate::executor::{MetaChunk, MAGIC};

/**
Compiles and embeds the bytecode of a requested lua file to form a standalone binary,
Expand Down
165 changes: 162 additions & 3 deletions src/executor.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,171 @@
use std::{env, process::ExitCode};
use std::{env, ops::ControlFlow, process::ExitCode};

use crate::cli::build::{MetaChunk, MAGIC};
use lune::Lune;

use anyhow::Result;
use num_traits::FromBytes;
use num_traits::{FromBytes, ToBytes};
use tokio::fs::read as read_to_vec;

// The signature which separates indicates the presence of bytecode to execute
// If a binary contains this magic signature as the last 8 bytes, that must mean
// it is a standalone binary
pub const MAGIC: &[u8; 8] = b"cr3sc3nt";

/// Utility struct to parse and generate bytes to the META chunk of standalone binaries.
#[derive(Debug, Clone)]
pub struct MetaChunk {
/// Compiled lua bytecode of the entrypoint script.
pub bytecode: Vec<u8>,
/// Offset to the the beginning of the bytecode from the start of the lune binary.
pub bytecode_offset: Option<u64>,
/// Number of files present, currently unused. **For future use**.
pub file_count: Option<u64>,
}

impl MetaChunk {
/// Creates an emtpy `MetaChunk` instance.
pub fn new() -> Self {
Self {
bytecode: Vec::new(),
bytecode_offset: None,
file_count: None,
}
}

/// Builder method to include the bytecode, **mandatory** before build.
pub fn with_bytecode(&mut self, bytecode: Vec<u8>) -> Self {
self.bytecode = bytecode;

self.clone()
}

/// Builder method to include the bytecode offset, **mandatory** before build.
pub fn with_bytecode_offset(&mut self, offset: u64) -> Self {
self.bytecode_offset = Some(offset);

self.clone()
}

/// Builder method to include the file count, **mandatory** before build.

pub fn with_file_count(&mut self, count: u64) -> Self {
self.file_count = Some(count);

self.clone()
}

/// Builds the final `Vec` of bytes, based on the endianness specified.
pub fn build(self, endianness: &str) -> Vec<u8> {
match endianness {
"big" => self.to_be_bytes(),
"little" => self.to_le_bytes(),
&_ => panic!("unexpected endianness"),
}
}

/// Internal method which implements endian independent bytecode discovery logic.
fn from_bytes(bytes: &[u8], int_handler: fn([u8; 8]) -> u64) -> Result<Self> {
let mut bytecode_offset = 0;
let mut bytecode_size = 0;

// standalone binary structure (reversed, 8 bytes per field)
// [0] => magic signature
// ----------------
// -- META Chunk --
// [1] => file count
// [2] => bytecode size
// [3] => bytecode offset
// ----------------
// -- MISC Chunk --
// [4..n] => bytecode (variable size)
// ----------------
// NOTE: All integers are 8 byte, padded, unsigned & 64 bit (u64's).

// The rchunks will have unequally sized sections in the beginning
// but that doesn't matter to us because we don't need anything past the
// middle chunks where the bytecode is stored
bytes
.rchunks(MAGIC.len())
.enumerate()
.try_for_each(|(idx, chunk)| {
if bytecode_offset != 0 && bytecode_size != 0 {
return ControlFlow::Break(());
}

if idx == 0 && chunk != MAGIC {
// Binary is guaranteed to be standalone, we've confirmed this before
unreachable!("expected proper magic signature for standalone binary")
}

if idx == 3 {
bytecode_offset = int_handler(chunk.try_into().unwrap());
}

if idx == 2 {
bytecode_size = int_handler(chunk.try_into().unwrap());
}

ControlFlow::Continue(())
});

Ok(Self {
bytecode: bytes[usize::try_from(bytecode_offset)?
..usize::try_from(bytecode_offset + bytecode_size)?]
.to_vec(),
bytecode_offset: Some(bytecode_offset),
file_count: Some(1),
})
}
}

impl Default for MetaChunk {
fn default() -> Self {
Self {
bytecode: Vec::new(),
bytecode_offset: Some(0),
file_count: Some(1),
}
}
}

impl ToBytes for MetaChunk {
type Bytes = Vec<u8>;

fn to_be_bytes(&self) -> Self::Bytes {
// We start with the bytecode offset as the first field already filled in
let mut tmp = self.bytecode_offset.unwrap().to_be_bytes().to_vec();

// NOTE: The order of the fields here are reversed, which is on purpose
tmp.extend(self.bytecode.len().to_be_bytes());
tmp.extend(self.file_count.unwrap().to_be_bytes());

tmp
}

fn to_le_bytes(&self) -> Self::Bytes {
// We start with the bytecode offset as the first field already filled in
let mut tmp = self.bytecode_offset.unwrap().to_le_bytes().to_vec();

// NOTE: The order of the fields here are reversed, which is on purpose
tmp.extend(self.bytecode.len().to_le_bytes());
tmp.extend(self.file_count.unwrap().to_le_bytes());

tmp
}
}

impl FromBytes for MetaChunk {
type Bytes = Vec<u8>;

fn from_be_bytes(bytes: &Self::Bytes) -> Self {
Self::from_bytes(bytes, u64::from_be_bytes).unwrap()
}

fn from_le_bytes(bytes: &Self::Bytes) -> Self {
Self::from_bytes(bytes, u64::from_le_bytes).unwrap()
}
}

/**
Returns information about whether the execution environment is standalone
or not, the standalone binary signature, and the contents of the binary.
Expand Down

0 comments on commit 55fe033

Please sign in to comment.