mirror of
https://github.com/urbit/ares.git
synced 2024-11-22 15:08:54 +03:00
[jets] import ubig, add "add" jet
This commit is contained in:
parent
a527ba7aaa
commit
4c1e742044
34
rust/ares/Cargo.lock
generated
34
rust/ares/Cargo.lock
generated
@ -16,6 +16,7 @@ dependencies = [
|
||||
"bitvec",
|
||||
"criterion",
|
||||
"either",
|
||||
"ibig",
|
||||
"intmap",
|
||||
"libc",
|
||||
"memmap",
|
||||
@ -254,6 +255,18 @@ dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ibig"
|
||||
version = "0.3.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d1fcc7f316b2c079dde77564a1360639c1a956a23fa96122732e416cb10717bb"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"num-traits",
|
||||
"rand",
|
||||
"static_assertions",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "1.9.2"
|
||||
@ -439,6 +452,21 @@ version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09"
|
||||
|
||||
[[package]]
|
||||
name = "rand"
|
||||
version = "0.8.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
|
||||
dependencies = [
|
||||
"rand_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_core"
|
||||
version = "0.6.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
|
||||
|
||||
[[package]]
|
||||
name = "rayon"
|
||||
version = "1.6.1"
|
||||
@ -528,6 +556,12 @@ dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "static_assertions"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.98"
|
||||
|
@ -17,6 +17,7 @@ intmap = "1.1.0"
|
||||
num-traits = "0.2"
|
||||
num-derive = "0.3"
|
||||
criterion = "0.4"
|
||||
ibig = "0.3.6"
|
||||
|
||||
[[bin]]
|
||||
name = "cue_pill"
|
||||
|
@ -1,9 +1,9 @@
|
||||
use crate::interpreter::raw_slot;
|
||||
use crate::jets_math::*;
|
||||
use crate::mem::NockStack;
|
||||
use crate::mug::mug;
|
||||
use crate::noun::{DirectAtom, IndirectAtom, Noun};
|
||||
use crate::noun::{Noun};
|
||||
use ares_macros::tas;
|
||||
use either::Either::*;
|
||||
|
||||
/// Return Err if the computation crashed or should punt to Nock
|
||||
pub type Jet = fn(&mut NockStack, Noun) -> Result<Noun, JetErr>;
|
||||
@ -25,83 +25,26 @@ impl From<JetErr> for () {
|
||||
fn from(_: JetErr) -> Self {}
|
||||
}
|
||||
|
||||
use JetErr::*;
|
||||
|
||||
pub fn get_jet(jet_name: Noun) -> Result<Jet, ()> {
|
||||
match jet_name.as_direct()?.data() {
|
||||
tas!(b"dec") => Ok(jet_dec),
|
||||
tas!(b"add") => Ok(jet_add),
|
||||
tas!(b"cut") => Ok(jet_cut),
|
||||
tas!(b"mug") => Ok(jet_mug),
|
||||
_ => {
|
||||
// eprintln!("Unknown jet: {:?}", jet_name);
|
||||
Err(())
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_jet_test_mode(jet_name: Noun) -> bool {
|
||||
match jet_name.as_direct().unwrap().data() {
|
||||
tas!(b"dec") => true,
|
||||
tas!(b"cut") => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
fn jet_dec(stack: &mut NockStack, subject: Noun) -> Result<Noun, JetErr> {
|
||||
let arg = raw_slot(subject, 6);
|
||||
if let Ok(atom) = arg.as_atom() {
|
||||
match atom.as_either() {
|
||||
Left(direct) => {
|
||||
if direct.data() == 0 {
|
||||
Err(Deterministic)
|
||||
} else {
|
||||
Ok(unsafe { DirectAtom::new_unchecked(direct.data() - 1) }.as_noun())
|
||||
}
|
||||
}
|
||||
Right(indirect) => {
|
||||
let indirect_slice = indirect.as_bitslice();
|
||||
match indirect_slice.first_one() {
|
||||
None => {
|
||||
panic!("Decrementing 0 stored as an indirect atom");
|
||||
}
|
||||
Some(first_one) => {
|
||||
let (mut new_indirect, new_slice) =
|
||||
unsafe { IndirectAtom::new_raw_mut_bitslice(stack, indirect.size()) };
|
||||
if first_one > 0 {
|
||||
new_slice[..first_one].fill(true);
|
||||
}
|
||||
new_slice.set(first_one, false);
|
||||
new_slice[first_one + 1..]
|
||||
.copy_from_bitslice(&indirect_slice[first_one + 1..]);
|
||||
let res = unsafe { new_indirect.normalize_as_atom() };
|
||||
Ok(res.as_noun())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Err(Deterministic)
|
||||
}
|
||||
}
|
||||
|
||||
fn jet_cut(stack: &mut NockStack, subject: Noun) -> Result<Noun, JetErr> {
|
||||
let arg = raw_slot(subject, 6);
|
||||
let bloq = raw_slot(arg, 2).as_direct()?.data();
|
||||
let start = raw_slot(arg, 12).as_direct()?.data();
|
||||
let run = raw_slot(arg, 13).as_direct()?.data();
|
||||
let atom = raw_slot(arg, 7).as_atom()?;
|
||||
let slice = atom.as_bitslice();
|
||||
let unit = 1 << bloq;
|
||||
let new_indirect = unsafe {
|
||||
let (mut new_indirect, new_slice) =
|
||||
IndirectAtom::new_raw_mut_bitslice(stack, ((run * unit + 63) >> 6) as usize);
|
||||
new_slice[..(unit * run) as usize]
|
||||
.copy_from_bitslice(&slice[(unit * start) as usize..(unit * (start + run)) as usize]);
|
||||
new_indirect.normalize_as_atom()
|
||||
};
|
||||
Ok(new_indirect.as_noun())
|
||||
}
|
||||
|
||||
fn jet_mug(stack: &mut NockStack, subject: Noun) -> Result<Noun, JetErr> {
|
||||
let arg = raw_slot(subject, 6);
|
||||
Ok(mug(stack, arg).as_noun())
|
||||
|
95
rust/ares/src/jets_math.rs
Normal file
95
rust/ares/src/jets_math.rs
Normal file
@ -0,0 +1,95 @@
|
||||
/** Math jets
|
||||
*
|
||||
* We use ibig for math operations. This is a pure rust library, and it is very convenient to use.
|
||||
* If they're noticeably, fater, we may want to use gmp or a library wrapping it, such as rug.
|
||||
*
|
||||
* In any case, it's important to ensure that the library only allocates on the nock stack. Gmp
|
||||
* has mp_set_memory_functions. I don't know if rug does any allocation on top of that. ibig does
|
||||
* not appear to support custom allocation functions, but we could probably patch it. If we're
|
||||
* patching it, we might even be able to avoid copying the input and output at all, which might
|
||||
* give a greater performance advantage than using gmp anyway.
|
||||
*
|
||||
* Another approach is use a global custom allocator. This is fairly involved, but it would allow
|
||||
* us to use any library without worrying whether it allocates.
|
||||
*/
|
||||
|
||||
use crate::interpreter::raw_slot;
|
||||
use crate::jets::{JetErr, JetErr::*};
|
||||
use crate::mem::NockStack;
|
||||
use crate::noun::{Atom, DirectAtom, IndirectAtom, Noun};
|
||||
use either::Either::*;
|
||||
|
||||
pub fn jet_dec(stack: &mut NockStack, subject: Noun) -> Result<Noun, JetErr> {
|
||||
let arg = raw_slot(subject, 6);
|
||||
if let Ok(atom) = arg.as_atom() {
|
||||
match atom.as_either() {
|
||||
Left(direct) => {
|
||||
if direct.data() == 0 {
|
||||
Err(Deterministic)
|
||||
} else {
|
||||
Ok(unsafe { DirectAtom::new_unchecked(direct.data() - 1) }.as_noun())
|
||||
}
|
||||
}
|
||||
Right(indirect) => {
|
||||
let indirect_slice = indirect.as_bitslice();
|
||||
match indirect_slice.first_one() {
|
||||
None => {
|
||||
panic!("Decrementing 0 stored as an indirect atom");
|
||||
}
|
||||
Some(first_one) => {
|
||||
let (mut new_indirect, new_slice) =
|
||||
unsafe { IndirectAtom::new_raw_mut_bitslice(stack, indirect.size()) };
|
||||
if first_one > 0 {
|
||||
new_slice[..first_one].fill(true);
|
||||
}
|
||||
new_slice.set(first_one, false);
|
||||
new_slice[first_one + 1..]
|
||||
.copy_from_bitslice(&indirect_slice[first_one + 1..]);
|
||||
let res = unsafe { new_indirect.normalize_as_atom() };
|
||||
Ok(res.as_noun())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Err(Deterministic)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn jet_cut(stack: &mut NockStack, subject: Noun) -> Result<Noun, JetErr> {
|
||||
let arg = raw_slot(subject, 6);
|
||||
let bloq = raw_slot(arg, 2).as_direct()?.data();
|
||||
let start = raw_slot(arg, 12).as_direct()?.data();
|
||||
let run = raw_slot(arg, 13).as_direct()?.data();
|
||||
let atom = raw_slot(arg, 7).as_atom()?;
|
||||
let slice = atom.as_bitslice();
|
||||
let unit = 1 << bloq;
|
||||
let new_indirect = unsafe {
|
||||
let (mut new_indirect, new_slice) =
|
||||
IndirectAtom::new_raw_mut_bitslice(stack, ((run * unit + 63) >> 6) as usize);
|
||||
new_slice[..(unit * run) as usize]
|
||||
.copy_from_bitslice(&slice[(unit * start) as usize..(unit * (start + run)) as usize]);
|
||||
new_indirect.normalize_as_atom()
|
||||
};
|
||||
Ok(new_indirect.as_noun())
|
||||
}
|
||||
|
||||
pub fn jet_add(stack: &mut NockStack, subject: Noun) -> Result<Noun, JetErr> {
|
||||
eprintln!("\radd");
|
||||
let arg = raw_slot(subject, 6);
|
||||
let a = raw_slot(arg, 2).as_atom()?;
|
||||
let b = raw_slot(arg, 3).as_atom()?;
|
||||
|
||||
let res = match (a.as_direct(), b.as_direct()) {
|
||||
(Ok(a), Ok(b)) => {
|
||||
Atom::new(stack, a.data() + b.data())
|
||||
}
|
||||
(_, _) => {
|
||||
let a_int = a.as_ubig();
|
||||
let b_int = b.as_ubig();
|
||||
let res = a_int + b_int;
|
||||
Atom::from_ubig(stack, &res)
|
||||
}
|
||||
};
|
||||
Ok(res.as_noun())
|
||||
}
|
@ -2,6 +2,7 @@
|
||||
extern crate num_derive;
|
||||
pub mod interpreter;
|
||||
pub mod jets;
|
||||
pub mod jets_math;
|
||||
pub mod mem;
|
||||
pub mod mug;
|
||||
pub mod newt;
|
||||
|
@ -4,6 +4,7 @@ use intmap::IntMap;
|
||||
use std::fmt;
|
||||
use std::ptr;
|
||||
use std::slice::{from_raw_parts, from_raw_parts_mut};
|
||||
use ibig::UBig;
|
||||
|
||||
/** Tag for a direct atom. */
|
||||
const DIRECT_TAG: u64 = 0x0;
|
||||
@ -131,6 +132,10 @@ impl DirectAtom {
|
||||
Atom { direct: self }
|
||||
}
|
||||
|
||||
pub fn as_ubig(self) -> UBig {
|
||||
UBig::from(self.0)
|
||||
}
|
||||
|
||||
pub const fn as_noun(self) -> Noun {
|
||||
Noun { direct: self }
|
||||
}
|
||||
@ -210,7 +215,7 @@ impl IndirectAtom {
|
||||
|
||||
/** Make an indirect atom by copying from other memory.
|
||||
*
|
||||
* The size is specified in 64 bit words, not in bytes.
|
||||
* Note: size is in 64-bit words, not bytes.
|
||||
*/
|
||||
pub unsafe fn new_raw(
|
||||
allocator: &mut dyn NounAllocator,
|
||||
@ -222,6 +227,20 @@ impl IndirectAtom {
|
||||
*(indirect.normalize())
|
||||
}
|
||||
|
||||
/** Make an indirect atom by copying from other memory.
|
||||
*
|
||||
* Note: size is bytes, not words
|
||||
*/
|
||||
pub unsafe fn new_raw_bytes(
|
||||
allocator: &mut dyn NounAllocator,
|
||||
size: usize,
|
||||
data: *const u8,
|
||||
) -> Self {
|
||||
let (mut indirect, buffer) = Self::new_raw_mut_bytes(allocator, size);
|
||||
ptr::copy_nonoverlapping(data, buffer.as_mut_ptr(), size);
|
||||
*(indirect.normalize())
|
||||
}
|
||||
|
||||
/** Make an indirect atom that can be written into. Return the atom (which should not be used
|
||||
* until it is written and normalized) and a mutable pointer which is the data buffer for the
|
||||
* indirect atom, to be written into.
|
||||
@ -270,7 +289,7 @@ impl IndirectAtom {
|
||||
allocator: &mut dyn NounAllocator,
|
||||
size: usize,
|
||||
) -> (Self, &'a mut [u8]) {
|
||||
let word_size = (size + 7) << 3;
|
||||
let word_size = (size + 7) >> 3;
|
||||
let (noun, ptr) = Self::new_raw_mut_zeroed(allocator, word_size);
|
||||
(noun, from_raw_parts_mut(ptr as *mut u8, size))
|
||||
}
|
||||
@ -298,6 +317,10 @@ impl IndirectAtom {
|
||||
BitSlice::from_slice(self.as_slice())
|
||||
}
|
||||
|
||||
pub fn as_ubig(self) -> UBig {
|
||||
UBig::from_le_bytes(self.as_bytes())
|
||||
}
|
||||
|
||||
/** Ensure that the size does not contain any trailing 0 words */
|
||||
pub unsafe fn normalize(&mut self) -> &Self {
|
||||
let mut index = self.size() - 1;
|
||||
@ -497,6 +520,29 @@ impl Atom {
|
||||
unsafe { IndirectAtom::new_raw(allocator, 1, &value).as_atom() }
|
||||
}
|
||||
}
|
||||
|
||||
// to_le_bytes and new_raw are copies. We should be able to do this completely without copies
|
||||
// if we integrate with ibig properly.
|
||||
pub fn from_ubig(allocator: &mut dyn NounAllocator, big: &UBig) -> Atom {
|
||||
let bit_size = big.bit_len();
|
||||
let buffer = big.to_le_bytes();
|
||||
if bit_size < 64 {
|
||||
let value: u64 = if bit_size == 0 { 0 } else {
|
||||
buffer[0] as u64
|
||||
| if bit_size <= 8 { 0 } else { (buffer[1] as u64) << 8
|
||||
| if bit_size <= 16 { 0 } else { (buffer[2] as u64) << 16
|
||||
| if bit_size <= 24 { 0 } else { (buffer[3] as u64) << 24
|
||||
| if bit_size <= 32 { 0 } else { (buffer[4] as u64) << 32
|
||||
| if bit_size <= 40 { 0 } else { (buffer[5] as u64) << 40
|
||||
| if bit_size <= 48 { 0 } else { (buffer[6] as u64) << 48
|
||||
| if bit_size <= 56 { 0 } else { (buffer[7] as u64) << 56 } } } } } } } };
|
||||
unsafe { DirectAtom::new_unchecked(value).as_atom() }
|
||||
} else {
|
||||
let byte_size = (big.bit_len() + 7) >> 3;
|
||||
unsafe { IndirectAtom::new_raw_bytes(allocator, byte_size, buffer.as_ptr()).as_atom() }
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_direct(&self) -> bool {
|
||||
unsafe { is_direct_atom(self.raw) }
|
||||
}
|
||||
@ -537,6 +583,14 @@ impl Atom {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_ubig(&self) -> UBig {
|
||||
if self.is_indirect() {
|
||||
unsafe { self.indirect.as_ubig() }
|
||||
} else {
|
||||
unsafe { self.direct.as_ubig() }
|
||||
}
|
||||
}
|
||||
|
||||
pub fn size(&self) -> usize {
|
||||
match self.as_either() {
|
||||
Either::Left(_direct) => 1,
|
||||
|
Loading…
Reference in New Issue
Block a user