From 795ef7a0d1cf5c62f7213ee4e02e000bfac44b7f Mon Sep 17 00:00:00 2001 From: Chris Allen Date: Mon, 28 Oct 2024 18:21:13 -0500 Subject: [PATCH] alloc_would_oom, guarding alloc now --- rust/ibig/src/add_ops.rs | 18 +- rust/ibig/src/buffer.rs | 19 +- rust/ibig/src/convert.rs | 24 +-- rust/ibig/src/div_ops.rs | 51 ++--- rust/ibig/src/memory.rs | 14 +- rust/ibig/src/mul_ops.rs | 21 ++- rust/sword/src/flog.rs | 17 +- rust/sword/src/hamt.rs | 81 ++++---- rust/sword/src/interpreter.rs | 270 +++++++++++++++----------- rust/sword/src/jets.rs | 82 ++++---- rust/sword/src/jets/bits.rs | 132 ++++++------- rust/sword/src/jets/cold.rs | 323 ++++++++++++++++--------------- rust/sword/src/jets/form.rs | 24 +-- rust/sword/src/jets/hash.rs | 10 +- rust/sword/src/jets/hot.rs | 15 +- rust/sword/src/jets/list.rs | 46 ++--- rust/sword/src/jets/lock/aes.rs | 203 ++++++++++---------- rust/sword/src/jets/lock/ed.rs | 68 ++++--- rust/sword/src/jets/lock/sha.rs | 126 ++++++------- rust/sword/src/jets/lute.rs | 42 ++--- rust/sword/src/jets/math.rs | 191 ++++++++++--------- rust/sword/src/jets/nock.rs | 130 ++++++------- rust/sword/src/jets/parse.rs | 128 ++++++------- rust/sword/src/jets/serial.rs | 15 +- rust/sword/src/jets/sort.rs | 41 ++-- rust/sword/src/jets/tree.rs | 18 +- rust/sword/src/jets/warm.rs | 21 ++- rust/sword/src/lib.rs | 19 +- rust/sword/src/mem.rs | 324 +++++++++++++++++++++++++++----- rust/sword/src/newt.rs | 86 +++++---- rust/sword/src/noun.rs | 123 ++++++------ rust/sword/src/serf.rs | 106 ++++++----- rust/sword/src/serialization.rs | 123 ++++++------ rust/sword/src/site.rs | 2 +- rust/sword/src/test_fns.rs | 11 ++ rust/sword/src/trace.rs | 10 +- 36 files changed, 1682 insertions(+), 1252 deletions(-) create mode 100644 rust/sword/src/test_fns.rs diff --git a/rust/ibig/src/add_ops.rs b/rust/ibig/src/add_ops.rs index da98bed..8cf4827 100644 --- a/rust/ibig/src/add_ops.rs +++ b/rust/ibig/src/add_ops.rs @@ -531,9 +531,9 @@ impl UBig { // given at least 2 words of extra capacity. However, this supports UBigs which have already // been expanded through other operations. #[inline] - pub fn add_stack(stack: &mut S, lhs: UBig, rhs: UBig) -> UBig { - match (lhs.into_repr(), rhs.into_repr()) { - (Small(word0), Small(word1)) => UBig::add_word_stack(stack, word0, word1), + pub fn add_stack(stack: &mut S, lhs: UBig, rhs: UBig) -> Result { + let ubig = match (lhs.into_repr(), rhs.into_repr()) { + (Small(word0), Small(word1)) => UBig::add_word_stack(stack, word0, word1)?, (Small(word0), Large(buffer1)) => UBig::add_large_word_stack(stack, buffer1, word0), (Large(buffer0), Small(word1)) => UBig::add_large_word_stack(stack, buffer0, word1), (Large(buffer0), Large(buffer1)) => { @@ -543,21 +543,23 @@ impl UBig { UBig::add_large_stack(stack, buffer1, &buffer0) } } - } + }; + Ok(ubig) } /// Add two `Word`s. #[inline] - fn add_word_stack(stack: &mut S, a: Word, b: Word) -> UBig { + fn add_word_stack(stack: &mut S, a: Word, b: Word) -> Result { let (res, overflow) = a.overflowing_add(b); - if overflow { - let mut buffer = Buffer::allocate_stack(stack, 2); + let ubig = if overflow { + let mut buffer = Buffer::allocate_stack(stack, 2)?; buffer.push(res); buffer.push(1); buffer.into() } else { UBig::from_word(res) - } + }; + Ok(ubig) } /// Add a large number to a `Word`. diff --git a/rust/ibig/src/buffer.rs b/rust/ibig/src/buffer.rs index cd39913..9ee177e 100644 --- a/rust/ibig/src/buffer.rs +++ b/rust/ibig/src/buffer.rs @@ -21,14 +21,14 @@ use core::{ pub(crate) struct Buffer(ManuallyDrop>); impl Buffer { - pub(crate) fn allocate_stack(stack: &mut S, num_words: usize) -> Buffer { + pub(crate) fn allocate_stack(stack: &mut S, num_words: usize) -> Result { if num_words > Buffer::MAX_CAPACITY { UBig::panic_number_too_large(); } let capacity = Buffer::default_capacity(num_words); unsafe { - let ptr = stack.alloc_layout(memory::array_layout::(capacity)); - Buffer(ManuallyDrop::new(Vec::from_raw_parts(ptr, 0, capacity))) + let ptr = stack.alloc_layout(memory::array_layout::(capacity))?; + Ok(Buffer(ManuallyDrop::new(Vec::from_raw_parts(ptr, 0, capacity)))) } } @@ -44,9 +44,11 @@ impl Buffer { ))) } - pub(crate) fn ensure_capacity_stack(&mut self, stack: &mut S, num_words: usize) { + pub(crate) fn ensure_capacity_stack(&mut self, stack: &mut S, num_words: usize) -> Result<(), S::AllocError> { if num_words > self.capacity() { - self.reallocate_stack(stack, num_words); + self.reallocate_stack(stack, num_words) + } else { + Ok(()) } } @@ -69,11 +71,12 @@ impl Buffer { // } } - fn reallocate_stack(&mut self, stack: &mut S, num_words: usize) { + fn reallocate_stack(&mut self, stack: &mut S, num_words: usize) -> Result<(), S::AllocError> { assert!(num_words >= self.len()); - let mut new_buffer = Buffer::allocate_stack(stack, num_words); + let mut new_buffer = Buffer::allocate_stack(stack, num_words)?; new_buffer.clone_from(self); - *self = new_buffer + *self = new_buffer; + Ok(()) } /// Change capacity to store `num_words` plus some extra space for future growth. diff --git a/rust/ibig/src/convert.rs b/rust/ibig/src/convert.rs index 87b6aff..66095b0 100644 --- a/rust/ibig/src/convert.rs +++ b/rust/ibig/src/convert.rs @@ -31,18 +31,19 @@ impl Default for IBig { impl UBig { #[inline] - pub fn from_le_bytes_stack(stack: &mut S, bytes: &[u8]) -> UBig { - if bytes.len() <= WORD_BYTES { + pub fn from_le_bytes_stack(stack: &mut S, bytes: &[u8]) -> Result { + let ubig = if bytes.len() <= WORD_BYTES { // fast path UBig::from_word(primitive::word_from_le_bytes_partial(bytes)) } else { - UBig::from_le_bytes_large_stack(stack, bytes) - } + UBig::from_le_bytes_large_stack(stack, bytes)? + }; + Ok(ubig) } - fn from_le_bytes_large_stack(stack: &mut S, bytes: &[u8]) -> UBig { + fn from_le_bytes_large_stack(stack: &mut S, bytes: &[u8]) -> Result { debug_assert!(bytes.len() > WORD_BYTES); - let mut buffer = Buffer::allocate_stack(stack, (bytes.len() - 1) / WORD_BYTES + 1); + let mut buffer = Buffer::allocate_stack(stack, (bytes.len() - 1) / WORD_BYTES + 1)?; let mut chunks = bytes.chunks_exact(WORD_BYTES); for chunk in &mut chunks { buffer.push(Word::from_le_bytes(chunk.try_into().unwrap())); @@ -50,7 +51,7 @@ impl UBig { if !chunks.remainder().is_empty() { buffer.push(primitive::word_from_le_bytes_partial(chunks.remainder())); } - buffer.into() + Ok(buffer.into()) } /// Construct from little-endian bytes. @@ -552,17 +553,18 @@ impl TryFrom<&IBig> for UBig { impl UBig { #[inline] - pub(crate) fn from_unsigned_stack(stack: &mut S, x: T) -> UBig + pub(crate) fn from_unsigned_stack(stack: &mut S, x: T) -> Result where T: PrimitiveUnsigned, { - match x.try_into() { + let ubig = match x.try_into() { Ok(w) => UBig::from_word(w), Err(_) => { let repr = x.to_le_bytes(); - UBig::from_le_bytes_stack(stack, repr.as_ref()) + UBig::from_le_bytes_stack(stack, repr.as_ref())? } - } + }; + Ok(ubig) } /// Convert an unsigned primitive to [UBig]. diff --git a/rust/ibig/src/div_ops.rs b/rust/ibig/src/div_ops.rs index 1448a6a..a7c1495 100644 --- a/rust/ibig/src/div_ops.rs +++ b/rust/ibig/src/div_ops.rs @@ -1279,68 +1279,71 @@ impl_div_ibig_signed!(isize); impl UBig { #[inline] - pub fn div_stack(stack: &mut S, lhs: UBig, rhs: UBig) -> UBig { - match (lhs.into_repr(), rhs.into_repr()) { + pub fn div_stack(stack: &mut S, lhs: UBig, rhs: UBig) -> Result { + let ubig_tuple = match (lhs.into_repr(), rhs.into_repr()) { (Small(word0), Small(word1)) => UBig::div_word(word0, word1), (Small(_), Large(_)) => UBig::from_word(0), (Large(buffer0), Small(word1)) => UBig::div_large_word(buffer0, word1), (Large(buffer0), Large(buffer1)) => { if buffer0.len() >= buffer1.len() { - UBig::div_large_stack(stack, buffer0, buffer1) + UBig::div_large_stack(stack, buffer0, buffer1)? } else { UBig::from_word(0) } } - } + }; + Ok(ubig_tuple) } #[inline] - pub fn rem_stack(stack: &mut S, lhs: UBig, rhs: UBig) -> UBig { - match (lhs.into_repr(), rhs.into_repr()) { + pub fn rem_stack(stack: &mut S, lhs: UBig, rhs: UBig) -> Result { + let ubig_tuple = match (lhs.into_repr(), rhs.into_repr()) { (Small(word0), Small(word1)) => UBig::rem_word(word0, word1), (Small(word0), Large(_)) => UBig::from_word(word0), (Large(buffer0), Small(word1)) => UBig::rem_large_word(&buffer0, word1), (Large(buffer0), Large(buffer1)) => { if buffer0.len() >= buffer1.len() { - UBig::rem_large_stack(stack, buffer0, buffer1) + UBig::rem_large_stack(stack, buffer0, buffer1)? } else { buffer0.into() } } - } + }; + Ok(ubig_tuple) } #[inline] - pub fn div_rem_stack(stack: &mut S, lhs: UBig, rhs: UBig) -> (UBig, UBig) { - match (lhs.into_repr(), rhs.into_repr()) { + pub fn div_rem_stack(stack: &mut S, lhs: UBig, rhs: UBig) -> Result<(UBig, UBig), S::AllocError> { + let ubig_tuple = match (lhs.into_repr(), rhs.into_repr()) { (Small(word0), Small(word1)) => UBig::div_rem_word(word0, word1), (Small(word0), Large(_)) => (UBig::from_word(0), UBig::from_word(word0)), (Large(buffer0), Small(word1)) => UBig::div_rem_large_word(buffer0, word1), (Large(buffer0), Large(buffer1)) => { if buffer0.len() >= buffer1.len() { - UBig::div_rem_large_stack(stack, buffer0, buffer1) + UBig::div_rem_large_stack(stack, buffer0, buffer1)? } else { (UBig::from_word(0), buffer0.into()) } } - } + }; + Ok(ubig_tuple) } /// `lhs / rhs` - fn div_large_stack(stack: &mut S, mut lhs: Buffer, mut rhs: Buffer) -> UBig { - let _shift = UBig::div_rem_in_lhs_stack(stack, &mut lhs, &mut rhs); + fn div_large_stack(stack: &mut S, mut lhs: Buffer, mut rhs: Buffer) -> Result { + let _shift = UBig::div_rem_in_lhs_stack(stack, &mut lhs, &mut rhs)?; lhs.erase_front(rhs.len()); - lhs.into() + Ok(lhs.into()) } /// `lhs % rhs` - fn rem_large_stack(stack: &mut S, mut lhs: Buffer, mut rhs: Buffer) -> UBig { - let shift = UBig::div_rem_in_lhs_stack(stack, &mut lhs, &mut rhs); + fn rem_large_stack(stack: &mut S, mut lhs: Buffer, mut rhs: Buffer) -> Result { + let shift = UBig::div_rem_in_lhs_stack(stack, &mut lhs, &mut rhs)?; let n = rhs.len(); rhs.copy_from_slice(&lhs[..n]); let low_bits = shift::shr_in_place(&mut rhs, shift); debug_assert!(low_bits == 0); - rhs.into() + Ok(rhs.into()) } /// `(lhs / rhs, lhs % rhs)` @@ -1348,33 +1351,33 @@ impl UBig { stack: &mut S, mut lhs: Buffer, mut rhs: Buffer, - ) -> (UBig, UBig) { - let shift = UBig::div_rem_in_lhs_stack(stack, &mut lhs, &mut rhs); + ) -> Result<(UBig, UBig), S::AllocError> { + let shift = UBig::div_rem_in_lhs_stack(stack, &mut lhs, &mut rhs)?; let n = rhs.len(); rhs.copy_from_slice(&lhs[..n]); let low_bits = shift::shr_in_place(&mut rhs, shift); debug_assert!(low_bits == 0); lhs.erase_front(n); - (lhs.into(), rhs.into()) + Ok((lhs.into(), rhs.into())) } /// lhs = (lhs / rhs, lhs % rhs) /// /// Returns shift. - fn div_rem_in_lhs_stack(stack: &mut S, lhs: &mut Buffer, rhs: &mut Buffer) -> u32 { + fn div_rem_in_lhs_stack(stack: &mut S, lhs: &mut Buffer, rhs: &mut Buffer) -> Result { let (shift, fast_div_rhs_top) = div::normalize_large(rhs); let lhs_carry = shift::shl_in_place(lhs, shift); if lhs_carry != 0 { lhs.push_may_reallocate_stack(stack, lhs_carry); } let mut allocation = - MemoryAllocation::new_stack(stack, div::memory_requirement_exact(lhs.len(), rhs.len())); + MemoryAllocation::new_stack(stack, div::memory_requirement_exact(lhs.len(), rhs.len()))?; let mut memory = allocation.memory(); let overflow = div::div_rem_in_place(lhs, rhs, fast_div_rhs_top, &mut memory); if overflow { lhs.push_may_reallocate(1); } - shift + Ok(shift) } /// `lhs / rhs` diff --git a/rust/ibig/src/memory.rs b/rust/ibig/src/memory.rs index bb67fa8..4d01f7f 100644 --- a/rust/ibig/src/memory.rs +++ b/rust/ibig/src/memory.rs @@ -10,7 +10,10 @@ pub(crate) struct MemoryAllocation { } pub trait Stack: Sized { - unsafe fn alloc_layout(&mut self, layout: Layout) -> *mut u64; + // type AllocError: Debug; + // no-std bites me in the keister again + type AllocError; + unsafe fn alloc_layout(&mut self, layout: Layout) -> Result<*mut u64, Self::AllocError>; } /// Chunk of memory. @@ -24,7 +27,7 @@ pub(crate) struct Memory<'a> { } impl MemoryAllocation { - pub(crate) fn new_stack(stack: &mut S, layout: Layout) -> MemoryAllocation { + pub(crate) fn new_stack(stack: &mut S, layout: Layout) -> Result { let start = if layout.size() == 0 { // We should use layout.dangling(), but that is unstable. layout.align() as *mut u8 @@ -32,14 +35,17 @@ impl MemoryAllocation { panic_out_of_memory() } else { // Safe because size is non-zero. - let ptr = unsafe { stack.alloc_layout(layout) as *mut u8 }; + let ptr = unsafe { + let ep = stack.alloc_layout(layout)?; + ep as *mut u8 + }; if ptr.is_null() { panic_out_of_memory(); } ptr }; - MemoryAllocation { layout, start } + Ok(MemoryAllocation { layout, start }) } /// Allocate memory. diff --git a/rust/ibig/src/mul_ops.rs b/rust/ibig/src/mul_ops.rs index 9ff961c..6cb6348 100644 --- a/rust/ibig/src/mul_ops.rs +++ b/rust/ibig/src/mul_ops.rs @@ -300,17 +300,18 @@ impl_mul_ibig_primitive!(isize); impl UBig { #[inline] - pub fn mul_stack(stack: &mut S, lhs: UBig, rhs: UBig) -> UBig { - match (lhs.into_repr(), rhs.into_repr()) { - (Small(word0), Small(word1)) => UBig::mul_word_stack(stack, word0, word1), + pub fn mul_stack(stack: &mut S, lhs: UBig, rhs: UBig) -> Result { + let res = match (lhs.into_repr(), rhs.into_repr()) { + (Small(word0), Small(word1)) => UBig::mul_word_stack(stack, word0, word1)?, (Small(word0), Large(buffer1)) => UBig::mul_large_word_stack(stack, buffer1, word0), (Large(buffer0), Small(word1)) => UBig::mul_large_word_stack(stack, buffer0, word1), - (Large(buffer0), Large(buffer1)) => UBig::mul_large_stack(stack, &buffer0, &buffer1), - } + (Large(buffer0), Large(buffer1)) => UBig::mul_large_stack(stack, &buffer0, &buffer1)?, + }; + Ok(res) } #[inline] - fn mul_word_stack(stack: &mut S, a: Word, b: Word) -> UBig { + fn mul_word_stack(stack: &mut S, a: Word, b: Word) -> Result { UBig::from_unsigned_stack(stack, extend_word(a) * extend_word(b)) } @@ -328,23 +329,23 @@ impl UBig { } } - pub fn mul_large_stack(stack: &mut S, lhs: &[Word], rhs: &[Word]) -> UBig { + pub fn mul_large_stack(stack: &mut S, lhs: &[Word], rhs: &[Word]) -> Result { debug_assert!(lhs.len() >= 2 && rhs.len() >= 2); // This may be 1 too large. const_assert!(Buffer::MAX_CAPACITY - UBig::MAX_LEN >= 1); let res_len = lhs.len() + rhs.len(); - let mut buffer = Buffer::allocate_stack(stack, res_len); + let mut buffer = Buffer::allocate_stack(stack, res_len)?; buffer.push_zeros(res_len); let mut allocation = MemoryAllocation::new_stack( stack, mul::memory_requirement_exact(res_len, lhs.len().min(rhs.len())), - ); + )?; let mut memory = allocation.memory(); let overflow = mul::add_signed_mul(&mut buffer, Positive, lhs, rhs, &mut memory); assert!(overflow == 0); - buffer.into() + Ok(buffer.into()) } /// Multiply two `Word`s. diff --git a/rust/sword/src/flog.rs b/rust/sword/src/flog.rs index 893af6f..9b7c738 100644 --- a/rust/sword/src/flog.rs +++ b/rust/sword/src/flog.rs @@ -1,5 +1,5 @@ use crate::interpreter::Context; -use crate::mem::NockStack; +use crate::mem::{AllocResult, NockStack}; use crate::noun::{Atom, IndirectAtom}; use std::fmt::Arguments; use std::io::{Result, Write}; @@ -14,26 +14,27 @@ struct NockWriter<'s, 'b> { const INITIAL_CAPACITY_BYTES: usize = 256; impl<'s, 'b> NockWriter<'s, 'b> { - unsafe fn new(stack: &'s mut NockStack) -> Self { - let (indirect, buffer) = IndirectAtom::new_raw_mut_bytes(stack, INITIAL_CAPACITY_BYTES); - NockWriter { + unsafe fn new(stack: &'s mut NockStack) -> AllocResult { + let (indirect, buffer) = IndirectAtom::new_raw_mut_bytes(stack, INITIAL_CAPACITY_BYTES)?; + Ok(NockWriter { stack, buffer, indirect, cursor: 0, - } + }) } unsafe fn finalize(mut self) -> Atom { self.indirect.normalize_as_atom() } - unsafe fn expand(&mut self) { + unsafe fn expand(&mut self) -> AllocResult<()> { let sz = self.buffer.len(); - let (new_indirect, new_buffer) = IndirectAtom::new_raw_mut_bytes(self.stack, sz * 2); + let (new_indirect, new_buffer) = IndirectAtom::new_raw_mut_bytes(self.stack, sz * 2)?; new_buffer[0..sz].copy_from_slice(self.buffer); self.buffer = new_buffer; self.indirect = new_indirect; + Ok(()) } } @@ -54,7 +55,7 @@ impl Write for NockWriter<'_, '_> { } pub fn nock_fmt(context: &mut Context, fmt: Arguments<'_>) -> Result { - let mut nw = unsafe { NockWriter::new(&mut context.stack) }; + let mut nw = unsafe { NockWriter::new(&mut context.stack)? }; nw.write_fmt(fmt)?; Ok(unsafe { nw.finalize() }) } diff --git a/rust/sword/src/hamt.rs b/rust/sword/src/hamt.rs index 7ec148c..6cb6f95 100644 --- a/rust/sword/src/hamt.rs +++ b/rust/sword/src/hamt.rs @@ -1,4 +1,4 @@ -use crate::mem::{NockStack, Preserve}; +use crate::mem::{AllocResult, NockStack, Preserve}; use crate::mug::mug_u32; use crate::noun::Noun; use crate::persist::{pma_contains, Persist}; @@ -61,12 +61,12 @@ impl MutStem { pub struct MutHamt(*mut MutStem); impl MutHamt { - pub fn new(stack: &mut NockStack) -> MutHamt { + pub fn new(stack: &mut NockStack) -> AllocResult> { unsafe { - let new_stem = stack.struct_alloc::>(1); + let new_stem = stack.struct_alloc::>(1)?; (*new_stem).bitmap = 0; (*new_stem).typemap = 0; - MutHamt(new_stem) + Ok(MutHamt(new_stem)) } } @@ -97,7 +97,7 @@ impl MutHamt { } } - pub fn insert(self, stack: &mut NockStack, n: &mut Noun, t: T) { + pub fn insert(self, stack: &mut NockStack, n: &mut Noun, t: T) -> AllocResult<()> { let mut stem = self.0; let mut mug = mug_u32(stack, *n); let mut depth = 0u8; @@ -107,7 +107,7 @@ impl MutHamt { mug >>= 5; match (*stem).entry(chunk) { None => { - let new_leaf_buffer = stack.struct_alloc::<(Noun, T)>(1); + let new_leaf_buffer = stack.struct_alloc::<(Noun, T)>(1)?; *new_leaf_buffer = (*n, t); (*stem).bitmap |= chunk_to_bit(chunk); (*stem).typemap &= !chunk_to_bit(chunk); @@ -132,7 +132,7 @@ impl MutHamt { } } if depth >= 5 { - let new_leaf_buffer = stack.struct_alloc::<(Noun, T)>(leaf.len + 1); + let new_leaf_buffer = stack.struct_alloc::<(Noun, T)>(leaf.len + 1)?; copy_nonoverlapping(leaf.buffer, new_leaf_buffer, leaf.len); *new_leaf_buffer.add(leaf.len) = (*n, t); (*stem).buffer[chunk as usize] = MutEntry { @@ -144,7 +144,7 @@ impl MutHamt { break; } else { assert!(leaf.len == 1); - let new_stem = stack.struct_alloc::>(1); + let new_stem = stack.struct_alloc::>(1)?; let leaf_mug = mug_u32(stack, (*leaf.buffer).0); let leaf_chunk = (leaf_mug >> ((depth + 1) * 5)) & 0x1f; (*new_stem).bitmap = chunk_to_bit(leaf_chunk); @@ -160,6 +160,7 @@ impl MutHamt { } } } + Ok(()) } } @@ -284,15 +285,15 @@ impl Hamt { unsafe { (*self.0).bitmap == 0 } } // Make a new, empty HAMT - pub fn new(stack: &mut NockStack) -> Self { + pub fn new(stack: &mut NockStack) -> AllocResult { unsafe { - let stem_ptr = stack.struct_alloc::>(1); + let stem_ptr = stack.struct_alloc::>(1)?; *stem_ptr = Stem { bitmap: 0, typemap: 0, buffer: null_mut(), }; - Hamt(stem_ptr) + Ok(Hamt(stem_ptr)) } } @@ -336,11 +337,11 @@ impl Hamt { // XX a delete function requires a stack, do we need one? /// Make a new HAMT with the value inserted or replaced at the key. - pub fn insert(&self, stack: &mut NockStack, n: &mut Noun, t: T) -> Hamt { + pub fn insert(&self, stack: &mut NockStack, n: &mut Noun, t: T) -> AllocResult> { let mut mug = mug_u32(stack, *n); let mut depth = 0u8; let mut stem = unsafe { *self.0 }; - let stem_ret = unsafe { stack.struct_alloc::>(1) }; + let stem_ret = unsafe { stack.struct_alloc::>(1) }?; let mut dest = stem_ret; unsafe { 'insert: loop { @@ -349,10 +350,10 @@ impl Hamt { match stem.entry(chunk) { // No entry found at mug chunk index; add Leaf to current Stem None => { - let new_leaf_buffer = stack.struct_alloc(1); + let new_leaf_buffer = stack.struct_alloc(1)?; *new_leaf_buffer = (*n, t); let split = stem.hypothetical_index(chunk); - let new_buffer = stack.struct_alloc(stem.size() + 1); + let new_buffer = stack.struct_alloc(stem.size() + 1)?; if split > 0 { copy_nonoverlapping(stem.buffer, new_buffer, split); } @@ -374,11 +375,11 @@ impl Hamt { typemap: stem.typemap & !chunk_to_bit(chunk), buffer: new_buffer, }; - break Hamt(stem_ret); + break Ok(Hamt(stem_ret)); } // Stem found at mug chunk index; insert into found Stem Some((Left(next_stem), idx)) => { - let new_buffer = stack.struct_alloc(stem.size()); + let new_buffer = stack.struct_alloc(stem.size())?; copy_nonoverlapping(stem.buffer, new_buffer, stem.size()); *dest = Stem { bitmap: stem.bitmap, @@ -395,10 +396,10 @@ impl Hamt { // Override existing value for key, if one exists for (ldx, pair) in leaf.to_mut_slice().iter_mut().enumerate() { if unifying_equality(stack, n, &mut pair.0) { - let new_leaf_buffer = stack.struct_alloc(leaf.len); + let new_leaf_buffer = stack.struct_alloc(leaf.len)?; copy_nonoverlapping(leaf.buffer, new_leaf_buffer, leaf.len); (*new_leaf_buffer.add(ldx)).1 = t; - let new_buffer = stack.struct_alloc(stem.size()); + let new_buffer = stack.struct_alloc(stem.size())?; copy_nonoverlapping(stem.buffer, new_buffer, stem.size()); *new_buffer.add(idx) = Entry { leaf: Leaf { @@ -411,16 +412,16 @@ impl Hamt { typemap: stem.typemap, buffer: new_buffer, }; - break 'insert Hamt(stem_ret); + break 'insert Ok(Hamt(stem_ret)); } } // No existing pair in this Leaf matches the key, and we've maxxed out the // Hamt depth; add the the key-value pair to the list of pairs for this Leaf if depth >= 5 { - let new_leaf_buffer = stack.struct_alloc(leaf.len + 1); + let new_leaf_buffer = stack.struct_alloc(leaf.len + 1)?; copy_nonoverlapping(leaf.buffer, new_leaf_buffer, leaf.len); *new_leaf_buffer.add(leaf.len) = (*n, t); - let new_buffer = stack.struct_alloc(stem.size()); + let new_buffer = stack.struct_alloc(stem.size())?; copy_nonoverlapping(stem.buffer, new_buffer, stem.size()); *new_buffer.add(idx) = Entry { leaf: Leaf { @@ -433,7 +434,7 @@ impl Hamt { typemap: stem.typemap, buffer: new_buffer, }; - break 'insert Hamt(stem_ret); + break 'insert Ok(Hamt(stem_ret)); // No existing pair in this Leaf matches the key, but we haven't maxxed out // the Hamt depth yet. If we haven't hit the depth limit yet, we shouldn't // be making a linked list of pairs. Turn the Leaf into a Stem and insert @@ -443,7 +444,7 @@ impl Hamt { // Make a fake node pointing to the old leaf and "insert into it" the // next time around assert!(leaf.len == 1); - let fake_buffer = stack.struct_alloc(1); + let fake_buffer = stack.struct_alloc(1)?; *fake_buffer = Entry { leaf }; // Get the mug chunk for the Noun at the *next* level so that we can // build a fake stem for it @@ -454,7 +455,7 @@ impl Hamt { typemap: 0, buffer: fake_buffer, }; - let new_buffer = stack.struct_alloc(stem.size()); + let new_buffer = stack.struct_alloc(stem.size())?; copy_nonoverlapping(stem.buffer, new_buffer, stem.size()); *dest = Stem { bitmap: stem.bitmap, @@ -906,10 +907,10 @@ mod test { let size = 1 << 27; let top_slots = 100; let mut stack = NockStack::new(size, top_slots); - let mut hamt = Hamt::::new(&mut stack); - hamt = hamt.insert(&mut stack, &mut D(0), D(1)); - hamt = hamt.insert(&mut stack, &mut D(2), D(3)); - hamt = hamt.insert(&mut stack, &mut D(4), D(5)); + let mut hamt = Hamt::::new(&mut stack).unwrap(); + hamt = hamt.insert(&mut stack, &mut D(0), D(1)).unwrap(); + hamt = hamt.insert(&mut stack, &mut D(2), D(3)).unwrap(); + hamt = hamt.insert(&mut stack, &mut D(4), D(5)).unwrap(); let mut iter = hamt.iter(); let three = cdr(&mut iter); let one = cdr(&mut iter); @@ -927,10 +928,10 @@ mod test { let size = 1 << 27; let top_slots = 100; let mut stack = NockStack::new(size, top_slots); - let mut hamt = Hamt::::new(&mut stack); + let mut hamt = Hamt::::new(&mut stack).unwrap(); let mut hs = HashSet::new(); for n in 0..100 { - hamt = hamt.insert(&mut stack, &mut D(n), D(n)); + hamt = hamt.insert(&mut stack, &mut D(n), D(n)).unwrap(); hs.insert((n, n)); } let mut iter = hamt.iter(); @@ -945,16 +946,16 @@ mod test { let size = 1 << 27; let top_slots = 100; let mut stack = NockStack::new(size, top_slots); - let mut hamt = Hamt::::new(&mut stack); + let mut hamt = Hamt::::new(&mut stack).unwrap(); let mut n = D(0); let t = D(1); - hamt = hamt.insert(&mut stack, &mut n, t); + hamt = hamt.insert(&mut stack, &mut n, t).unwrap(); let lu = hamt.lookup(&mut stack, &mut n); let lu_value = unsafe { lu.expect("lookup failed").as_raw() }; assert_eq!(lu_value, 1); let mut n = D(2); let t = D(3); - hamt = hamt.insert(&mut stack, &mut n, t); + hamt = hamt.insert(&mut stack, &mut n, t).unwrap(); let lu = hamt.lookup(&mut stack, &mut D(2)); let lu_value = unsafe { lu.expect("lookup failed").as_raw() }; assert_eq!(lu_value, 3); @@ -965,22 +966,22 @@ mod test { let size = 1 << 27; let top_slots = 100; let mut stack = NockStack::new(size, top_slots); - let mut hamt = Hamt::::new(&mut stack); + let mut hamt = Hamt::::new(&mut stack).unwrap(); // 3-way collision // x: 0 y: 87699370 x_hash: 2046756072 y_hash: 2046756072 // x: 0 z: 317365951 x_hash: 2046756072 z_hash: 2046756072 let mut n = D(0); let t = D(0); - hamt = hamt.insert(&mut stack, &mut n, t); + hamt = hamt.insert(&mut stack, &mut n, t).unwrap(); let mut n = D(87699370); let t = D(87699370); - hamt = hamt.insert(&mut stack, &mut n, t); + hamt = hamt.insert(&mut stack, &mut n, t).unwrap(); let mut n = D(317365951); let t = D(317365951); - hamt = hamt.insert(&mut stack, &mut n, t); + hamt = hamt.insert(&mut stack, &mut n, t).unwrap(); let lu = hamt.lookup(&mut stack, &mut D(0)); let lu_value = unsafe { lu.expect("0 lookup failed").as_raw() }; @@ -1000,13 +1001,13 @@ mod test { let size = 1 << 27; let top_slots = 100; let mut stack = NockStack::new(size, top_slots); - let mut hamt = Hamt::::new(&mut stack); + let mut hamt = Hamt::::new(&mut stack).unwrap(); // 3-way collision // x: 0 y: 87699370 x_hash: 2046756072 y_hash: 2046756072 // x: 0 z: 317365951 x_hash: 2046756072 z_hash: 2046756072 let mut hs = HashSet::new(); for x in &[0, 87699370, 317365951] { - hamt = hamt.insert(&mut stack, &mut D(*x), D(*x)); + hamt = hamt.insert(&mut stack, &mut D(*x), D(*x)).unwrap(); hs.insert((*x, *x)); } for x in hamt.iter() { diff --git a/rust/sword/src/interpreter.rs b/rust/sword/src/interpreter.rs index 6440bcd..5c6b276 100644 --- a/rust/sword/src/interpreter.rs +++ b/rust/sword/src/interpreter.rs @@ -8,6 +8,7 @@ use crate::jets::cold::Cold; use crate::jets::hot::Hot; use crate::jets::warm::Warm; use crate::jets::JetErr; +use crate::mem::AllocResult; use crate::mem::NockStack; use crate::mem::Preserve; use crate::noun; @@ -266,10 +267,10 @@ pub trait Slogger { * pri = debug priority * tank = output as tank */ - fn slog(&mut self, stack: &mut NockStack, pri: u64, tank: Noun); + fn slog(&mut self, stack: &mut NockStack, pri: u64, tank: Noun) -> AllocResult<()>; /** Send %flog, raw debug output. */ - fn flog(&mut self, stack: &mut NockStack, cord: Noun); + fn flog(&mut self, stack: &mut NockStack, cord: Noun) -> AllocResult<()>; } impl Slogger for Pin<&mut T> @@ -278,12 +279,14 @@ where { // + Unpin // type SlogTarget = T::Target; - fn flog(&mut self, stack: &mut NockStack, cord: Noun) { + fn flog(&mut self, stack: &mut NockStack, cord: Noun) -> AllocResult<()> { (*self).deref_mut().flog(stack, cord); + Ok(()) } - fn slog(&mut self, stack: &mut NockStack, pri: u64, tank: Noun) { + fn slog(&mut self, stack: &mut NockStack, pri: u64, tank: Noun) -> AllocResult<()> { (**self).slog(stack, pri, tank); + Ok(()) } } @@ -351,12 +354,21 @@ pub enum Mote { Meme = tas!(b"meme") as isize, } -#[derive(Clone, Copy, Debug)] +/// Interpreter errors, reused in [`JetErr::Fail`] +#[derive(Clone, Debug)] pub enum Error { ScryBlocked(Noun), // path ScryCrashed(Noun), // trace Deterministic(Mote, Noun), // mote, trace NonDeterministic(Mote, Noun), // mote, trace + /// Allocation failed + AllocationError(crate::mem::AllocationError, Noun), +} + +impl From for Error { + fn from(err: crate::mem::AllocationError) -> Self { + Error::AllocationError(err, D(0)) + } } impl Preserve for Error { @@ -366,6 +378,7 @@ impl Preserve for Error { Error::ScryCrashed(ref mut trace) => trace.preserve(stack), Error::Deterministic(_, ref mut trace) => trace.preserve(stack), Error::NonDeterministic(_, ref mut trace) => trace.preserve(stack), + Error::AllocationError(_, ref mut trace) => trace.preserve(stack), } } @@ -375,6 +388,7 @@ impl Preserve for Error { Error::ScryCrashed(ref trace) => trace.assert_in_stack(stack), Error::Deterministic(_, ref trace) => trace.assert_in_stack(stack), Error::NonDeterministic(_, ref trace) => trace.assert_in_stack(stack), + Error::AllocationError(_, ref trace) => trace.assert_in_stack(stack), } } } @@ -391,11 +405,11 @@ impl From for Error { } } -pub type Result = result::Result; +pub type Result = result::Result; -const BAIL_EXIT: Result = Err(Error::Deterministic(Mote::Exit, D(0))); -const BAIL_FAIL: Result = Err(Error::NonDeterministic(Mote::Fail, D(0))); -const BAIL_INTR: Result = Err(Error::NonDeterministic(Mote::Intr, D(0))); +const BAIL_EXIT: Result = Err(Error::Deterministic(Mote::Exit, D(0))); +const BAIL_FAIL: Result = Err(Error::NonDeterministic(Mote::Fail, D(0))); +const BAIL_INTR: Result = Err(Error::NonDeterministic(Mote::Intr, D(0))); #[allow(unused_variables)] fn debug_assertions(stack: &mut NockStack, noun: Noun) { @@ -405,7 +419,7 @@ fn debug_assertions(stack: &mut NockStack, noun: Noun) { } /** Interpret nock */ -pub fn interpret(context: &mut Context, subject: Noun, formula: Noun) -> Result { +pub fn interpret(context: &mut Context, subject: Noun, formula: Noun) -> Result { let terminator = Arc::clone(&TERMINATOR); let orig_subject = subject; // for debugging let snapshot = context.save(); @@ -449,7 +463,7 @@ pub fn interpret(context: &mut Context, subject: Noun, formula: Noun) -> Result } } -unsafe fn work(terminator: Arc, context: &mut Context, formula: Noun, orig_subject: Noun, mut subject: Noun, mut res: Noun) -> Result { +unsafe fn work(terminator: Arc, context: &mut Context, formula: Noun, orig_subject: Noun, mut subject: Noun, mut res: Noun) -> Result { push_formula(&mut context.stack, formula, true)?; loop { let work: NockWork = *context.stack.top(); @@ -504,7 +518,7 @@ unsafe fn work(terminator: Arc, context: &mut Context, formula: Noun } TodoCons::Cons => { let stack = &mut context.stack; - res = T(stack, &[cons.head, res]); + res = T(stack, &[cons.head, res])?; stack.pop::(); } }, @@ -589,7 +603,7 @@ unsafe fn work(terminator: Arc, context: &mut Context, formula: Noun } Todo4::Increment => { if let Ok(atom) = res.as_atom() { - res = inc(&mut context.stack, atom).as_noun(); + res = inc(&mut context.stack, atom)?.as_noun(); context.stack.pop::(); } else { // Cannot increment (Nock 4) a cell @@ -678,14 +692,14 @@ unsafe fn work(terminator: Arc, context: &mut Context, formula: Noun Todo8::ComputeResult => { let stack = &mut context.stack; if pins.tail { - subject = T(stack, &[res, subject]); + subject = T(stack, &[res, subject])?; stack.pop::(); push_formula(stack, pins.formula, true)?; } else { pins.todo = Todo8::RestoreSubject; pins.pin = subject; *stack.top() = NockWork::Work8(pins); - subject = T(stack, &[res, subject]); + subject = T(stack, &[res, subject])?; push_formula(stack, pins.formula, false)?; } } @@ -803,7 +817,7 @@ unsafe fn work(terminator: Arc, context: &mut Context, formula: Noun diet.axis.as_bitslice(), res, diet.tree, - ); + )?; context.stack.pop::(); } } @@ -864,7 +878,7 @@ unsafe fn work(terminator: Arc, context: &mut Context, formula: Noun Some(dint.hint), dint.body, res, - ) { + )? { res = found; } context.stack.pop::(); @@ -897,7 +911,7 @@ unsafe fn work(terminator: Arc, context: &mut Context, formula: Noun Todo11S::Done => { if let Some(found) = hint::match_post_nock( context, subject, sint.tag, None, sint.body, res, - ) { + )? { res = found; } context.stack.pop::(); @@ -923,7 +937,7 @@ unsafe fn work(terminator: Arc, context: &mut Context, formula: Noun let scry_stack = context.scry_stack; let scry_handler = cell.head(); let scry_gate = scry_handler.as_cell()?; - let payload = T(&mut context.stack, &[scry.reff, res]); + let payload = T(&mut context.stack, &[scry.reff, res])?; let scry_core = T( &mut context.stack, &[ @@ -931,9 +945,9 @@ unsafe fn work(terminator: Arc, context: &mut Context, formula: Noun payload, scry_gate.tail().as_cell()?.tail(), ], - ); + )?; let scry_form = - T(&mut context.stack, &[D(9), D(2), D(1), scry_core]); + T(&mut context.stack, &[D(9), D(2), D(1), scry_core])?; context.scry_stack = cell.tail(); // Alternately, we could use scry_core as the subject and [9 2 0 1] as @@ -959,7 +973,7 @@ unsafe fn work(terminator: Arc, context: &mut Context, formula: Noun scry.reff, scry.path, ], - ); + )?; mean_push(stack, hunk); break Err(Error::ScryCrashed(D(0))); } @@ -982,6 +996,9 @@ unsafe fn work(terminator: Arc, context: &mut Context, formula: Noun Error::ScryBlocked(_) => { break BAIL_FAIL; } + Error::AllocationError(_, _) => { + break Err(error); + }, }, } } else { @@ -993,7 +1010,7 @@ unsafe fn work(terminator: Arc, context: &mut Context, formula: Noun }; } } -fn push_formula(stack: &mut NockStack, formula: Noun, tail: bool) -> Result { +fn push_formula(stack: &mut NockStack, formula: Noun, tail: bool) -> Result { unsafe { if let Ok(formula_cell) = formula.as_cell() { // Formula @@ -1228,8 +1245,10 @@ fn exit( let h = *(stack.local_noun_pointer(0)); // XX: Small chance of clobbering something important after OOM? // XX: what if we OOM while making a stack trace - T(stack, &[h, t]) + T(stack, &[h, t]).expect("serf: failed to create trace, allocation error") } + // TODO: What do we do with an allocation error here? + Error::AllocationError(allocation_error, noun) => todo!(), }; while stack.get_frame_pointer() != virtual_frame { @@ -1242,6 +1261,7 @@ fn exit( Error::NonDeterministic(mote, _) => Error::NonDeterministic(mote, preserve), Error::ScryCrashed(_) => Error::ScryCrashed(preserve), Error::ScryBlocked(_) => error, + Error::AllocationError(_, _) => error, } } } @@ -1259,11 +1279,12 @@ fn mean_frame_push(stack: &mut NockStack, slots: usize) { /** Push onto the mean stack. */ -fn mean_push(stack: &mut NockStack, noun: Noun) { +fn mean_push(stack: &mut NockStack, noun: Noun) -> AllocResult<()>{ unsafe { let cur_trace = *(stack.local_noun_pointer(0)); - let new_trace = T(stack, &[noun, cur_trace]); + let new_trace = T(stack, &[noun, cur_trace])?; *(stack.local_noun_pointer(0)) = new_trace; + Ok(()) } } @@ -1283,7 +1304,7 @@ fn edit( edit_axis: &BitSlice, patch: Noun, mut tree: Noun, -) -> Noun { +) -> AllocResult { let mut res = patch; let mut dest: *mut Noun = &mut res; let mut cursor = edit_axis @@ -1300,7 +1321,7 @@ fn edit( cursor -= 1; if edit_axis[cursor] { unsafe { - let (cell, cellmem) = Cell::new_raw_mut(stack); + let (cell, cellmem) = Cell::new_raw_mut(stack)?; *dest = cell.as_noun(); (*cellmem).head = tree_cell.head(); dest = &mut ((*cellmem).tail); @@ -1308,7 +1329,7 @@ fn edit( tree = tree_cell.tail(); } else { unsafe { - let (cell, cellmem) = Cell::new_raw_mut(stack); + let (cell, cellmem) = Cell::new_raw_mut(stack)?; *dest = cell.as_noun(); (*cellmem).tail = tree_cell.tail(); dest = &mut ((*cellmem).head); @@ -1319,10 +1340,10 @@ fn edit( panic!("Invalid axis for edit"); }; } - res + Ok(res) } -pub fn inc(stack: &mut NockStack, atom: Atom) -> Atom { +pub fn inc(stack: &mut NockStack, atom: Atom) -> AllocResult { match atom.as_either() { Left(direct) => Atom::new(stack, direct.data() + 1), Right(indirect) => { @@ -1331,17 +1352,17 @@ pub fn inc(stack: &mut NockStack, atom: Atom) -> Atom { None => { // all ones, make an indirect one word bigger let (new_indirect, new_slice) = - unsafe { IndirectAtom::new_raw_mut_bitslice(stack, indirect.size() + 1) }; + unsafe { IndirectAtom::new_raw_mut_bitslice(stack, indirect.size() + 1)? }; new_slice.set(indirect_slice.len(), true); - new_indirect.as_atom() + Ok(new_indirect.as_atom()) } Some(first_zero) => { let (new_indirect, new_slice) = - unsafe { IndirectAtom::new_raw_mut_bitslice(stack, indirect.size()) }; + unsafe { IndirectAtom::new_raw_mut_bitslice(stack, indirect.size())? }; new_slice.set(first_zero, true); new_slice[first_zero + 1..] .copy_from_bitslice(&indirect_slice[first_zero + 1..]); - new_indirect.as_atom() + Ok(new_indirect.as_atom()) } } } @@ -1349,16 +1370,17 @@ pub fn inc(stack: &mut NockStack, atom: Atom) -> Atom { } /// Push onto the tracing stack -fn append_trace(stack: &mut NockStack, path: Noun) { +fn append_trace(stack: &mut NockStack, path: Noun) -> AllocResult<()> { unsafe { let trace_stack = *(stack.local_noun_pointer(1) as *const *const TraceStack); - let new_trace_entry = stack.struct_alloc(1); + let new_trace_entry = stack.struct_alloc(1)?; *new_trace_entry = TraceStack { path, start: Instant::now(), next: trace_stack, }; *(stack.local_noun_pointer(1) as *mut *const TraceStack) = new_trace_entry; + Ok(()) } } @@ -1404,7 +1426,7 @@ mod hint { tag: Atom, hint: Noun, body: Noun, - ) -> Option { + ) -> Option> { // XX: handle IndirectAtom tags match tag.direct()?.data() { tas!(b"sham") => { @@ -1476,7 +1498,11 @@ mod hint { } tas!(b"memo") => { let stack = &mut context.stack; - let mut key = Cell::new(stack, subject, body).as_noun(); + let key = match Cell::new(stack, subject, body) { + Ok(key) => key, + Err(_) => return Some(BAIL_EXIT), + }; + let mut key = key.as_noun(); context.cache.lookup(stack, &mut key).map(Ok) } _ => None, @@ -1490,7 +1516,7 @@ mod hint { tag: Atom, hint: Option<(Noun, Noun)>, _body: Noun, - ) -> Option { + ) -> Option> { // XX: handle IndirectAtom tags match tag.direct()?.data() { tas!(b"dont") => { @@ -1521,7 +1547,10 @@ mod hint { let stack = &mut context.stack; let (_form, clue) = hint?; - let noun = T(stack, &[tag.as_noun(), clue]); + let noun = match T(stack, &[tag.as_noun(), clue]) { + Ok(noun) => noun, + Err(_) => return Some(BAIL_EXIT), + }; mean_push(stack, noun); None } @@ -1532,7 +1561,10 @@ mod hint { // recursively work down frames to get the stack trace all // the way to the root. let mean = unsafe { *(context.stack.local_noun_pointer(0)) }; - let tone = Cell::new(&mut context.stack, D(2), mean); + let tone = match Cell::new(&mut context.stack, D(2), mean) { + Ok(tone) => tone, + Err(_) => return Some(BAIL_EXIT), + }; match mook(context, tone, true) { Ok(toon) => { @@ -1581,90 +1613,110 @@ mod hint { hint: Option, body: Noun, res: Noun, - ) -> Option { - let stack = &mut context.stack; - let slogger = &mut context.slogger; - let cold = &mut context.cold; - let hot = &context.hot; - let cache = &mut context.cache; - + ) -> Result> { // XX: handle IndirectAtom tags - match tag.direct()?.data() { - tas!(b"memo") => { - let mut key = Cell::new(stack, subject, body).as_noun(); - context.cache = cache.insert(stack, &mut key, res); - } - tas!(b"hand") | tas!(b"hunk") | tas!(b"lose") | tas!(b"mean") | tas!(b"spot") => { - mean_pop(stack); - } - tas!(b"fast") => { - if !cfg!(feature = "sham_hints") { - if let Some(clue) = hint { - let chum = clue.slot(2).ok()?; - let mut parent = clue.slot(6).ok()?; - loop { - if let Ok(parent_cell) = parent.as_cell() { - if unsafe { parent_cell.head().raw_equals(D(11)) } { - match parent.slot(7) { - Ok(noun) => { - parent = noun; - } - Err(_) => { - return None; + // Yes it has to be a nested closure: Option -> Result