API BREAK: make the VM u128-based (as only datatype)

This commit is contained in:
Alain Zscheile 2022-09-28 14:23:05 +02:00
parent 920983d003
commit 3cccbc31b8
7 changed files with 78 additions and 429 deletions

View file

@ -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, /* = */

View file

@ -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(()),
}
}
}

View file

@ -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;

View file

@ -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

View file

@ -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]);

View file

@ -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;
}
}
}
}

View file

@ -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! {