diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/app.rs | 2 | ||||
| -rw-r--r-- | src/sheet/cell.rs | 46 | ||||
| -rw-r--r-- | src/sheet/eval.rs | 170 | ||||
| -rw-r--r-- | src/sheet/mod.rs | 60 | ||||
| -rw-r--r-- | src/widgets/sheetview.rs | 2 |
5 files changed, 210 insertions, 70 deletions
@@ -16,7 +16,7 @@ pub struct App<'a> { impl App<'_> { pub fn new() -> Self { - let sheet_id = Register::create(2000, 10000); + let sheet_id = Register::create(200, 1000); let sheet = Register::get(sheet_id).unwrap(); { diff --git a/src/sheet/cell.rs b/src/sheet/cell.rs index 53a4b04..9bee8ca 100644 --- a/src/sheet/cell.rs +++ b/src/sheet/cell.rs @@ -80,66 +80,80 @@ cell_from_integer!( f32, f64 ); -#[derive(Debug, Clone, Copy)] +#[derive(Debug, Clone)] pub struct CellRef { row: usize, column: usize, sheet_id: SheetId, + cell: Cell, } impl CellRef { - pub fn new(sheet_id: SheetId, row: usize, column: usize) -> Option<Self> { + pub unsafe fn new(sheet_id: SheetId, row: usize, column: usize, cell: Cell) -> Self { + Self { + sheet_id, + row, + column, + cell + } + } + + pub fn fetch_new(sheet_id: SheetId, row: usize, column: usize) -> Option<Self> { Register::get(sheet_id) .map(|sheet| { - sheet.read().unwrap().get_cell(row, column).map(|_| Self { + sheet.read().unwrap().get_cell(row, column).map(|cell| Self { sheet_id, row, column, + cell: cell.clone(), }) }) .unwrap_or(None) } pub fn value(&self) -> Cell { - return Register::get(self.sheet_id) - .unwrap() - .read() + self.cell.clone() + } + + pub fn set_value(&mut self, cell: Cell) { + self.cell = cell.clone(); + Register::get(self.sheet_id) .unwrap() - .get_cell(self.row, self.column) + .write() .unwrap() - .clone(); + .set_cell(self.row, self.column, cell); } pub fn left(&self) -> Option<Self> { if self.column > 0 { - Self::new(self.sheet_id, self.row, self.column - 1) + Self::fetch_new(self.sheet_id, self.row, self.column - 1) } else { None } } pub fn right(&self) -> Option<Self> { - Self::new(self.sheet_id, self.row, self.column + 1) + Self::fetch_new(self.sheet_id, self.row, self.column + 1) } pub fn up(&self) -> Option<Self> { if self.row > 0 { - Self::new(self.sheet_id, self.row - 1, self.column) + Self::fetch_new(self.sheet_id, self.row - 1, self.column) } else { None } } pub fn down(&self) -> Option<Self> { - Self::new(self.sheet_id, self.row + 1, self.column) + Self::fetch_new(self.sheet_id, self.row + 1, self.column) } pub fn begin(&self) -> Option<Self> { - Self::new(self.sheet_id, self.row, 0) + Self::fetch_new(self.sheet_id, self.row, 0) } pub fn end(&self) -> Option<Self> { - Self::new( + Self::fetch_new( self.sheet_id, self.row, Register::get(self.sheet_id) @@ -152,11 +166,11 @@ impl CellRef { } pub fn top(&self) -> Option<Self> { - Self::new(self.sheet_id, 0, self.column) + Self::fetch_new(self.sheet_id, 0, self.column) } pub fn bottom(&self) -> Option<Self> { - Self::new( + Self::fetch_new( self.sheet_id, Register::get(self.sheet_id) .unwrap() diff --git a/src/sheet/eval.rs b/src/sheet/eval.rs new file mode 100644 index 0000000..4633949 --- /dev/null +++ b/src/sheet/eval.rs @@ -0,0 +1,170 @@ +use std::{ + sync::{mpsc::channel, Arc, RwLock, RwLockReadGuard}, + thread::JoinHandle, +}; + +use mlua::prelude::*; + +use super::{cell::Cell, Sheet}; + +pub trait EvalFunction { + fn eval_function( + &self, + func: String, + range: Vec<(usize, usize)>, + ) -> JoinHandle<Result<Sheet, String>>; +} + +impl EvalFunction for Arc<RwLock<Sheet>> { + fn eval_function( + &self, + func_text: String, + range: Vec<(usize, usize)>, + ) -> JoinHandle<Result<Sheet, String>> { + *self.read().unwrap().progress.lock().unwrap() = 0; + let this = Arc::clone(self); + std::thread::spawn(move || { + let chunks = range.chunks( + (range.len() / std::thread::available_parallelism().unwrap()) + .min(range.len()) + .max(1000), + ); + + if chunks.len() > 4 { + let (tx, rx) = channel(); + + std::thread::scope(|s| { + for chunk in chunks { + let tx = tx.clone(); + let this = Arc::clone(&this); + let func_text = func_text.clone(); + + s.spawn(move || { + let tx = tx.clone(); + let read_sheet = this.read().unwrap(); + let result = EvalRange::new(chunk.iter(), func_text, read_sheet); + match result { + Ok(evaluator) => { + for result in evaluator { + if result.is_err() { + tx.send(result).unwrap(); + break; + } + + tx.send(result).unwrap(); + } + } + Err(error) => tx.send(Err(error)).unwrap(), + } + }); + } + + drop(tx); + + let read_sheet = this.read().unwrap(); + let mut sheet = read_sheet.clone(); + + let mut count = 0; + + for result in rx { + count += 1; + *read_sheet.progress.lock().unwrap() = (count * 100 / range.len()) as u8; + + match result { + Ok((row, column, cell)) => sheet.set_cell(row, column, cell), + Err(error) => return Err(error.to_string()), + } + } + + crate::lua::new_instance().unwrap().expire_registry_values(); + Ok(sheet) + }) + } else { + let read_sheet = this.read().unwrap(); + let mut sheet = read_sheet.clone(); + let result = EvalRange::new((&range).iter(), func_text, read_sheet); + + match result { + Ok(evaluator) => { + for result in evaluator { + match result { + Ok((row, column, cell)) => sheet.set_cell(row, column, cell), + Err(error) => return Err(error.to_string()), + } + } + } + Err(error) => return Err(error), + } + + crate::lua::new_instance().unwrap().expire_registry_values(); + + Ok(sheet) + } + }) + } +} + +struct EvalRange<'a, I> +where + I: Iterator<Item = &'a (usize, usize)>, +{ + lua: Lua, + reg_func_key: LuaRegistryKey, + range: I, + read_sheet: RwLockReadGuard<'a, Sheet>, +} + +impl<'a, I> EvalRange<'a, I> +where + I: Iterator<Item = &'a (usize, usize)>, +{ + fn new( + range: I, + script: String, + read_sheet: RwLockReadGuard<'a, Sheet>, + ) -> Result<Self, String> { + let lua = crate::lua::new_instance().unwrap(); + let result = lua + .load(script.clone()) + .set_name("Temp Script") + .eval::<LuaFunction>(); + + if result.is_err() { + return Err(result.err().unwrap().to_string()); + } + + let lua = crate::lua::new_instance().unwrap(); + let func = lua + .load(script) + .set_name("Temp Script") + .eval::<LuaFunction>() + .unwrap(); + + Ok(Self { + reg_func_key: lua.create_registry_value(func).unwrap(), + lua, + range, + read_sheet, + }) + } +} + +impl<'a, I> Iterator for EvalRange<'a, I> +where + I: Iterator<Item = &'a (usize, usize)>, +{ + type Item = Result<(usize, usize, Cell), String>; + + fn next(&mut self) -> Option<Self::Item> { + let func: LuaFunction = self.lua.registry_value(&self.reg_func_key).unwrap(); + if let Some((row, column)) = self.range.next() { + let cellref = self.read_sheet.get_ref(*row, *column); + match func.call::<_, Cell>(cellref) { + Ok(cell) => Some(Ok((*row, *column, cell))), + Err(error) => Some(Err(error.to_string())), + } + } else { + None + } + } +} diff --git a/src/sheet/mod.rs b/src/sheet/mod.rs index 18ccee0..11f95c9 100644 --- a/src/sheet/mod.rs +++ b/src/sheet/mod.rs @@ -1,13 +1,11 @@ -use std::{ - sync::{Arc, Mutex, RwLock}, - thread::JoinHandle, -}; +use std::sync::Mutex; use cell::{Cell, CellRef}; use mlua::prelude::*; use register::{Register, SheetId}; pub mod cell; +pub mod eval; pub mod register; #[derive(Debug, Default)] @@ -43,7 +41,9 @@ impl Sheet { } pub fn get_ref(&self, row: usize, column: usize) -> Option<CellRef> { - CellRef::new(self.id, row, column) + self.get_cell(row, column).map(|cell| { + unsafe { CellRef::new(self.id, row, column, cell.clone()) } + }) } pub fn height(&self) -> usize { @@ -113,56 +113,12 @@ impl LuaUserData for SheetLuaRef { fn add_methods<'lua, M: mlua::UserDataMethods<'lua, Self>>(methods: &mut M) { methods.add_method_mut("cell", |lua, luaref, (row, column): (usize, usize)| { - if let Some(_) = Register::get(luaref.id) { - Ok(CellRef::new(luaref.id, row, column).into_lua(lua).unwrap()) + if let Some(rw) = Register::get(luaref.id) { + let sheet = rw.read().unwrap(); + Ok(sheet.get_ref(row, column).into_lua(lua).unwrap()) } else { Ok(LuaValue::Nil) } }) } } - -pub trait EvalFunction { - fn eval_function( - &self, - func: String, - range: Vec<(usize, usize)>, - ) -> JoinHandle<Result<Sheet, String>>; -} - -impl EvalFunction for Arc<RwLock<Sheet>> { - fn eval_function( - &self, - func_text: String, - range: Vec<(usize, usize)>, - ) -> JoinHandle<Result<Sheet, String>> { - let this = Arc::clone(self); - std::thread::spawn(move || { - let read_sheet = this.read().unwrap(); - *read_sheet.progress.lock().unwrap() = 0; - let mut sheet = read_sheet.clone(); - - let lua = crate::lua::new_instance().unwrap(); - let result = lua - .load(func_text.clone()) - .set_name("Temp Script") - .eval::<LuaFunction>(); - - match result { - Ok(func) => { - for (i, (row, column)) in range.iter().enumerate() { - *read_sheet.progress.lock().unwrap() = (i * 100 / range.len()) as u8; - let cellref = read_sheet.get_ref(*row, *column); - match func.call::<_, Cell>(cellref) { - Ok(cell) => sheet.set_cell(*row, *column, cell), - Err(error) => return Err(error.to_string()), - } - } - } - Err(error) => return Err(error.to_string()), - } - - Ok(sheet) - }) - } -} diff --git a/src/widgets/sheetview.rs b/src/widgets/sheetview.rs index f814cf2..2584c96 100644 --- a/src/widgets/sheetview.rs +++ b/src/widgets/sheetview.rs @@ -11,7 +11,7 @@ use ratatui::{ use crate::sheet::{ register::{Register, SheetId}, - EvalFunction, Sheet, + eval::EvalFunction, Sheet, }; use super::luaeditor::LuaEditor; |