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",
|
"bitvec",
|
||||||
"criterion",
|
"criterion",
|
||||||
"either",
|
"either",
|
||||||
|
"ibig",
|
||||||
"intmap",
|
"intmap",
|
||||||
"libc",
|
"libc",
|
||||||
"memmap",
|
"memmap",
|
||||||
@ -254,6 +255,18 @@ dependencies = [
|
|||||||
"libc",
|
"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]]
|
[[package]]
|
||||||
name = "indexmap"
|
name = "indexmap"
|
||||||
version = "1.9.2"
|
version = "1.9.2"
|
||||||
@ -439,6 +452,21 @@ version = "0.7.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09"
|
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]]
|
[[package]]
|
||||||
name = "rayon"
|
name = "rayon"
|
||||||
version = "1.6.1"
|
version = "1.6.1"
|
||||||
@ -528,6 +556,12 @@ dependencies = [
|
|||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "static_assertions"
|
||||||
|
version = "1.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "syn"
|
name = "syn"
|
||||||
version = "1.0.98"
|
version = "1.0.98"
|
||||||
|
@ -17,6 +17,7 @@ intmap = "1.1.0"
|
|||||||
num-traits = "0.2"
|
num-traits = "0.2"
|
||||||
num-derive = "0.3"
|
num-derive = "0.3"
|
||||||
criterion = "0.4"
|
criterion = "0.4"
|
||||||
|
ibig = "0.3.6"
|
||||||
|
|
||||||
[[bin]]
|
[[bin]]
|
||||||
name = "cue_pill"
|
name = "cue_pill"
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
use crate::interpreter::raw_slot;
|
use crate::interpreter::raw_slot;
|
||||||
|
use crate::jets_math::*;
|
||||||
use crate::mem::NockStack;
|
use crate::mem::NockStack;
|
||||||
use crate::mug::mug;
|
use crate::mug::mug;
|
||||||
use crate::noun::{DirectAtom, IndirectAtom, Noun};
|
use crate::noun::{Noun};
|
||||||
use ares_macros::tas;
|
use ares_macros::tas;
|
||||||
use either::Either::*;
|
|
||||||
|
|
||||||
/// Return Err if the computation crashed or should punt to Nock
|
/// Return Err if the computation crashed or should punt to Nock
|
||||||
pub type Jet = fn(&mut NockStack, Noun) -> Result<Noun, JetErr>;
|
pub type Jet = fn(&mut NockStack, Noun) -> Result<Noun, JetErr>;
|
||||||
@ -25,83 +25,26 @@ impl From<JetErr> for () {
|
|||||||
fn from(_: JetErr) -> Self {}
|
fn from(_: JetErr) -> Self {}
|
||||||
}
|
}
|
||||||
|
|
||||||
use JetErr::*;
|
|
||||||
|
|
||||||
pub fn get_jet(jet_name: Noun) -> Result<Jet, ()> {
|
pub fn get_jet(jet_name: Noun) -> Result<Jet, ()> {
|
||||||
match jet_name.as_direct()?.data() {
|
match jet_name.as_direct()?.data() {
|
||||||
tas!(b"dec") => Ok(jet_dec),
|
tas!(b"dec") => Ok(jet_dec),
|
||||||
|
tas!(b"add") => Ok(jet_add),
|
||||||
tas!(b"cut") => Ok(jet_cut),
|
tas!(b"cut") => Ok(jet_cut),
|
||||||
tas!(b"mug") => Ok(jet_mug),
|
tas!(b"mug") => Ok(jet_mug),
|
||||||
_ => {
|
_ => {
|
||||||
// eprintln!("Unknown jet: {:?}", jet_name);
|
// eprintln!("Unknown jet: {:?}", jet_name);
|
||||||
Err(())
|
Err(())
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_jet_test_mode(jet_name: Noun) -> bool {
|
pub fn get_jet_test_mode(jet_name: Noun) -> bool {
|
||||||
match jet_name.as_direct().unwrap().data() {
|
match jet_name.as_direct().unwrap().data() {
|
||||||
tas!(b"dec") => true,
|
|
||||||
tas!(b"cut") => true,
|
tas!(b"cut") => true,
|
||||||
_ => false,
|
_ => 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> {
|
fn jet_mug(stack: &mut NockStack, subject: Noun) -> Result<Noun, JetErr> {
|
||||||
let arg = raw_slot(subject, 6);
|
let arg = raw_slot(subject, 6);
|
||||||
Ok(mug(stack, arg).as_noun())
|
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;
|
extern crate num_derive;
|
||||||
pub mod interpreter;
|
pub mod interpreter;
|
||||||
pub mod jets;
|
pub mod jets;
|
||||||
|
pub mod jets_math;
|
||||||
pub mod mem;
|
pub mod mem;
|
||||||
pub mod mug;
|
pub mod mug;
|
||||||
pub mod newt;
|
pub mod newt;
|
||||||
|
@ -4,6 +4,7 @@ use intmap::IntMap;
|
|||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::ptr;
|
use std::ptr;
|
||||||
use std::slice::{from_raw_parts, from_raw_parts_mut};
|
use std::slice::{from_raw_parts, from_raw_parts_mut};
|
||||||
|
use ibig::UBig;
|
||||||
|
|
||||||
/** Tag for a direct atom. */
|
/** Tag for a direct atom. */
|
||||||
const DIRECT_TAG: u64 = 0x0;
|
const DIRECT_TAG: u64 = 0x0;
|
||||||
@ -131,6 +132,10 @@ impl DirectAtom {
|
|||||||
Atom { direct: self }
|
Atom { direct: self }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn as_ubig(self) -> UBig {
|
||||||
|
UBig::from(self.0)
|
||||||
|
}
|
||||||
|
|
||||||
pub const fn as_noun(self) -> Noun {
|
pub const fn as_noun(self) -> Noun {
|
||||||
Noun { direct: self }
|
Noun { direct: self }
|
||||||
}
|
}
|
||||||
@ -210,7 +215,7 @@ impl IndirectAtom {
|
|||||||
|
|
||||||
/** Make an indirect atom by copying from other memory.
|
/** 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(
|
pub unsafe fn new_raw(
|
||||||
allocator: &mut dyn NounAllocator,
|
allocator: &mut dyn NounAllocator,
|
||||||
@ -222,6 +227,20 @@ impl IndirectAtom {
|
|||||||
*(indirect.normalize())
|
*(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
|
/** 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
|
* until it is written and normalized) and a mutable pointer which is the data buffer for the
|
||||||
* indirect atom, to be written into.
|
* indirect atom, to be written into.
|
||||||
@ -270,7 +289,7 @@ impl IndirectAtom {
|
|||||||
allocator: &mut dyn NounAllocator,
|
allocator: &mut dyn NounAllocator,
|
||||||
size: usize,
|
size: usize,
|
||||||
) -> (Self, &'a mut [u8]) {
|
) -> (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);
|
let (noun, ptr) = Self::new_raw_mut_zeroed(allocator, word_size);
|
||||||
(noun, from_raw_parts_mut(ptr as *mut u8, size))
|
(noun, from_raw_parts_mut(ptr as *mut u8, size))
|
||||||
}
|
}
|
||||||
@ -298,6 +317,10 @@ impl IndirectAtom {
|
|||||||
BitSlice::from_slice(self.as_slice())
|
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 */
|
/** Ensure that the size does not contain any trailing 0 words */
|
||||||
pub unsafe fn normalize(&mut self) -> &Self {
|
pub unsafe fn normalize(&mut self) -> &Self {
|
||||||
let mut index = self.size() - 1;
|
let mut index = self.size() - 1;
|
||||||
@ -497,6 +520,29 @@ impl Atom {
|
|||||||
unsafe { IndirectAtom::new_raw(allocator, 1, &value).as_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 {
|
pub fn is_direct(&self) -> bool {
|
||||||
unsafe { is_direct_atom(self.raw) }
|
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 {
|
pub fn size(&self) -> usize {
|
||||||
match self.as_either() {
|
match self.as_either() {
|
||||||
Either::Left(_direct) => 1,
|
Either::Left(_direct) => 1,
|
||||||
|
Loading…
Reference in New Issue
Block a user