feat(bytecode): +Header

This commit is contained in:
Alain Zscheile 2022-09-23 09:54:36 +02:00
parent ec6eed9a7f
commit 2c05579f7a
7 changed files with 175 additions and 73 deletions

View file

@ -1,4 +1,6 @@
use crate::intern::Sealed;
use crate::parse::intern::Sealed;
pub const MAGIC: &[u8] = b"FgtX";
#[repr(u8)]
#[derive(Clone, Copy, Debug, PartialEq, Eq, int_enum::IntEnum)]

View file

@ -0,0 +1,98 @@
use crate::{consts::MAGIC, Value};
use alloc::collections::BTreeMap;
use core::fmt;
pub struct Header<'a> {
// if a symbol `Bytes([])` exists, it's the entry point
// only symbols named with a byte string or atom are exported
pub symbols: BTreeMap<Value<'a>, u64>,
}
#[derive(Clone, Copy, Debug)]
pub struct ParseError;
impl fmt::Display for ParseError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "unable to parse header from buffer")
}
}
#[cfg(feature = "std")]
impl std::error::Error for ParseError {}
impl From<()> for ParseError {
fn from(_: ()) -> Self {
Self
}
}
impl From<core::num::TryFromIntError> for ParseError {
fn from(_: core::num::TryFromIntError) -> Self {
Self
}
}
impl From<crate::ValueParseError> for ParseError {
fn from(_: crate::ValueParseError) -> Self {
Self
}
}
impl<'a> crate::Parse<'a> for Header<'a> {
type Err = ParseError;
fn parse(inp: &'a [u8]) -> Result<(&'a [u8], Self), ParseError> {
if !inp.starts_with(MAGIC) {
return Err(ParseError);
}
let (mut inp, len) = u32::parse(&inp[4..])?;
let mut h = Header {
symbols: BTreeMap::new(),
};
for _ in 0..len {
let (xinp, exptarget) = u64::parse(inp)?;
let (xinp, expsrc) = Value::parse(xinp)?;
inp = xinp;
if h.symbols.insert(expsrc, exptarget).is_some() {
return Err(ParseError);
}
}
Ok((inp, h))
}
}
#[cfg(any(test, feature = "std"))]
impl Header<'_> {
pub fn write_to<W: std::io::Write>(&self, mut writer: W) -> std::io::Result<()> {
writer.write_all(MAGIC)?;
let len: u32 = self
.symbols
.len()
.try_into()
.map_err(|e| std::io::Error::new(std::io::ErrorKind::InvalidData, e))?;
writer.write_all(&len.to_be_bytes())?;
for (k, v) in &self.symbols {
writer.write_all(&v.to_be_bytes())?;
k.write_to(&mut writer)?;
}
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
proptest::proptest! {
#![proptest_config(proptest::prelude::ProptestConfig::with_cases(4096))]
#[test]
fn doesnt_crash(inp in proptest::collection::vec(0..=u8::MAX, 0..1024)) {
if let Ok((xinp, v)) = <Header as crate::Parse<'_>>::parse(&inp[..]) {
let mut buf = alloc::vec::Vec::with_capacity(inp.len() - xinp.len());
v.write_to(&mut buf).unwrap();
assert_eq!(buf[..], inp[..inp.len() - xinp.len()]);
}
}
}
}

View file

@ -1,6 +1,7 @@
use crate::{consts::OpType, Value};
use core::fmt;
#[derive(Clone, Debug)]
pub enum Instr<'a> {
Call(Value<'a>),
Jump(u32),

View file

@ -1,80 +1,16 @@
#![cfg_attr(not(any(test, feature = "std")), no_std)]
#![forbid(unsafe_code)]
/// Fogtix bytecode is structured as type-(length?)-value items, which can be nested.
#[cfg(test)]
extern crate alloc;
mod intern {
pub trait Sealed {}
}
/// parse an object from a byte stream
pub trait Parse<'a>: Sized {
type Err: core::fmt::Debug + Send + Sync;
fn parse(inp: &'a [u8]) -> Result<(&'a [u8], Self), Self::Err>;
}
pub mod consts;
mod header;
pub use header::{Header, ParseError as HeaderParseError};
mod instr;
pub use instr::{Instr, ParseError as InstrParseError};
mod parse;
pub use parse::Parse;
mod pointer;
pub use pointer::*;
pub use pointer::{Atom, Pointer};
mod value;
pub use value::{ParseError as ValueParseError, Value};
impl<'a> Parse<'a> for u8 {
type Err = ();
#[inline]
fn parse(inp: &'a [u8]) -> Result<(&'a [u8], Self), Self::Err> {
if inp.is_empty() {
Err(())
} else {
Ok((&inp[1..], inp[0]))
}
}
}
impl<'a> Parse<'a> for u16 {
type Err = ();
fn parse(inp: &'a [u8]) -> Result<(&'a [u8], Self), Self::Err> {
if inp.len() < 2 {
Err(())
} else {
Ok((&inp[2..], Self::from_be_bytes(inp[..2].try_into().unwrap())))
}
}
}
impl<'a> Parse<'a> for u32 {
type Err = ();
fn parse(inp: &'a [u8]) -> Result<(&'a [u8], Self), Self::Err> {
if inp.len() < 4 {
Err(())
} else {
Ok((&inp[4..], Self::from_be_bytes(inp[..4].try_into().unwrap())))
}
}
}
impl<'a> Parse<'a> for u64 {
type Err = ();
fn parse(inp: &'a [u8]) -> Result<(&'a [u8], Self), Self::Err> {
if inp.len() < 8 {
Err(())
} else {
Ok((&inp[8..], Self::from_be_bytes(inp[..8].try_into().unwrap())))
}
}
}
impl<'a, T: int_enum::IntEnum + intern::Sealed> Parse<'a> for T
where
T::Int: Parse<'a, Err = ()>,
{
type Err = ();
fn parse(inp: &'a [u8]) -> Result<(&'a [u8], Self), Self::Err> {
let (inp, this) = T::Int::parse(inp)?;
T::from_int(this).map(|this| (inp, this)).map_err(|_| ())
}
}

View file

@ -0,0 +1,65 @@
pub(crate) mod intern {
pub(crate) trait Sealed {}
}
/// parse an object from a byte stream
pub trait Parse<'a>: Sized {
type Err: core::fmt::Debug + Send + Sync;
fn parse(inp: &'a [u8]) -> Result<(&'a [u8], Self), Self::Err>;
}
impl<'a> Parse<'a> for u8 {
type Err = ();
#[inline]
fn parse(inp: &'a [u8]) -> Result<(&'a [u8], Self), Self::Err> {
if inp.is_empty() {
Err(())
} else {
Ok((&inp[1..], inp[0]))
}
}
}
impl<'a> Parse<'a> for u16 {
type Err = ();
fn parse(inp: &'a [u8]) -> Result<(&'a [u8], Self), Self::Err> {
if inp.len() < 2 {
Err(())
} else {
Ok((&inp[2..], Self::from_be_bytes(inp[..2].try_into().unwrap())))
}
}
}
impl<'a> Parse<'a> for u32 {
type Err = ();
fn parse(inp: &'a [u8]) -> Result<(&'a [u8], Self), Self::Err> {
if inp.len() < 4 {
Err(())
} else {
Ok((&inp[4..], Self::from_be_bytes(inp[..4].try_into().unwrap())))
}
}
}
impl<'a> Parse<'a> for u64 {
type Err = ();
fn parse(inp: &'a [u8]) -> Result<(&'a [u8], Self), Self::Err> {
if inp.len() < 8 {
Err(())
} else {
Ok((&inp[8..], Self::from_be_bytes(inp[..8].try_into().unwrap())))
}
}
}
impl<'a, T: int_enum::IntEnum + intern::Sealed> Parse<'a> for T
where
T::Int: Parse<'a, Err = ()>,
{
type Err = ();
fn parse(inp: &'a [u8]) -> Result<(&'a [u8], Self), Self::Err> {
let (inp, this) = T::Int::parse(inp)?;
T::from_int(this).map(|this| (inp, this)).map_err(|_| ())
}
}

View file

@ -2,7 +2,7 @@ use siphasher::sip::SipHasher24 as SipHasher;
/// An atomic value (128bit),
/// can also be used to sign a pointer
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[repr(align(16))]
pub struct Atom(pub u64, pub u64);
@ -15,7 +15,7 @@ impl Atom {
/// A signed pointer, the `payload` should never by dereferenced
/// without verifying the pointer first
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[repr(align(16))]
pub struct Pointer {
hmac: u64,

View file

@ -2,7 +2,7 @@ use crate::{consts::ValueType, Atom, Pointer};
use core::fmt;
use int_enum::IntEnum;
#[derive(Clone, Debug, PartialEq)]
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub enum Value<'a> {
Bytes(&'a [u8]),
Int(u64),