API BREAK: make the VM u128-based (as only datatype)
This commit is contained in:
parent
920983d003
commit
3cccbc31b8
|
@ -1,16 +1,6 @@
|
|||
use crate::parse::intern::Sealed;
|
||||
use int_enum::IntEnum;
|
||||
|
||||
#[repr(u8)]
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, IntEnum)]
|
||||
pub enum ValueType {
|
||||
Atom = 0x41, /* A */
|
||||
Bytes = 0x42, /* B */
|
||||
Int_ = 0x49, /* I */
|
||||
Pointer = 0x50, /* P */
|
||||
}
|
||||
impl Sealed for ValueType {}
|
||||
|
||||
#[repr(u8)]
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, IntEnum)]
|
||||
pub enum OpType {
|
||||
|
@ -28,9 +18,6 @@ pub enum OpType {
|
|||
Dup = 0x44, /* D */
|
||||
Swap = 0x53, /* S */
|
||||
|
||||
// atom operations
|
||||
ADecon = 0x2a, /* * */
|
||||
|
||||
// extensions
|
||||
DoMath1 = 0x6d, /* m */
|
||||
DoMath2 = 0x4d, /* M */
|
||||
|
@ -48,8 +35,6 @@ impl Sealed for MathUnOp {}
|
|||
#[repr(u8)]
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, IntEnum)]
|
||||
pub enum MathBinOp {
|
||||
Concat = 0x2e, /* . */
|
||||
|
||||
// comparison operations
|
||||
Lt = 0x3c, /* < */
|
||||
Eq = 0x3d, /* = */
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
use crate::consts::{MathBinOp, MathUnOp, OpType};
|
||||
use crate::Value;
|
||||
use core::fmt;
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub enum Instr<'a> {
|
||||
pub enum Instr {
|
||||
// control flow
|
||||
/// defines a destination label
|
||||
Label,
|
||||
|
@ -33,7 +32,7 @@ pub enum Instr<'a> {
|
|||
|
||||
// basic stack operations
|
||||
/// pushes the given value to the stack
|
||||
Push(Value<'a>),
|
||||
Push(u128),
|
||||
|
||||
/// pops the top $0+1 values from the stack
|
||||
Pop(u16),
|
||||
|
@ -45,9 +44,6 @@ pub enum Instr<'a> {
|
|||
/// (`Swap(0)` swaps the two top-most stack values)
|
||||
Swap(u16),
|
||||
|
||||
/// basic atom operations
|
||||
ADecon,
|
||||
|
||||
/// basic math operations (on integers and atoms)
|
||||
DoMath1(MathUnOp),
|
||||
|
||||
|
@ -55,7 +51,7 @@ pub enum Instr<'a> {
|
|||
DoMath2(MathBinOp),
|
||||
}
|
||||
|
||||
impl Instr<'_> {
|
||||
impl Instr {
|
||||
#[inline]
|
||||
pub fn typ(&self) -> OpType {
|
||||
match self {
|
||||
|
@ -69,7 +65,6 @@ impl Instr<'_> {
|
|||
Instr::Pop(_) => OpType::Pop,
|
||||
Instr::Dup(_) => OpType::Dup,
|
||||
Instr::Swap(_) => OpType::Swap,
|
||||
Instr::ADecon => OpType::ADecon,
|
||||
Instr::DoMath1(_) => OpType::DoMath1,
|
||||
Instr::DoMath2(_) => OpType::DoMath2,
|
||||
}
|
||||
|
@ -94,13 +89,7 @@ impl From<()> for ParseError {
|
|||
}
|
||||
}
|
||||
|
||||
impl From<crate::ValueParseError> for ParseError {
|
||||
fn from(_: crate::ValueParseError) -> Self {
|
||||
Self
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> crate::Parse<'a> for Instr<'a> {
|
||||
impl<'a> crate::Parse<'a> for Instr {
|
||||
type Err = ParseError;
|
||||
|
||||
fn parse(inp: &'a [u8]) -> Result<(&'a [u8], Self), ParseError> {
|
||||
|
@ -112,7 +101,7 @@ impl<'a> crate::Parse<'a> for Instr<'a> {
|
|||
OpType::CallLocal => u64::parse(inp).map(|(inp, val)| (inp, Instr::CallLocal(val))),
|
||||
OpType::CallLDefer => u64::parse(inp).map(|(inp, val)| (inp, Instr::CallLDefer(val))),
|
||||
OpType::JumpCond => u64::parse(inp).map(|(inp, val)| (inp, Instr::JumpCond(val))),
|
||||
OpType::Push => Ok(Value::parse(inp).map(|(inp, val)| (inp, Instr::Push(val)))?),
|
||||
OpType::Push => u128::parse(inp).map(|(inp, val)| (inp, Instr::Push(val))),
|
||||
OpType::Pop => u16::parse(inp).map(|(inp, val)| (inp, Instr::Pop(val))),
|
||||
OpType::Dup => u16::parse(inp).map(|(inp, val)| (inp, Instr::Dup(val))),
|
||||
OpType::Swap => u16::parse(inp).map(|(inp, val)| (inp, Instr::Swap(val))),
|
||||
|
@ -121,14 +110,13 @@ impl<'a> crate::Parse<'a> for Instr<'a> {
|
|||
OpType::Label => Ok((inp, Instr::Label)),
|
||||
OpType::CallRemote => Ok((inp, Instr::CallRemote)),
|
||||
OpType::Return => Ok((inp, Instr::Return)),
|
||||
OpType::ADecon => Ok((inp, Instr::ADecon)),
|
||||
}
|
||||
.map_err(|()| ParseError)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(test, feature = "std"))]
|
||||
impl Instr<'_> {
|
||||
impl Instr {
|
||||
pub fn write_to<W: std::io::Write>(&self, mut writer: W) -> std::io::Result<()> {
|
||||
use int_enum::IntEnum;
|
||||
writer.write_all(&self.typ().int_value().to_be_bytes())?;
|
||||
|
@ -136,13 +124,13 @@ impl Instr<'_> {
|
|||
Instr::CallLocal(val) => writer.write_all(&val.to_be_bytes()),
|
||||
Instr::CallLDefer(val) => writer.write_all(&val.to_be_bytes()),
|
||||
Instr::JumpCond(val) => writer.write_all(&val.to_be_bytes()),
|
||||
Instr::Push(val) => val.write_to(writer),
|
||||
Instr::Pop(val) => writer.write_all(&val.to_be_bytes()),
|
||||
Instr::Dup(val) => writer.write_all(&val.to_be_bytes()),
|
||||
Instr::Swap(val) => writer.write_all(&val.to_be_bytes()),
|
||||
Instr::Push(val) => writer.write_all(&val.to_be_bytes()),
|
||||
Instr::Pop(val) | Instr::Dup(val) | Instr::Swap(val) => {
|
||||
writer.write_all(&val.to_be_bytes())
|
||||
}
|
||||
Instr::DoMath1(val) => writer.write_all(&val.int_value().to_be_bytes()),
|
||||
Instr::DoMath2(val) => writer.write_all(&val.int_value().to_be_bytes()),
|
||||
Instr::Label | Instr::CallRemote | Instr::Return | Instr::ADecon => Ok(()),
|
||||
Instr::Label | Instr::CallRemote | Instr::Return => Ok(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,6 +9,4 @@ pub use instr::{Instr, ParseError as InstrParseError};
|
|||
mod parse;
|
||||
pub use parse::Parse;
|
||||
mod pointer;
|
||||
pub use pointer::{Atom, Pointer};
|
||||
mod value;
|
||||
pub use value::{ParseError as ValueParseError, Value};
|
||||
pub use pointer::Pointer;
|
||||
|
|
|
@ -20,38 +20,23 @@ impl<'a> Parse<'a> for u8 {
|
|||
}
|
||||
}
|
||||
|
||||
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())))
|
||||
}
|
||||
macro_rules! parse_number {
|
||||
($($t:ty),+) => {
|
||||
$( impl<'a> Parse<'a> for $t {
|
||||
type Err = ();
|
||||
fn parse(inp: &'a [u8]) -> Result<(&'a [u8], Self), Self::Err> {
|
||||
const TSIZ: usize = core::mem::size_of::<$t>();
|
||||
if inp.len() < TSIZ {
|
||||
Err(())
|
||||
} else {
|
||||
Ok((&inp[TSIZ..], Self::from_be_bytes(inp[..TSIZ].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())))
|
||||
}
|
||||
}
|
||||
}
|
||||
parse_number!(u16, u32, u64, u128);
|
||||
|
||||
impl<'a, T: int_enum::IntEnum + intern::Sealed> Parse<'a> for T
|
||||
where
|
||||
|
|
|
@ -1,29 +1,22 @@
|
|||
use siphasher::sip::SipHasher24 as SipHasher;
|
||||
|
||||
/// An atomic value (128bit),
|
||||
/// can also be used to sign a pointer
|
||||
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
/// A signed pointer, the `payload` should never by dereferenced
|
||||
/// without verifying the pointer first
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
#[repr(C, align(16))]
|
||||
pub struct Atom(pub [u8; 16]);
|
||||
pub struct Pointer(pub [u8; 16]);
|
||||
|
||||
impl Atom {
|
||||
impl From<u128> for Pointer {
|
||||
#[inline]
|
||||
fn build_hasher(&self) -> SipHasher {
|
||||
SipHasher::new_with_key(&self.0)
|
||||
fn from(x: u128) -> Self {
|
||||
Self(x.to_be_bytes())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<u128> for Atom {
|
||||
impl From<(u64, u64)> for Pointer {
|
||||
#[inline]
|
||||
fn from(x: u128) -> Atom {
|
||||
Atom(x.to_be_bytes())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<(u64, u64)> for Atom {
|
||||
#[inline]
|
||||
fn from((a, b): (u64, u64)) -> Atom {
|
||||
let mut this = Atom([0; 16]);
|
||||
fn from((a, b): (u64, u64)) -> Self {
|
||||
let mut this = Self([0; 16]);
|
||||
{
|
||||
let (a_, b_) = this.0.split_at_mut(8);
|
||||
a_.copy_from_slice(&a.to_be_bytes());
|
||||
|
@ -33,16 +26,16 @@ impl From<(u64, u64)> for Atom {
|
|||
}
|
||||
}
|
||||
|
||||
impl From<Atom> for u128 {
|
||||
impl From<Pointer> for u128 {
|
||||
#[inline]
|
||||
fn from(Atom(x): Atom) -> u128 {
|
||||
fn from(Pointer(x): Pointer) -> u128 {
|
||||
u128::from_be_bytes(x)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Atom> for (u64, u64) {
|
||||
impl From<Pointer> for (u64, u64) {
|
||||
#[inline]
|
||||
fn from(Atom(x): Atom) -> (u64, u64) {
|
||||
fn from(Pointer(x): Pointer) -> (u64, u64) {
|
||||
let (a, b) = x.split_at(8);
|
||||
(
|
||||
u64::from_be_bytes(a.try_into().unwrap()),
|
||||
|
@ -51,46 +44,24 @@ impl From<Atom> for (u64, u64) {
|
|||
}
|
||||
}
|
||||
|
||||
/// A signed pointer, the `payload` should never by dereferenced
|
||||
/// without verifying the pointer first
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
#[repr(C, align(16))]
|
||||
pub struct Pointer(pub [u8; 16]);
|
||||
|
||||
impl From<Pointer> for Atom {
|
||||
#[inline(always)]
|
||||
/// allows to derive a atom/pointer key from a pointer
|
||||
fn from(Pointer(x): Pointer) -> Self {
|
||||
Self(x)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Atom> for Pointer {
|
||||
#[inline(always)]
|
||||
/// allows to derive a atom/pointer key from a pointer
|
||||
fn from(Atom(x): Atom) -> Self {
|
||||
Self(x)
|
||||
}
|
||||
}
|
||||
|
||||
impl Pointer {
|
||||
fn calculate_hmac(payload: u64, key: &Atom) -> u64 {
|
||||
fn calculate_hmac(payload: u64, key: &u128) -> u64 {
|
||||
use core::hash::Hasher;
|
||||
let mut h = key.build_hasher();
|
||||
let mut h = SipHasher::new_with_key(&key.to_be_bytes());
|
||||
h.write_u64(payload & ((1 << 48) - 1));
|
||||
h.finish()
|
||||
}
|
||||
|
||||
/// SECURITY NOTE: the upper two bytes of `payload` (`origin`) aren't taken
|
||||
/// into account when calculating the HMAC because they're node-specific
|
||||
pub fn new_with_key(payload: u64, key: &Atom) -> Pointer {
|
||||
pub fn new_with_key(payload: u64, key: &u128) -> Pointer {
|
||||
let hmac = Self::calculate_hmac(payload, key);
|
||||
Atom::from((hmac, payload)).into()
|
||||
Self::from((hmac, payload))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn verify(&self, key: &Atom) -> Option<u64> {
|
||||
let (hmac, payload) = Atom(self.0).into();
|
||||
pub fn verify(&self, key: &u128) -> Option<u64> {
|
||||
let (hmac, payload) = (*self).into();
|
||||
if hmac == Self::calculate_hmac(payload, key) {
|
||||
Some(payload)
|
||||
} else {
|
||||
|
@ -128,45 +99,28 @@ impl Pointer {
|
|||
}
|
||||
}
|
||||
|
||||
impl crate::Parse<'_> for Atom {
|
||||
type Err = ();
|
||||
#[inline]
|
||||
fn parse(inp: &[u8]) -> Result<(&[u8], Self), ()> {
|
||||
Pointer::parse(inp).map(|(inp, p)| (inp, p.into()))
|
||||
}
|
||||
}
|
||||
#[cfg(any(test, feature = "std"))]
|
||||
impl Atom {
|
||||
#[inline]
|
||||
pub fn write_to<W: std::io::Write>(&self, mut writer: W) -> std::io::Result<()> {
|
||||
writer.write_all(&self.0)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
proptest::proptest! {
|
||||
#[test]
|
||||
fn atom_u128_roundtrip(x in 0..u128::MAX) {
|
||||
let atom = Atom::from(x);
|
||||
assert_eq!(u128::from(atom), x);
|
||||
fn u128_roundtrip(x in 0..u128::MAX) {
|
||||
let ptr = Pointer::from(x);
|
||||
assert_eq!(u128::from(ptr), x);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pointer_repr() {
|
||||
use core::mem;
|
||||
assert_eq!(mem::size_of::<Atom>(), 16);
|
||||
assert_eq!(mem::align_of::<Atom>(), 16);
|
||||
assert_eq!(mem::size_of::<Pointer>(), 16);
|
||||
assert_eq!(mem::align_of::<Pointer>(), 16);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pointer_usage() {
|
||||
let k = Atom([0, 0, 0, 0, 0, 0, 0xde, 0xad, 0, 0, 0, 0, 0, 0, 0xbe, 0xef]);
|
||||
let k: u128 = 0x000000000000dead000000000000beef;
|
||||
let p = Pointer::new_with_key(0xfefe, &k);
|
||||
// verify that this is the same value on all systems
|
||||
assert_eq!(p.0[0..8], [42, 115, 131, 215, 127, 235, 147, 241]);
|
||||
|
@ -176,7 +130,7 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn pointer_usage2() {
|
||||
let k = Atom([0, 0, 0, 0, 0, 0, 0xde, 0xad, 0, 0, 0, 0, 0, 0, 0xbe, 0xef]);
|
||||
let k: u128 = 0x000000000000dead000000000000beef;
|
||||
let p = Pointer::new_with_key(0x0508deadbeeffefe, &k);
|
||||
// verify that this is the same value on all systems
|
||||
assert_eq!(p.0[0..8], [191, 23, 107, 0, 61, 74, 249, 219]);
|
||||
|
@ -186,7 +140,7 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn pointer_usage3() {
|
||||
let k = Atom([0, 0, 0, 0, 0, 0, 0xde, 0xad, 0, 0, 0, 0, 0, 0, 0xbe, 0xef]);
|
||||
let k: u128 = 0x000000000000dead000000000000beef;
|
||||
let p = Pointer::new_with_key(0xf7d8deadbeeffefe, &k);
|
||||
// verify that this is the same value on all systems
|
||||
assert_eq!(p.0[0..8], [191, 23, 107, 0, 61, 74, 249, 219]);
|
||||
|
|
|
@ -1,110 +0,0 @@
|
|||
use crate::consts::ValueType;
|
||||
use crate::{Atom, Pointer};
|
||||
use core::fmt;
|
||||
use int_enum::IntEnum;
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub enum Value<'a> {
|
||||
Bytes(&'a [u8]),
|
||||
Int(u64),
|
||||
Atom(Atom),
|
||||
Pointer(Pointer),
|
||||
}
|
||||
|
||||
impl Value<'_> {
|
||||
#[inline]
|
||||
pub fn typ(&self) -> ValueType {
|
||||
match self {
|
||||
Value::Bytes(_) => ValueType::Bytes,
|
||||
Value::Int(_) => ValueType::Int_,
|
||||
Value::Atom(_) => ValueType::Atom,
|
||||
Value::Pointer(_) => ValueType::Pointer,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub struct ParseError;
|
||||
|
||||
impl fmt::Display for ParseError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "unable to parse value from buffer")
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
impl std::error::Error for ParseError {}
|
||||
|
||||
impl From<()> for ParseError {
|
||||
fn from(_: ()) -> Self {
|
||||
Self
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: int_enum::IntEnum> From<int_enum::IntEnumError<T>> for ParseError {
|
||||
fn from(_: int_enum::IntEnumError<T>) -> Self {
|
||||
Self
|
||||
}
|
||||
}
|
||||
|
||||
impl From<core::num::TryFromIntError> for ParseError {
|
||||
fn from(_: core::num::TryFromIntError) -> Self {
|
||||
Self
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> crate::Parse<'a> for Value<'a> {
|
||||
type Err = ParseError;
|
||||
|
||||
fn parse(inp: &'a [u8]) -> Result<(&'a [u8], Self), ParseError> {
|
||||
if inp.len() < 2 {
|
||||
return Err(ParseError);
|
||||
}
|
||||
let (vtyp, inp) = (ValueType::from_int(inp[0])?, &inp[1..]);
|
||||
match vtyp {
|
||||
ValueType::Bytes => <&[u8]>::parse(inp).map(|(inp, data)| (inp, Value::Bytes(data))),
|
||||
ValueType::Int_ => u64::parse(inp).map(|(inp, data)| (inp, Value::Int(data))),
|
||||
ValueType::Pointer => Pointer::parse(inp).map(|(inp, ptr)| (inp, Value::Pointer(ptr))),
|
||||
ValueType::Atom => Atom::parse(inp).map(|(inp, atom)| (inp, Value::Atom(atom))),
|
||||
}
|
||||
.map_err(|()| ParseError)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(test, feature = "std"))]
|
||||
impl Value<'_> {
|
||||
pub fn write_to<W: std::io::Write>(&self, mut writer: W) -> std::io::Result<()> {
|
||||
writer.write_all(&[self.typ().int_value()])?;
|
||||
match self {
|
||||
Value::Bytes(b) => {
|
||||
let len: u64 = b.len().try_into().unwrap();
|
||||
writer.write_all(&len.to_be_bytes())?;
|
||||
writer.write_all(b)?;
|
||||
}
|
||||
Value::Int(i) => writer.write_all(&i.to_be_bytes())?,
|
||||
Value::Pointer(p) => p.write_to(writer)?,
|
||||
Value::Atom(pk) => pk.write_to(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)) {
|
||||
let mut inp = &inp[..];
|
||||
while let Ok((xinp, v)) = <Value 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()]);
|
||||
inp = xinp;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -3,9 +3,9 @@
|
|||
|
||||
extern crate alloc;
|
||||
|
||||
use alloc::{format, string::String, string::ToString, sync::Arc, vec::Vec};
|
||||
use alloc::{format, string::String, sync::Arc, vec::Vec};
|
||||
use core::fmt;
|
||||
use fogtix_bytecode::{consts, Atom, Instr, Parse, Pointer, Value as BcValue};
|
||||
use fogtix_bytecode::{consts, Instr, Parse, Pointer};
|
||||
|
||||
pub type Module = Arc<dyn ModuleKind>;
|
||||
|
||||
|
@ -26,7 +26,7 @@ pub struct InstrPtr {
|
|||
pub pos: usize,
|
||||
}
|
||||
|
||||
fn next_instr(m: &Module, pos: usize) -> Option<Result<(usize, Instr<'_>), &[u8]>> {
|
||||
fn next_instr(m: &Module, pos: usize) -> Option<Result<(usize, Instr), &[u8]>> {
|
||||
m.as_slice()
|
||||
.get(pos..)
|
||||
.map(|nxti_arr| match Instr::parse(nxti_arr) {
|
||||
|
@ -37,7 +37,7 @@ fn next_instr(m: &Module, pos: usize) -> Option<Result<(usize, Instr<'_>), &[u8]
|
|||
|
||||
impl InstrPtr {
|
||||
#[inline(always)]
|
||||
pub fn next_instr(&self) -> Option<Result<(usize, Instr<'_>), &[u8]>> {
|
||||
pub fn next_instr(&self) -> Option<Result<(usize, Instr), &[u8]>> {
|
||||
next_instr(&self.m, self.pos)
|
||||
}
|
||||
|
||||
|
@ -47,13 +47,7 @@ impl InstrPtr {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum StackEntValue {
|
||||
Bytes(Vec<u8>),
|
||||
Int(u64),
|
||||
Atom(Atom),
|
||||
Pointer(Pointer),
|
||||
}
|
||||
pub type StackEntValue = u128;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum Error {
|
||||
|
@ -62,7 +56,6 @@ pub enum Error {
|
|||
UnparsableInstruction(Vec<u8>),
|
||||
OutOfFuel,
|
||||
NotEnoughStacked,
|
||||
StackedInvalidType { expected: &'static str, got: String },
|
||||
DivisionByZero,
|
||||
RemoteCall(Pointer),
|
||||
}
|
||||
|
@ -77,11 +70,6 @@ impl fmt::Display for Error {
|
|||
Self::UnparsableInstruction(x) => write!(f, "reached unparsable instruction: {:x?}", x),
|
||||
Self::OutOfFuel => write!(f, "out of fuel"),
|
||||
Self::NotEnoughStacked => write!(f, "not enough operands on stack"),
|
||||
Self::StackedInvalidType { expected, got } => write!(
|
||||
f,
|
||||
"operand from stack doesn't have correct data type: expected={}, got={}",
|
||||
expected, got
|
||||
),
|
||||
Self::DivisionByZero => write!(f, "tried to divide by zero"),
|
||||
Self::RemoteCall(ptr) => write!(f, "tried to call remote @ {:x?}", ptr.0),
|
||||
}
|
||||
|
@ -114,34 +102,6 @@ fn verify_jumptarget_explicit(
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn u128_binmath(a: u128, b: u128, mbo: consts::MathBinOp) -> Result<StackEntValue, Error> {
|
||||
use consts::MathBinOp as B;
|
||||
Ok(StackEntValue::Atom(Atom::from(match mbo {
|
||||
B::Concat => {
|
||||
return Err(Error::StackedInvalidType {
|
||||
expected: "bytes,bytes",
|
||||
got: "[int|atom, int|atom]".to_string(),
|
||||
})
|
||||
}
|
||||
B::Lt => return Ok(StackEntValue::Int(if a < b { 1 } else { 0 })),
|
||||
B::Eq => return Ok(StackEntValue::Int(if a < b { 1 } else { 0 })),
|
||||
B::And => a & b,
|
||||
B::Or => a | b,
|
||||
B::Xor => a ^ b,
|
||||
B::Add => a.wrapping_add(b),
|
||||
B::Sub => a.wrapping_sub(b),
|
||||
B::Mul => a.wrapping_mul(b),
|
||||
B::Div => match a.checked_div(b) {
|
||||
Some(x) => x,
|
||||
None => return Err(Error::DivisionByZero),
|
||||
},
|
||||
B::Rem => match a.checked_rem(b) {
|
||||
Some(x) => x,
|
||||
None => return Err(Error::DivisionByZero),
|
||||
},
|
||||
})))
|
||||
}
|
||||
|
||||
impl Process {
|
||||
fn verify_jumptarget(&self, previptr: usize, jinstr: &'static str) -> Result<(), Error> {
|
||||
verify_jumptarget_explicit(previptr, jinstr, &self.instrp)
|
||||
|
@ -173,16 +133,7 @@ impl Process {
|
|||
if !self.instrp.is_call2jump() {
|
||||
self.callstack.push(self.instrp.clone());
|
||||
}
|
||||
let ptr = match self.stpop()? {
|
||||
StackEntValue::Pointer(ptr) => ptr,
|
||||
x => {
|
||||
return Err(Error::StackedInvalidType {
|
||||
expected: "pointer",
|
||||
got: format!("{:?}", x),
|
||||
})
|
||||
}
|
||||
};
|
||||
return Err(Error::RemoteCall(ptr));
|
||||
return Err(Error::RemoteCall(Pointer::from(self.stpop()?)));
|
||||
}
|
||||
Instr::CallLocal(x) => {
|
||||
if !self.instrp.is_call2jump() {
|
||||
|
@ -214,17 +165,7 @@ impl Process {
|
|||
Ok(y) => y,
|
||||
Err(_) => return Err(Error::InstrpOutOfBounds),
|
||||
};
|
||||
let doit = match self.stpop()? {
|
||||
StackEntValue::Int(i) => i != 0,
|
||||
StackEntValue::Atom(a) => a != Atom([0; 16]),
|
||||
z => {
|
||||
return Err(Error::StackedInvalidType {
|
||||
expected: "atom | int",
|
||||
got: format!("{:?}", z),
|
||||
})
|
||||
}
|
||||
};
|
||||
if doit {
|
||||
if self.stpop()? != 0 {
|
||||
self.instrp.pos = x;
|
||||
self.verify_jumptarget(previptr, "jump-cond")?;
|
||||
}
|
||||
|
@ -233,14 +174,7 @@ impl Process {
|
|||
Some(x) => self.instrp = x,
|
||||
None => break Ok(()),
|
||||
},
|
||||
Instr::Push(v) => {
|
||||
self.stack.push(match v {
|
||||
BcValue::Bytes(v) => StackEntValue::Bytes(v.to_vec()),
|
||||
BcValue::Int(i) => StackEntValue::Int(i),
|
||||
BcValue::Atom(a) => StackEntValue::Atom(a),
|
||||
BcValue::Pointer(p) => StackEntValue::Pointer(p),
|
||||
});
|
||||
}
|
||||
Instr::Push(v) => self.stack.push(v),
|
||||
Instr::Pop(_) if self.stack.is_empty() => return Err(Error::NotEnoughStacked),
|
||||
Instr::Pop(cnt) => {
|
||||
let ssl = self.stack.len() - 1;
|
||||
|
@ -267,119 +201,34 @@ impl Process {
|
|||
};
|
||||
core::mem::swap(&mut y[0], &mut z[0]);
|
||||
}
|
||||
Instr::ADecon => match self.stpop()? {
|
||||
StackEntValue::Atom(atom) => {
|
||||
let (b, a) = atom.into();
|
||||
self.stack.push(StackEntValue::Int(b));
|
||||
self.stack.push(StackEntValue::Int(a));
|
||||
}
|
||||
x => {
|
||||
return Err(Error::StackedInvalidType {
|
||||
expected: "atom",
|
||||
got: format!("{:?}", x),
|
||||
})
|
||||
}
|
||||
},
|
||||
Instr::DoMath1(ubo) => {
|
||||
use consts::MathUnOp as U;
|
||||
use StackEntValue as V;
|
||||
let x = self.stpop()?;
|
||||
self.stack.push(match (x, ubo) {
|
||||
(V::Int(x), U::Not) => V::Int(!x),
|
||||
(V::Atom(x), U::Not) => V::Atom(Atom::from(!u128::from(x))),
|
||||
(V::Bytes(mut x), U::Not) => {
|
||||
x.iter_mut().for_each(|i| *i = !*i);
|
||||
V::Bytes(x)
|
||||
}
|
||||
(x, _) => {
|
||||
return Err(Error::StackedInvalidType {
|
||||
expected: "int|atom|bytes",
|
||||
got: format!("{:?}", x),
|
||||
})
|
||||
}
|
||||
self.stack.push(match ubo {
|
||||
U::Not => !x,
|
||||
});
|
||||
}
|
||||
Instr::DoMath2(mbo) => {
|
||||
let b = self.stpop()?;
|
||||
let a = self.stpop()?;
|
||||
use consts::MathBinOp as B;
|
||||
use StackEntValue as V;
|
||||
self.stack.push(match (a, b) {
|
||||
(V::Int(a), V::Int(b)) => match mbo {
|
||||
B::Concat => V::Atom(Atom::from((a, b))),
|
||||
B::Lt => V::Int(if a < b { 1 } else { 0 }),
|
||||
B::Eq => V::Int(if a == b { 1 } else { 0 }),
|
||||
B::And => V::Int(a & b),
|
||||
B::Or => V::Int(a | b),
|
||||
B::Xor => V::Int(a ^ b),
|
||||
B::Add => V::Int(a.wrapping_add(b)),
|
||||
B::Sub => V::Int(a.wrapping_sub(b)),
|
||||
B::Mul => {
|
||||
V::Atom(Atom::from(u128::from(a).wrapping_mul(u128::from(b))))
|
||||
}
|
||||
B::Div => V::Int(match a.checked_div(b) {
|
||||
Some(x) => x,
|
||||
None => return Err(Error::DivisionByZero),
|
||||
}),
|
||||
B::Rem => V::Int(match a.checked_rem(b) {
|
||||
Some(x) => x,
|
||||
None => return Err(Error::DivisionByZero),
|
||||
}),
|
||||
self.stack.push(match mbo {
|
||||
B::Lt => if a < b { 1 } else { 0 },
|
||||
B::Eq => if a < b { 1 } else { 0 },
|
||||
B::And => a & b,
|
||||
B::Or => a | b,
|
||||
B::Xor => a ^ b,
|
||||
B::Add => a.wrapping_add(b),
|
||||
B::Sub => a.wrapping_sub(b),
|
||||
B::Mul => a.wrapping_mul(b),
|
||||
B::Div => match a.checked_div(b) {
|
||||
Some(x) => x,
|
||||
None => return Err(Error::DivisionByZero),
|
||||
},
|
||||
(V::Atom(a), V::Atom(b)) => {
|
||||
u128_binmath(u128::from(a), u128::from(b), mbo)?
|
||||
}
|
||||
(V::Int(a), V::Atom(b)) => u128_binmath(u128::from(a), u128::from(b), mbo)?,
|
||||
(V::Atom(a), V::Int(b)) => u128_binmath(u128::from(a), u128::from(b), mbo)?,
|
||||
(V::Bytes(a), V::Bytes(b)) => match mbo {
|
||||
B::Concat => {
|
||||
let mut x = Vec::with_capacity(a.len() + b.len());
|
||||
x.extend_from_slice(&a);
|
||||
x.extend_from_slice(&b);
|
||||
V::Bytes(x)
|
||||
}
|
||||
B::Eq => V::Int(if a == b { 1 } else { 0 }),
|
||||
B::And => {
|
||||
let mut x: Vec<_> =
|
||||
a.iter().zip(b.iter()).map(|(w, v)| w & v).collect();
|
||||
let minp = x.len();
|
||||
x.extend_from_slice(
|
||||
&(if a.len() < b.len() { b } else { a })[minp..],
|
||||
);
|
||||
V::Bytes(x)
|
||||
}
|
||||
B::Or => {
|
||||
let mut x: Vec<_> =
|
||||
a.iter().zip(b.iter()).map(|(w, v)| w | v).collect();
|
||||
let minp = x.len();
|
||||
x.extend_from_slice(
|
||||
&(if a.len() < b.len() { b } else { a })[minp..],
|
||||
);
|
||||
V::Bytes(x)
|
||||
}
|
||||
B::Xor => {
|
||||
let mut x: Vec<_> =
|
||||
a.iter().zip(b.iter()).map(|(w, v)| w ^ v).collect();
|
||||
let minp = x.len();
|
||||
x.extend_from_slice(
|
||||
&(if a.len() < b.len() { b } else { a })[minp..],
|
||||
);
|
||||
V::Bytes(x)
|
||||
}
|
||||
_ => {
|
||||
return Err(Error::StackedInvalidType {
|
||||
expected: "[int|atom, int|atom]",
|
||||
got: "[bytes, bytes]".to_string(),
|
||||
})
|
||||
}
|
||||
B::Rem => match a.checked_rem(b) {
|
||||
Some(x) => x,
|
||||
None => return Err(Error::DivisionByZero),
|
||||
},
|
||||
_ if mbo == B::Eq => V::Int(0),
|
||||
(a, b) => {
|
||||
return Err(Error::StackedInvalidType {
|
||||
expected: "[int|atom, int|atom] | [bytes,bytes]",
|
||||
got: format!("{:?}, {:?}", a, b),
|
||||
})
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -393,7 +242,7 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn stack_item_size() {
|
||||
assert_eq!(core::mem::size_of::<StackEntValue>(), 32);
|
||||
assert_eq!(core::mem::size_of::<StackEntValue>(), 16);
|
||||
}
|
||||
|
||||
proptest::proptest! {
|
||||
|
|
Loading…
Reference in a new issue