diff --git a/rust/ares/src/serf.rs b/rust/ares/src/serf.rs index f84438b..d059695 100644 --- a/rust/ares/src/serf.rs +++ b/rust/ares/src/serf.rs @@ -1,9 +1,18 @@ use crate::interpreter::{interpret, raw_slot}; use crate::mem::NockStack; +use crate::mug::{mug, mug_u32}; use crate::newt::Newt; -use crate::noun::{Cell, Noun, D}; +use crate::noun::{Cell, IndirectAtom, Noun, D}; +use crate::serialization::{cue, jam}; use ares_macros::tas; +use memmap::Mmap; +use memmap::MmapMut; +use std::fs::{create_dir_all, File, OpenOptions}; use std::io; +use std::mem; +use std::path::PathBuf; +use std::ptr::copy_nonoverlapping; +use std::ptr::write_bytes; #[allow(dead_code)] const LOAD_AXIS: u64 = 4; @@ -17,16 +26,26 @@ const WISH_AXIS: u64 = 10; * u3_lord_init in vere to point at this binary and start vere like normal. */ pub fn serf() -> io::Result<()> { - let mut stack = NockStack::new(8 << 10 << 10, 0); - let mut newt = Newt::new(); - newt.ripe(&mut stack, 0, 0); + let pier_path_string = std::env::args() + .nth(2) + .ok_or(io::Error::new(io::ErrorKind::Other, "no pier path"))?; + let mut pier_path = PathBuf::from(pier_path_string); + pier_path.push(".urb"); + pier_path.push("chk"); + create_dir_all(&pier_path)?; + pier_path.push("snapshot.jam"); - let mut eve = 0; - let mut cor = D(0); + let ref mut stack = NockStack::new(8 << 10 << 10, 0); + let ref mut newt = Newt::new(); + + let (mut eve, mut cor) = load(stack, &pier_path).unwrap_or((0, D(0))); + let mug = mug_u32(stack, cor); + + newt.ripe(stack, eve, mug as u64); // Can't use for loop because it borrows newt loop { - let writ = if let Some(writ) = newt.next(&mut stack) { + let writ = if let Some(writ) = newt.next(stack) { writ } else { break; @@ -35,47 +54,67 @@ pub fn serf() -> io::Result<()> { let tag = raw_slot(writ, 2).as_direct().unwrap(); match tag.data() { tas!(b"live") => { - newt.live(&mut stack); + let inner = raw_slot(writ, 6); + match inner.as_direct().unwrap().data() { + tas!(b"cram") => eprintln!("cram"), + tas!(b"exit") => eprintln!("exit"), + tas!(b"save") => { + // XX what is eve for? + eprintln!("save"); + save(stack, &pier_path, eve, cor); + } + tas!(b"meld") => eprintln!("meld"), + tas!(b"pack") => eprintln!("pack"), + _ => eprintln!("unknown live"), + } + newt.live(stack); } tas!(b"peek") => { let sam = raw_slot(writ, 7); - let res = slam(&mut stack, &mut newt, cor, PEEK_AXIS, sam); - newt.peek_done(&mut stack, res); + let res = slam(stack, newt, cor, PEEK_AXIS, sam); + newt.peek_done(stack, res); } tas!(b"play") => { - if eve == 0 { + let run = if eve == 0 { // apply lifecycle to first batch let lit = raw_slot(writ, 7); - let sub = Cell::new_tuple(&mut stack, &[D(0), D(3)]).as_noun(); - let lyf = Cell::new_tuple(&mut stack, &[D(2), sub, D(0), D(2)]).as_noun(); - let gat = interpret(&mut stack, &mut Some(&mut newt), lit, lyf); + let sub = Cell::new_tuple(stack, &[D(0), D(3)]).as_noun(); + let lyf = Cell::new_tuple(stack, &[D(2), sub, D(0), D(2)]).as_noun(); + let gat = interpret(stack, &mut Some(newt), lit, lyf); cor = raw_slot(gat, 7); + false } else { - panic!("partial replay not implemented"); - } - eve = raw_slot(writ, 6).as_direct().unwrap().data(); + true + }; + + // do we need to assert something here? + // eve = raw_slot(writ, 6).as_direct().unwrap().data(); + let mut lit = raw_slot(writ, 7); loop { if let Ok(cell) = lit.as_cell() { + if run { + let ovo = cell.head(); + let res = slam(stack, newt, cor, POKE_AXIS, ovo).as_cell().unwrap(); + cor = res.tail(); + } eve += 1; lit = cell.tail(); } else { break; } } - newt.play_done(&mut stack, 0); + newt.play_done(stack, 0); } tas!(b"work") => { let ovo = raw_slot(writ, 7); - let res = slam(&mut stack, &mut newt, cor, POKE_AXIS, ovo) - .as_cell() - .unwrap(); + let res = slam(stack, newt, cor, POKE_AXIS, ovo).as_cell().unwrap(); let fec = res.head(); cor = res.tail(); eve += 1; - newt.work_done(&mut stack, eve - 1, 0, fec); + newt.work_done(stack, eve, 0, fec); } _ => panic!("got message with unknown tag {:?}", tag), }; @@ -91,3 +130,58 @@ pub fn slam(stack: &mut NockStack, newt: &mut Newt, cor: Noun, axis: u64, ovo: N let sub = Cell::new_tuple(stack, &[cor, ovo]).as_noun(); interpret(stack, &mut Some(newt), sub, fol) } + +fn save(stack: &mut NockStack, snap_path: &PathBuf, eve: u64, cor: Noun) { + let state = Cell::new(stack, D(eve), cor).as_noun(); + let mugged = mug(stack, state).as_noun(); + let snapshot = Cell::new(stack, mugged, state).as_noun(); + + let jammed = jam(stack, snapshot); + let f_out = OpenOptions::new() + .read(true) + .write(true) + .create(true) + .open(snap_path) + .unwrap(); + + f_out.set_len((jammed.size() << 3) as u64).unwrap(); + unsafe { + let mut out_map = MmapMut::map_mut(&f_out).unwrap(); + copy_nonoverlapping( + jammed.data_pointer() as *mut u8, + out_map.as_mut_ptr(), + jammed.size() << 3, + ); + out_map.flush().unwrap(); + }; +} + +fn load(stack: &mut NockStack, snap_path: &PathBuf) -> io::Result<(u64, Noun)> { + let f_in = File::open(snap_path)?; + + let in_len = f_in.metadata().unwrap().len(); + let jammed = unsafe { + let in_map = Mmap::map(&f_in).unwrap(); + let word_len = (in_len + 7) >> 3; + let (mut atom, dest) = IndirectAtom::new_raw_mut(stack, word_len as usize); + write_bytes(dest.add(word_len as usize - 1), 0, 8); + copy_nonoverlapping(in_map.as_ptr(), dest as *mut u8, in_len as usize); + mem::drop(in_map); + atom.normalize_as_atom() + }; + + let snapshot = cue(stack, jammed).as_cell().unwrap(); + let state = snapshot.tail().as_cell().unwrap(); + assert!( + unsafe { + mug(stack, state.as_noun()) + .as_noun() + .raw_equals(snapshot.head()) + }, + "snapshot is corrupt" + ); + let eve = state.head().as_direct().unwrap().data(); + let cor = state.tail(); + + Ok((eve, cor)) +}