feat(bytecode): +Header
This commit is contained in:
parent
ec6eed9a7f
commit
2c05579f7a
|
@ -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)]
|
||||
|
|
98
crates/fogtix-bytecode/src/header.rs
Normal file
98
crates/fogtix-bytecode/src/header.rs
Normal 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()]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,6 +1,7 @@
|
|||
use crate::{consts::OpType, Value};
|
||||
use core::fmt;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum Instr<'a> {
|
||||
Call(Value<'a>),
|
||||
Jump(u32),
|
||||
|
|
|
@ -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(|_| ())
|
||||
}
|
||||
}
|
||||
|
|
65
crates/fogtix-bytecode/src/parse.rs
Normal file
65
crates/fogtix-bytecode/src/parse.rs
Normal 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(|_| ())
|
||||
}
|
||||
}
|
|
@ -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,
|
||||
|
|
|
@ -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),
|
||||
|
|
Loading…
Reference in a new issue