use std::collections::{HashMap, VecDeque}; use crsn::asm::data::literal::Value; use crsn::asm::instr::cond::Flag; use crsn::module::{EvalRes, OpTrait}; use crsn::runtime::fault::Fault; use crsn::runtime::run_thread::{state::RunState, ThreadInfo}; use crsn::sexp; use crsn::sexp::{atom_qs, Sexp}; use crsn::utils::A; use crate::defs::{BufIoMode, BufOps, BufValue}; use crsn::asm::data::Wr; #[derive(Debug, Default, Clone)] struct Buffer { data: VecDeque, iomode: BufIoMode, } #[derive(Debug, Default)] struct ExtData { buffers: HashMap, } // TODO optimize and DRY impl OpTrait for BufOps { fn execute(&self, info: &ThreadInfo, state: &mut RunState) -> Result { let eres = EvalRes::default(); match self { BufOps::New { dst, value } => { let id = info.unique_handle(); let que = match value { BufValue::Zeros(len) => { let len = state.read(len)? as usize; let mut que = VecDeque::with_capacity(len); que.resize(len, 0); que } BufValue::Values(vals) => { let mut que = VecDeque::with_capacity(vals.len()); for val in vals { que.push_back(state.read(val)?); } que } BufValue::Chars(string) => { let mut que = VecDeque::new(); for val in string.chars() { que.push_back(val as Value); } que } }; let data: &mut ExtData = state.ext_mut(); data.buffers.insert(id, Buffer { data: que, iomode: Default::default() }); state.write(dst, id)?; } BufOps::SetIoMode { obj, mode } => { state.clear_status(); let handle = state.read_obj(obj)?; let store: &mut ExtData = state.ext_mut(); let buf = store.buffers.get_mut(&handle).ok_or_else(|| Fault::ObjectNotExist(handle))?; buf.iomode = *mode; } BufOps::Push { obj, src } => { state.clear_status(); let val = state.read(src)?; let handle = state.read_obj(obj)?; let store: &mut ExtData = state.ext_mut(); let buf = store.buffers.get_mut(&handle).ok_or_else(|| Fault::ObjectNotExist(handle))?; buf.data.push_back(val); } BufOps::Pop { dst, obj } => { state.clear_status(); let handle = state.read_obj(obj)?; let store: &mut ExtData = state.ext_mut(); let buf = store.buffers.get_mut(&handle).ok_or_else(|| Fault::ObjectNotExist(handle))?; if let Some(val) = buf.data.pop_back() { let empty = buf.data.is_empty(); state.write(dst, val)?; state.update_status(val); state.set_flag(Flag::Empty, empty); } else { state.set_flag(Flag::Empty, true); state.set_flag(Flag::Overflow, true); } } BufOps::Read { dst, obj, idx } => { state.clear_status(); let handle = state.read_obj(obj)?; let idx = state.read(idx)?; let store: &mut ExtData = state.ext_mut(); let buf = store.buffers.get(&handle).ok_or_else(|| Fault::ObjectNotExist(handle))?; if let Some(val) = buf.data.get(idx as usize) { let val = *val; state.update_status(val); state.write(dst, val)?; } else { state.set_flag(Flag::Overflow, true); } } BufOps::GetLen { dst, obj } => { state.clear_status(); let handle = state.read_obj(obj)?; let store: &mut ExtData = state.ext_mut(); let buf = store.buffers.get(&handle).ok_or_else(|| Fault::ObjectNotExist(handle))?; let val = buf.data.len() as Value; state.write(dst, val)?; state.update_status(val); } BufOps::Write { obj, idx, value } => { state.clear_status(); let handle = state.read_obj(obj)?; let val = state.read(value)?; let idx = state.read(idx)? as usize; let store: &mut ExtData = state.ext_mut(); let buf = store.buffers.get_mut(&handle).ok_or_else(|| Fault::ObjectNotExist(handle))?; if idx < buf.data.len() { buf.data[idx] = val; } else if idx == buf.data.len() { buf.data.push_back(val); } else { state.set_flag(Flag::Overflow, true); } } BufOps::CompareSwap { obj, idx, expected, src } => { state.clear_status(); let handle = state.read_obj(obj)?; let idx = state.read(idx)? as usize; let expected = state.read(expected)?; let store: &mut ExtData = state.ext_mut(); let buf = store.buffers.get_mut(&handle).ok_or_else(|| Fault::ObjectNotExist(handle))?; if idx < buf.data.len() { if buf.data[idx] == expected { // This looks stupid and it is stupid - the dance is needed to satisfy the borrow checker. let val = state.read(src)?; let store: &mut ExtData = state.ext_mut(); let buf = store.buffers.get_mut(&handle).unwrap(); buf.data[idx] = val; state.set_flag(Flag::Equal, true); } } else if idx == buf.data.len() && expected == 0 { let val = state.read(src)?; let store: &mut ExtData = state.ext_mut(); let buf = store.buffers.get_mut(&handle).unwrap(); buf.data.push_back(val); state.set_flag(Flag::Equal, true); } else { state.set_flag(Flag::Overflow, true); } } BufOps::Insert { obj, idx, value } => { state.clear_status(); let handle = state.read_obj(obj)?; let val = state.read(value)?; let idx = state.read(idx)? as usize; let store: &mut ExtData = state.ext_mut(); let buf = store.buffers.get_mut(&handle).ok_or_else(|| Fault::ObjectNotExist(handle))?; if idx < buf.data.len() { buf.data.insert(idx, val); } else if idx == buf.data.len() { buf.data.push_back(val); } else { state.set_flag(Flag::Overflow, true); } } BufOps::Remove { dst, obj, idx } => { state.clear_status(); let handle = state.read_obj(obj)?; let idx = state.read(idx)? as usize; let store: &mut ExtData = state.ext_mut(); let buf = store.buffers.get_mut(&handle).ok_or_else(|| Fault::ObjectNotExist(handle))?; if idx < buf.data.len() { let val = buf.data.remove(idx).unwrap(); let empty = buf.data.is_empty(); state.update_status(val); state.write(dst, val)?; state.set_flag(Flag::Empty, empty); } else { state.set_flag(Flag::Overflow, true); } } BufOps::Resize { obj, len } => { let handle = state.read_obj(obj)?; let len = state.read(len)? as usize; let store: &mut ExtData = state.ext_mut(); let buf = store.buffers.get_mut(&handle).ok_or_else(|| Fault::ObjectNotExist(handle))?; buf.data.resize(len, 0); } BufOps::Reverse { obj } => { let handle = state.read_obj(obj)?; let store: &mut ExtData = state.ext_mut(); let buf = store.buffers.get_mut(&handle).ok_or_else(|| Fault::ObjectNotExist(handle))?; let len = buf.data.len(); if len > 1 { let mut start = 0; let mut end = len - 1; while start < end { buf.data.swap(start, end); start += 1; end -= 1; } } } BufOps::AppendBuf { obj, obj2 } => { let handle = state.read_obj(obj)?; let handle2 = state.read_obj(obj2)?; let store: &mut ExtData = state.ext_mut(); // TODO figure out how to avoid cloning let buf2 = store.buffers.get(&handle2).ok_or_else(|| Fault::ObjectNotExist(handle))?.data.clone(); let buf = store.buffers.get_mut(&handle).ok_or_else(|| Fault::ObjectNotExist(handle))?; buf.data.extend(buf2); } BufOps::PrependBuf { obj, obj2 } => { let handle = state.read_obj(obj)?; let handle2 = state.read_obj(obj2)?; let store: &mut ExtData = state.ext_mut(); // TODO figure out how to avoid cloning let buf2 = store.buffers.get(&handle2).ok_or_else(|| Fault::ObjectNotExist(handle))?.data.clone(); let buf = store.buffers.get_mut(&handle).ok_or_else(|| Fault::ObjectNotExist(handle))?; for v in buf2.into_iter().rev() { buf.data.push_front(v); } } } Ok(eres) } fn to_sexp(&self) -> Sexp { match self { BufOps::Push { obj, src } => sexp::list(&[A("push"), A(obj), A(src)]), BufOps::Pop { dst, obj } => sexp::list(&[A("pop"), A(dst), A(obj)]), BufOps::New { dst, value } => { match value { BufValue::Zeros(n) => { sexp::list(&[A("mkbf"), A(dst), A(n)]) } BufValue::Values(vals) => { let mut vals: Vec<_> = vals.iter().map(A).collect(); vals.insert(0, A(dst)); vals.insert(0, A("mkbfv")); sexp::list(&vals) } BufValue::Chars(str) => { sexp::list(&[A("mkbfv"), A(dst), atom_qs(str)]) } } } BufOps::Read { dst, obj, idx } => { sexp::list(&[A("bfrd"), A(dst), A(obj), A(idx)]) } BufOps::GetLen { dst, obj } => { sexp::list(&[A("bfsz"), A(dst), A(obj)]) } BufOps::Write { obj, idx, value } => { sexp::list(&[A("bfwr"), A(obj), A(idx), A(value)]) } BufOps::CompareSwap { obj, idx, expected , src } => { sexp::list(&[A("bfcas"), A(obj), A(idx), A(expected), A(src)]) } BufOps::Insert { obj, idx, value } => { sexp::list(&[A("bfins"), A(obj), A(idx), A(value)]) } BufOps::Remove { dst, obj, idx } => { sexp::list(&[A("bfrm"), A(dst), A(obj), A(idx)]) } BufOps::Resize { obj, len } => { sexp::list(&[A("bfrsz"), A(obj), A(len)]) } BufOps::Reverse { obj } => { sexp::list(&[A("bfrev"), A(obj)]) } BufOps::AppendBuf { obj, obj2 } => { sexp::list(&[A("bfapp"), A(obj), A(obj2)]) } BufOps::PrependBuf { obj, obj2 } => { sexp::list(&[A("bfprep"), A(obj), A(obj2)]) } BufOps::SetIoMode { obj, mode } => { sexp::list(&[A("bfio"), A(obj), A(mode.to_value())]) } } } } pub(crate) fn drop_obj(state: &mut RunState, handle: Value) -> Result, Fault> { let store: &mut ExtData = state.ext_mut(); Ok(store.buffers.remove(&handle).map(|_| ())) } /// Run-time method called to read an object (using the object handle syntax) pub(crate) fn read_obj(state: &mut RunState, handle: Value) -> Result, Fault> { let store: &mut ExtData = state.ext_mut(); if let Some(buf) = store.buffers.get_mut(&handle) { match match buf.iomode { BufIoMode::Queue => buf.data.pop_front(), BufIoMode::ReverseQueue => buf.data.pop_back(), BufIoMode::Stack => buf.data.pop_back(), BufIoMode::ReverseStack => buf.data.pop_front() } { None => { state.set_flag(Flag::Overflow, true); state.set_flag(Flag::Empty, true); Ok(Some(0)) } Some(val) => { let em = buf.data.is_empty(); state.set_flag(Flag::Empty, em); Ok(Some(val)) } } } else { Ok(None) } } /// Run-time method called to read all from an object (using the object handle syntax) pub(crate) fn read_obj_all(state: &mut RunState, whandle: Wr, rhandle: Value) -> Result, Fault> { let store: &mut ExtData = state.ext_mut(); if let Some(buf) = store.buffers.get_mut(&rhandle) { for v in buf.data.clone() { // FIXME potentially needless clone (but allows "aliasing") state.write(whandle, v)?; } Ok(Some(())) } else { Ok(None) } } /// Run-time method called to write an object (using the object handle syntax) pub(crate) fn write_obj(state: &mut RunState, handle: Value, value: Value) -> Result, Fault> { let store: &mut ExtData = state.ext_mut(); if let Some(buf) = store.buffers.get_mut(&handle) { match buf.iomode { BufIoMode::Queue => buf.data.push_back(value), BufIoMode::ReverseQueue => buf.data.push_front(value), BufIoMode::Stack => buf.data.push_back(value), BufIoMode::ReverseStack => buf.data.push_front(value) } Ok(Some(())) } else { Ok(None) } }