diff --git a/rust/ares/Cargo.toml b/rust/ares/Cargo.toml index 7f7866f..ea0f734 100644 --- a/rust/ares/Cargo.toml +++ b/rust/ares/Cargo.toml @@ -12,9 +12,9 @@ edition = "2018" # Please keep these alphabetized [dependencies] ares_macros = { path = "../ares_macros" } -# assert_no_alloc = "1.1.2" +assert_no_alloc = "1.1.2" # use this when debugging requires allocation (e.g. eprintln) -assert_no_alloc = {version="1.1.2", features=["warn_debug"]} +# assert_no_alloc = {version="1.1.2", features=["warn_debug"]} bitvec = "1.0.0" criterion = "0.4" either = "1.9.0" diff --git a/rust/ares/src/jets.rs b/rust/ares/src/jets.rs index 6d97162..8c31292 100644 --- a/rust/ares/src/jets.rs +++ b/rust/ares/src/jets.rs @@ -49,15 +49,15 @@ pub enum JetErr { Fail(Error), // Error; do not retry } -impl From for JetErr { - fn from(_err: noun::Error) -> Self { - Self::Fail(Error::Deterministic(D(0))) +impl From for JetErr { + fn from(err: Error) -> Self { + Self::Fail(err) } } -impl From for JetErr { - fn from(e: Error) -> Self { - Self::Fail(e) +impl From for JetErr { + fn from(_err: noun::Error) -> Self { + Self::Fail(Error::Deterministic(D(0))) } } @@ -343,6 +343,14 @@ pub mod util { } } + pub fn assert_jet_size(context: &mut Context, jet: Jet, sam: Noun, siz: usize) { + let sam = T(&mut context.stack, &[D(0), sam, D(0)]); + let res = assert_no_alloc(|| jet(context, sam).unwrap()); + assert!(res.is_atom(), "jet result not atom"); + let res_siz = res.atom().unwrap().size(); + assert!(siz == res_siz, "got: {}, need: {}", res_siz, siz); + } + pub fn assert_common_jet( context: &mut Context, jet: Jet, @@ -374,5 +382,16 @@ pub mod util { let sam = T(&mut context.stack, &sam); assert_jet_err(context, jet, sam, err); } + + pub fn assert_common_jet_size( + context: &mut Context, + jet: Jet, + sam: &[fn(&mut NockStack) -> Noun], + siz: usize, + ) { + let sam: Vec = sam.iter().map(|f| f(&mut context.stack)).collect(); + let sam = T(&mut context.stack, &sam); + assert_jet_size(context, jet, sam, siz) + } } } diff --git a/rust/ares/src/jets/bits.rs b/rust/ares/src/jets/bits.rs index bca0d5b..cc35a16 100644 --- a/rust/ares/src/jets/bits.rs +++ b/rust/ares/src/jets/bits.rs @@ -130,7 +130,7 @@ pub fn jet_lsh(context: &mut Context, subject: Noun) -> Result { let (bloq, step) = bite(slot(arg, 2)?)?; let a = slot(arg, 3)?.as_atom()?; - util::lsh(&mut context.stack, bloq, step, a).map(|a| a.as_noun()) + util::lsh(&mut context.stack, bloq, step, a) } pub fn jet_met(_context: &mut Context, subject: Noun) -> Result { @@ -306,13 +306,11 @@ pub fn jet_mix(context: &mut Context, subject: Noun) -> Result { } pub mod util { - use crate::jets; use crate::jets::util::*; - use crate::jets::JetErr; + use crate::jets::Result; use crate::mem::NockStack; use crate::noun::{Atom, Cell, DirectAtom, IndirectAtom, D, Noun}; use std::cmp; - use std::result; /// Binary exponent pub fn bex(stack: &mut NockStack, arg: usize) -> Atom { @@ -327,22 +325,17 @@ pub mod util { } } - pub fn lsh( - stack: &mut NockStack, - bloq: usize, - step: usize, - a: Atom, - ) -> result::Result { + pub fn lsh(stack: &mut NockStack, bloq: usize, step: usize, a: Atom) -> Result { let len = met(bloq, a); if len == 0 { - return Ok(D(0).as_atom()?); + return Ok(D(0)); } let new_size = bits_to_word(checked_add(a.bit_size(), checked_left_shift(bloq, step)?)?)?; unsafe { let (mut atom, dest) = IndirectAtom::new_raw_mut_bitslice(stack, new_size); chop(bloq, 0, len, step, dest, a.as_bitslice())?; - Ok(atom.normalize_as_atom()) + Ok(atom.normalize_as_atom().as_noun()) } } @@ -358,7 +351,7 @@ pub mod util { } } - pub fn rip(stack: &mut NockStack, bloq: usize, step: usize, atom: Atom) -> jets::Result { + pub fn rip(stack: &mut NockStack, bloq: usize, step: usize, atom: Atom) -> Result { let len = (met(bloq, atom) + step - 1) / step; let mut list = D(0); for i in (0..len).rev() { diff --git a/rust/ares/src/jets/list.rs b/rust/ares/src/jets/list.rs index 815d1f7..c281d35 100644 --- a/rust/ares/src/jets/list.rs +++ b/rust/ares/src/jets/list.rs @@ -9,12 +9,12 @@ crate::gdb!(); pub fn jet_flop(context: &mut Context, subject: Noun) -> Result { let sam = slot(subject, 6)?; - Ok(util::flop(&mut context.stack, sam)?) + util::flop(&mut context.stack, sam) } pub fn jet_lent(_context: &mut Context, subject: Noun) -> Result { - let tape = slot(subject, 6)?; - util::lent(tape).map(|x| D(x as u64)) + let list = slot(subject, 6)?; + util::lent(list).map(|x| D(x as u64)) } pub fn jet_zing(context: &mut Context, subject: Noun) -> Result { @@ -26,14 +26,13 @@ pub fn jet_zing(context: &mut Context, subject: Noun) -> Result { pub mod util { use crate::interpreter::Error; - use crate::jets; - use crate::jets::JetErr; + use crate::jets::{JetErr, Result}; use crate::mem::NockStack; use crate::noun::{Cell, Noun, D, T}; - use std::result::Result; + use std::result; /// Reverse order of list - pub fn flop(stack: &mut NockStack, noun: Noun) -> Result { + pub fn flop(stack: &mut NockStack, noun: Noun) -> Result { let mut list = noun; let mut tsil = D(0); loop { @@ -49,7 +48,7 @@ pub mod util { Ok(tsil) } - pub fn lent(tape: Noun) -> Result { + pub fn lent(tape: Noun) -> result::Result { let mut len = 0usize; let mut list = tape; loop { @@ -68,50 +67,29 @@ pub mod util { Ok(len) } - pub fn zing(stack: &mut NockStack, mut list: Noun) -> jets::Result { + pub fn zing(stack: &mut NockStack, mut list: Noun) -> Result { unsafe { - let (mut new_cell, mut new_memory) = Cell::new_raw_mut(stack); - #[allow(unused_assignments)] - let (mut cell, mut memory) = (new_cell, new_memory); let mut res: Noun = D(0); - let mut flag = false; - - loop { - if list.raw_equals(D(0)) { - break; - } + let mut dest = &mut res as *mut Noun; + while !list.raw_equals(D(0)) { let pair = list.as_cell()?; - let mut sub_list = pair.head(); - - loop { - if sub_list.raw_equals(D(0)) { - break; - } - - let elem = sub_list.as_cell()?; - let head = elem.head(); - - if flag { - (new_cell, new_memory) = Cell::new_raw_mut(stack); - (*memory).tail = new_cell.as_noun(); - memory = new_memory; - } else { - (cell, memory) = Cell::new_raw_mut(stack); - res = cell.as_noun(); - flag = true; - } - (*memory).head = head; - - sub_list = elem.tail(); - } - + let mut sublist = pair.head(); list = pair.tail(); + + while !sublist.raw_equals(D(0)) { + let it = sublist.as_cell()?; + let i = it.head(); + sublist = it.tail(); + + let (new_cell, new_memory) = Cell::new_raw_mut(stack); + (*new_memory).head = i; + *dest = new_cell.as_noun(); + dest = &mut (*new_memory).tail; + } } - if flag { - (*memory).tail = D(0); - } + *dest = D(0); Ok(res) } } diff --git a/rust/ares/src/jets/math.rs b/rust/ares/src/jets/math.rs index 6dc0537..f7b03be 100644 --- a/rust/ares/src/jets/math.rs +++ b/rust/ares/src/jets/math.rs @@ -307,6 +307,7 @@ pub mod util { } } + /// Less than pub fn lth(stack: &mut NockStack, a: Atom, b: Atom) -> Noun { if let (Ok(a), Ok(b)) = (a.as_direct(), b.as_direct()) { if a.data() < b.data() { diff --git a/rust/ares/src/jets/tree.rs b/rust/ares/src/jets/tree.rs index 0ec48cc..ede01cd 100644 --- a/rust/ares/src/jets/tree.rs +++ b/rust/ares/src/jets/tree.rs @@ -1,18 +1,17 @@ /** Tree jets */ use crate::interpreter::{Context, Error}; -use crate::jets::bits; -use crate::jets::math; -use crate::jets::util::slot; +use crate::jets::bits::util::*; +use crate::jets::util::*; use crate::jets::{JetErr, Result}; -use crate::noun::{Noun, D}; +use crate::noun::{IndirectAtom, Noun, D}; crate::gdb!(); pub fn jet_cap(_context: &mut Context, subject: Noun) -> Result { let arg = slot(subject, 6)?; let tom = arg.as_atom()?; - let met = bits::util::met(0, tom); + let met = met(0, tom); unsafe { if met < 2 { @@ -27,43 +26,53 @@ pub fn jet_cap(_context: &mut Context, subject: Noun) -> Result { pub fn jet_mas(context: &mut Context, subject: Noun) -> Result { let stack = &mut context.stack; - let arg = slot(subject, 6)?; - let tom = arg.as_atom()?; - let met = bits::util::met(0, tom); + let tom = slot(subject, 6)?.as_atom()?; + let met = met(0, tom); if met < 2 { Err(JetErr::Fail(Error::Deterministic(D(0)))) } else { - let c = bits::util::bex(stack, met - 1); - let d = bits::util::bex(stack, met - 2); - let e = math::util::sub(stack, tom, c)?; - - Ok(bits::util::con(stack, e, d).as_noun()) + let out_bits = met - 1; + let out_words = (out_bits + 63) >> 6; + let (mut indirect_out, out_bs) = + unsafe { IndirectAtom::new_raw_mut_bitslice(stack, out_words) }; + out_bs.set(met - 2, true); // Set MSB + if met > 2 { + out_bs[0..(met - 2)].copy_from_bitslice(&tom.as_bitslice()[0..(met - 2)]); + }; + unsafe { Ok(indirect_out.normalize_as_atom().as_noun()) } } } pub fn jet_peg(context: &mut Context, subject: Noun) -> Result { let stack = &mut context.stack; let arg = slot(subject, 6)?; - let a = slot(arg, 2)?; - let b = slot(arg, 3)?; + let a = slot(arg, 2)?.as_atom()?; + let b = slot(arg, 3)?.as_atom()?; unsafe { - if a.raw_equals(D(0)) { + if a.as_noun().raw_equals(D(0)) { return Err(JetErr::Fail(Error::Deterministic(D(0)))); - } - // XX: Import jet mistmatch from Vere - if b.raw_equals(D(0)) { - return Err(JetErr::Fail(Error::Deterministic(D(0)))); - } - }; + }; - let c = bits::util::met(0, b.as_atom()?); - let d = c - 1; - let e = bits::util::lsh(stack, 0, d, D(1).as_atom()?)?; - let f = math::util::sub(stack, b.as_atom()?, e)?; - let g = bits::util::lsh(stack, 0, d, a.as_atom()?)?; - Ok(math::util::add(stack, f, g).as_noun()) + if b.as_noun().raw_equals(D(0)) { + return Err(JetErr::Fail(Error::Deterministic(D(0)))); + }; + } + + let a_bits = met(0, a); + let b_bits = met(0, b); + let out_bits = a_bits + b_bits - 1; + + let out_words = (out_bits + 63) >> 6; // bits to 8-byte words + + let (mut indirect_out, out_bs) = + unsafe { IndirectAtom::new_raw_mut_bitslice(stack, out_words) }; + + out_bs[0..b_bits - 1].copy_from_bitslice(&b.as_bitslice()[0..b_bits - 1]); + out_bs[b_bits - 1..out_bits].copy_from_bitslice(&a.as_bitslice()[0..a_bits]); + + unsafe { Ok(indirect_out.normalize_as_atom().as_noun()) } } #[cfg(test)] @@ -92,10 +101,18 @@ mod tests { D(0x4000000000000000) } + fn atom_64(stack: &mut NockStack) -> Noun { + A(stack, &ubig!(_0x8000000000000000)) + } + fn atom_65(stack: &mut NockStack) -> Noun { A(stack, &ubig!(_0x10000000000000000)) } + fn atom_66(stack: &mut NockStack) -> Noun { + A(stack, &ubig!(_0x20000000000000000)) + } + fn pos_2(_stack: &mut NockStack) -> Noun { D(2) } @@ -127,10 +144,16 @@ mod tests { #[test] fn test_mas() { let c = &mut init_context(); + let a63 = atom_63(&mut c.stack); + let a64 = atom_64(&mut c.stack); + let a65 = atom_65(&mut c.stack); + let a66 = atom_66(&mut c.stack); + // Test invalid input assert_jet_err(c, jet_mas, D(0), JetErr::Fail(Error::Deterministic(D(0)))); assert_jet_err(c, jet_mas, D(1), JetErr::Fail(Error::Deterministic(D(0)))); + // Test direct assert_jet(c, jet_mas, D(2), D(1)); assert_jet(c, jet_mas, D(3), D(1)); assert_jet(c, jet_mas, D(4), D(2)); @@ -138,6 +161,16 @@ mod tests { assert_jet(c, jet_mas, D(6), D(2)); assert_jet(c, jet_mas, D(7), D(3)); assert_jet(c, jet_mas, D(8), D(4)); + + // Test indirect + assert_jet(c, jet_mas, a64, a63); + assert_jet(c, jet_mas, a65, a64); + assert_jet(c, jet_mas, a66, a65); + + // Test allocation + assert_jet_size(c, jet_mas, D(2), 1_usize); + assert_jet_size(c, jet_mas, a65, 1_usize); + assert_jet_size(c, jet_mas, a66, 2_usize); } #[test] @@ -174,5 +207,10 @@ mod tests { &[atom_65, atom_65], ubig!(_0x100000000000000000000000000000000), ); + + // Test allocation + assert_common_jet_size(c, jet_peg, &[atom_65, atom_1], 2_usize); + assert_common_jet_size(c, jet_peg, &[atom_1, atom_65], 2_usize); + assert_common_jet_size(c, jet_peg, &[atom_65, atom_65], 3_usize); } } diff --git a/rust/ares/src/noun.rs b/rust/ares/src/noun.rs index 4b2afe5..c151126 100644 --- a/rust/ares/src/noun.rs +++ b/rust/ares/src/noun.rs @@ -40,7 +40,7 @@ const FORWARDING_MASK: u64 = CELL_MASK; /** Loobeans */ pub const YES: Noun = D(0); pub const NO: Noun = D(1); -pub const NONE: Noun = unsafe { DirectAtom::new_unchecked(tas!(b"mormagic")).as_noun() }; +pub const NONE: Noun = unsafe { DirectAtom::new_unchecked(tas!(b"MORMAGIC")).as_noun() }; #[cfg(feature = "check_acyclic")] #[macro_export]