jets: add checked helper functions

This commit is contained in:
Alex Shelkovnykov 2023-09-03 23:19:16 -06:00
parent a90b23d263
commit bb244ee52b
2 changed files with 67 additions and 70 deletions

View File

@ -17,7 +17,10 @@ crate::gdb!();
pub type Result = std::result::Result<Noun, JetErr>;
pub type Jet = fn(&mut NockStack, &mut Option<&mut Newt>, Noun) -> Result;
/// Only return a deterministic error if the Nock would have deterministically crashed.
/**
* Only return a deterministic error if the Nock would have deterministically
* crashed.
*/
#[derive(Debug, PartialEq)]
pub enum JetErr {
Punt, // Retry with the raw nock
@ -25,12 +28,6 @@ pub enum JetErr {
NonDeterministic, // Other error
}
impl From<()> for JetErr {
fn from(_: ()) -> Self {
JetErr::NonDeterministic
}
}
impl From<noun::Error> for JetErr {
fn from(_err: noun::Error) -> Self {
Self::Deterministic
@ -98,24 +95,55 @@ pub mod util {
use bitvec::prelude::{BitSlice, Lsb0};
use ibig::UBig;
use std::result;
/// Performs addition that returns None on Noun size overflow
pub fn checked_add(a: usize, b: usize) -> result::Result<usize, JetErr> {
a.checked_add(b).ok_or(JetErr::NonDeterministic)
}
/// Performs addition that returns None on Noun size overflow
pub fn checked_sub(a: usize, b: usize) -> result::Result<usize, JetErr> {
a.checked_sub(b).ok_or(JetErr::NonDeterministic)
}
pub fn checked_left_shift(bloq: usize, a: usize) -> result::Result<usize, JetErr> {
let res = a << bloq;
// Catch overflow
if (res >> bloq) < a {
Err(JetErr::NonDeterministic)
} else {
Ok(res)
}
}
/// Convert length in bits to length in 64-bit words
pub fn bits_to_word(a: usize) -> result::Result<usize, JetErr> {
checked_add(a, 63).map(|x| x >> 6)
}
/// Convert length as bite to length in 64-bit words
pub fn bite_to_word(bloq: usize, step: usize) -> result::Result<usize, JetErr> {
bits_to_word(checked_left_shift(bloq, step)?)
}
pub fn slot(noun: Noun, axis: u64) -> Result {
noun.slot(axis).map_err(|_e| JetErr::Deterministic)
}
/** Extract the bloq and step from a bite */
pub fn bite(a: Noun) -> result::Result<(usize, usize), ()> {
/// Extract the bloq and step from a bite
pub fn bite(a: Noun) -> result::Result<(usize, usize), JetErr> {
if let Ok(cell) = a.as_cell() {
let bloq = cell.head().as_direct()?.data() as usize;
if bloq >= 64 {
return Err(());
return Err(JetErr::NonDeterministic);
}
let step = cell.tail().as_direct()?.data() as usize;
Ok((bloq, step))
} else {
let bloq = a.as_direct()?.data() as usize;
if bloq >= 64 {
return Err(());
return Err(JetErr::NonDeterministic);
}
Ok((bloq, 1))
}
@ -123,8 +151,8 @@ pub mod util {
/** In a bloq space, copy from `from` for a span of `step`, to position `to`.
*
* Note: unlike the vere version, this sets the bits instead of XORing them. If we need the XOR
* version, we could use ^=.
* Note: unlike the vere version, this sets the bits instead of XORing
* them. If we need the XOR version, we could use ^=.
*/
pub unsafe fn chop(
bloq: usize,
@ -133,15 +161,11 @@ pub mod util {
to: usize,
dest: &mut BitSlice<u64, Lsb0>,
source: &BitSlice<u64, Lsb0>,
) -> result::Result<(), ()> {
let from_b = from << bloq;
let to_b = to << bloq;
let mut step_b = step << bloq;
let end_b = from_b.checked_add(step_b).ok_or(())?;
if (from_b >> bloq) != from {
return Err(());
}
) -> result::Result<(), JetErr> {
let from_b = checked_left_shift(bloq, from)?;
let to_b = checked_left_shift(bloq, to)?;
let mut step_b = checked_left_shift(bloq, step)?;
let end_b = checked_add(from_b, step_b)?;
if from_b >= source.len() {
return Ok(());
@ -155,7 +179,7 @@ pub mod util {
Ok(())
}
/** Subtraction */
/// Subtraction
pub fn sub(stack: &mut NockStack, a: Atom, b: Atom) -> noun::Result<Atom> {
if let (Ok(a), Ok(b)) = (a.as_direct(), b.as_direct()) {
let a_small = a.data();
@ -181,7 +205,7 @@ pub mod util {
}
}
/** Binary exponent */
/// Binary exponent
pub fn bex(stack: &mut NockStack, arg: usize) -> Atom {
unsafe {
if arg < 63 {
@ -194,7 +218,7 @@ pub mod util {
}
}
/** Binary OR */
/// Binary OR
pub fn con(stack: &mut NockStack, a: Atom, b: Atom) -> Atom {
let new_size = cmp::max(a.size(), b.size());
@ -207,7 +231,7 @@ pub mod util {
}
}
/** Measure the number of bloqs in an atom */
/// Measure the number of bloqs in an atom
pub fn met(bloq: usize, a: Atom) -> usize {
if unsafe { a.as_noun().raw_equals(D(0)) } {
0

View File

@ -23,7 +23,6 @@ use either::{Left, Right};
use ibig::ops::DivRem;
use ibig::UBig;
use std::cmp;
use std::convert::TryFrom;
crate::gdb!();
@ -324,24 +323,16 @@ pub fn jet_lsh(
let (bloq, step) = bite(slot(arg, 2)?)?;
let a = slot(arg, 3)?.as_atom()?;
// TODO: need to assert step << bloq doesn't overflow?
let len = met(bloq, a);
let new_size = (a
.bit_size()
.checked_add(step << bloq)
.ok_or(NonDeterministic)?
.checked_add(63)
.ok_or(NonDeterministic)?)
>> 6;
if unsafe { a.as_noun().raw_equals(D(0)) } {
Ok(D(0))
} else {
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().as_noun())
}
if len == 0 {
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().as_noun())
}
}
@ -359,15 +350,7 @@ pub fn jet_rsh(
return Ok(D(0));
}
// TODO: need to assert step << bloq doesn't overflow?
let new_size = (a
.bit_size()
.checked_sub(step << bloq)
.ok_or(NonDeterministic)?
.checked_add(63)
.ok_or(NonDeterministic)?)
>> 6;
let new_size = bits_to_word(checked_sub(a.bit_size(), checked_left_shift(bloq, step)?)?)?;
unsafe {
let (mut atom, dest) = IndirectAtom::new_raw_mut_bitslice(stack, new_size);
chop(bloq, step, len - step, 0, dest, a.as_bitslice())?;
@ -442,14 +425,7 @@ pub fn jet_end(
Ok(a.as_noun())
} else {
unsafe {
let new_size = (a
.bit_size()
.checked_sub(step << bloq)
.ok_or(NonDeterministic)?
.checked_add(63)
.ok_or(NonDeterministic)?)
>> 6;
let (mut new_indirect, new_slice) = IndirectAtom::new_raw_mut_bitslice(stack, new_size);
let (mut new_indirect, new_slice) = IndirectAtom::new_raw_mut_bitslice(stack, bite_to_word(bloq, step)?);
chop(bloq, 0, step, 0, new_slice, a.as_bitslice())?;
Ok(new_indirect.normalize_as_atom().as_noun())
}
@ -505,9 +481,9 @@ pub fn jet_can(
let cell = list.as_cell()?;
let item = cell.head().as_cell()?;
let step = usize::try_from(item.head().as_direct()?.data()).unwrap();
let step = item.head().as_direct()?.data() as usize;
len = len.checked_add(step).ok_or(NonDeterministic)?;
len = checked_add(len, step)?;
list = cell.tail();
}
@ -515,8 +491,7 @@ pub fn jet_can(
Ok(D(0))
} else {
unsafe {
let (mut new_indirect, new_slice) =
IndirectAtom::new_raw_mut_bitslice(stack, len << bloq);
let (mut new_indirect, new_slice) = IndirectAtom::new_raw_mut_bitslice(stack, bite_to_word(bloq, len)?);
let mut pos = 0;
let mut list = original_list;
loop {
@ -556,7 +531,7 @@ pub fn jet_rep(
let cell = list.as_cell()?;
len = len.checked_add(step).ok_or(NonDeterministic)?;
len = checked_add(len, step)?;
list = cell.tail();
}
@ -564,8 +539,7 @@ pub fn jet_rep(
Ok(D(0))
} else {
unsafe {
let (mut new_indirect, new_slice) =
IndirectAtom::new_raw_mut_bitslice(stack, len << bloq);
let (mut new_indirect, new_slice) = IndirectAtom::new_raw_mut_bitslice(stack, bite_to_word(bloq, len)?);
let mut pos = 0;
let mut list = original_list;
loop {
@ -600,8 +574,7 @@ pub fn jet_cut(
let atom = slot(arg, 7)?.as_atom()?;
let new_indirect = unsafe {
let (mut new_indirect, new_slice) =
IndirectAtom::new_raw_mut_bitslice(stack, ((run << bloq) + 63) >> 6);
let (mut new_indirect, new_slice) = IndirectAtom::new_raw_mut_bitslice(stack, bite_to_word(bloq, run)?);
chop(bloq, start, run, 0, new_slice, atom.as_bitslice())?;
new_indirect.normalize_as_atom()
};