1use crate::SheetId;
2use crate::arrow_store::SheetStore;
3use crate::engine::eval_delta::{DeltaCollector, DeltaMode, EvalDelta};
4use crate::engine::named_range::{NameScope, NamedDefinition};
5use crate::engine::range_view::RangeView;
6use crate::engine::spill::{RegionLockManager, SpillMeta, SpillShape};
7use crate::engine::{DependencyGraph, EvalConfig, Scheduler, VertexId, VertexKind};
8use crate::interpreter::Interpreter;
9use crate::reference::{CellRef, Coord, RangeRef};
10use crate::traits::FunctionProvider;
11use crate::traits::{EvaluationContext, Resolver};
12use chrono::Timelike;
13use formualizer_common::{col_letters_from_1based, parse_a1_1based};
14use formualizer_parse::parser::ReferenceType;
15use formualizer_parse::{ASTNode, ExcelError, ExcelErrorKind, LiteralValue};
16use rayon::ThreadPoolBuilder;
17use rustc_hash::{FxHashMap, FxHashSet};
18use std::sync::Arc;
19use std::sync::atomic::{AtomicBool, Ordering};
20
21pub struct Engine<R> {
22 pub(crate) graph: DependencyGraph,
23 resolver: R,
24 pub config: EvalConfig,
25 clock: Arc<dyn crate::timezone::ClockProvider>,
26 thread_pool: Option<Arc<rayon::ThreadPool>>,
27 pub recalc_epoch: u64,
28 snapshot_id: std::sync::atomic::AtomicU64,
29 spill_mgr: ShimSpillManager,
30 arrow_sheets: SheetStore,
32 has_edited: bool,
34 overlay_compactions: u64,
36
37 computed_overlay_bytes_estimate: usize,
39 computed_overlay_mirroring_disabled: bool,
40 pub(crate) force_materialize_range_views: bool,
43 row_bounds_cache: std::sync::RwLock<Option<RowBoundsCache>>,
45 source_cache: Arc<std::sync::RwLock<SourceCache>>,
46 staged_formulas: std::collections::HashMap<String, Vec<(u32, u32, String)>>,
48 active_cancel_flag: Option<Arc<AtomicBool>>,
50
51 action_depth: u32,
56}
57
58pub struct EngineAction<'a, R>
63where
64 R: EvaluationContext,
65{
66 engine: &'a mut Engine<R>,
67 name: String,
68 log: Option<*mut crate::engine::ChangeLog>,
71 arrow_undo: Option<*mut crate::engine::ArrowUndoBatch>,
74 atomic_policy: bool,
76}
77
78impl<'a, R> EngineAction<'a, R>
79where
80 R: EvaluationContext,
81{
82 #[inline]
83 fn addr_for(&mut self, sheet: &str, row: u32, col: u32) -> crate::reference::CellRef {
84 let sheet_id = self.engine.graph.sheet_id_mut(sheet);
85 let coord = crate::reference::Coord::from_excel(row, col, true, true);
86 crate::reference::CellRef::new(sheet_id, coord)
87 }
88
89 #[inline]
90 pub fn name(&self) -> &str {
91 &self.name
92 }
93
94 #[inline]
95 pub fn set_cell_value(
96 &mut self,
97 sheet: &str,
98 row: u32,
99 col: u32,
100 value: LiteralValue,
101 ) -> Result<(), crate::engine::EditorError> {
102 if self.log.is_some() {
103 let old_value = self.engine.read_cell_value(sheet, row, col);
104 let old_formula = self.engine.read_cell_formula_ast(sheet, row, col);
105 let addr = self.addr_for(sheet, row, col);
106 let Some(log_ptr) = self.log else {
107 return Err(crate::engine::EditorError::TransactionFailed {
108 reason: "action_with_logger: missing ChangeLog".to_string(),
109 });
110 };
111
112 let old_comp = if self.arrow_undo.is_some() {
115 self.engine.read_computed_overlay_cell(sheet, row, col)
116 } else {
117 None
118 };
119
120 let delta_old_sem = if old_formula.is_some() {
121 None
122 } else {
123 Some(old_value.clone().unwrap_or(LiteralValue::Empty))
124 };
125
126 let start_len = unsafe { (&*log_ptr).len() };
127
128 let log = unsafe { &mut *log_ptr };
130 self.engine.edit_with_logger(log, |editor| {
131 editor.set_cell_value(addr, value.clone());
132 });
133 log.patch_last_cell_event_old_state(addr, old_value.clone(), old_formula.clone());
134
135 if let Some(undo_ptr) = self.arrow_undo {
136 let new_events = &unsafe { (&*log_ptr).events() }[start_len..];
138 let undo = unsafe { &mut *undo_ptr };
139 self.engine
140 .record_spill_ops_into_arrow_undo(undo, new_events);
141
142 let new_comp = self.engine.read_computed_overlay_cell(sheet, row, col);
144 let sheet_id = self.engine.graph.sheet_id_mut(sheet);
145 let row0 = row.saturating_sub(1);
146 let col0 = col.saturating_sub(1);
147 let delta_new_sem = Some(value.clone());
148 undo.record_delta_cell(sheet_id, row0, col0, delta_old_sem, delta_new_sem);
149 undo.record_computed_cell(sheet_id, row0, col0, old_comp, new_comp);
150 }
151 Ok(())
152 } else {
153 self.engine
154 .set_cell_value(sheet, row, col, value)
155 .map_err(crate::engine::EditorError::from)
156 }
157 }
158
159 #[inline]
160 pub fn set_cell_formula(
161 &mut self,
162 sheet: &str,
163 row: u32,
164 col: u32,
165 ast: ASTNode,
166 ) -> Result<(), crate::engine::EditorError> {
167 if self.log.is_some() {
168 let old_value = self.engine.read_cell_value(sheet, row, col);
169 let old_formula = self.engine.read_cell_formula_ast(sheet, row, col);
170 let addr = self.addr_for(sheet, row, col);
171 let Some(log_ptr) = self.log else {
172 return Err(crate::engine::EditorError::TransactionFailed {
173 reason: "action_with_logger: missing ChangeLog".to_string(),
174 });
175 };
176
177 let delta_old = if self.arrow_undo.is_some() {
178 if old_formula.is_some() {
179 None
180 } else {
181 Some(old_value.clone().unwrap_or(LiteralValue::Empty))
182 }
183 } else {
184 None
185 };
186 let start_len = unsafe { (&*log_ptr).len() };
187
188 let log = unsafe { &mut *log_ptr };
190 self.engine.edit_with_logger(log, |editor| {
191 editor.set_cell_formula(addr, ast.clone());
192 });
193 log.patch_last_cell_event_old_state(addr, old_value, old_formula);
194
195 if let Some(undo_ptr) = self.arrow_undo {
196 let new_events = &unsafe { (&*log_ptr).events() }[start_len..];
197 let undo = unsafe { &mut *undo_ptr };
198 self.engine
199 .record_spill_ops_into_arrow_undo(undo, new_events);
200 let delta_new: Option<LiteralValue> = None;
201 let sheet_id = self.engine.graph.sheet_id_mut(sheet);
202 let row0 = row.saturating_sub(1);
203 let col0 = col.saturating_sub(1);
204 undo.record_delta_cell(sheet_id, row0, col0, delta_old, delta_new);
205 }
206 Ok(())
207 } else {
208 self.engine
209 .set_cell_formula(sheet, row, col, ast)
210 .map_err(crate::engine::EditorError::from)
211 }
212 }
213
214 #[inline]
215 pub fn insert_rows(
216 &mut self,
217 sheet: &str,
218 before: u32,
219 count: u32,
220 ) -> Result<crate::engine::ShiftSummary, crate::engine::EditorError> {
221 if self.log.is_some() {
222 let Some(log_ptr) = self.log else {
223 return Err(crate::engine::EditorError::TransactionFailed {
224 reason: "action_atomic: missing ChangeLog".to_string(),
225 });
226 };
227
228 let sheet_id = self.engine.graph.sheet_id_mut(sheet);
229 let before0 = before.saturating_sub(1);
230
231 let summary = {
233 let log = unsafe { &mut *log_ptr };
234 let mut out: Result<crate::engine::ShiftSummary, crate::engine::EditorError> =
235 Ok(crate::engine::ShiftSummary::default());
236 self.engine.edit_with_logger(log, |editor| {
237 out = editor.insert_rows(sheet_id, before0, count);
238 });
239 out?
240 };
241
242 self.engine.ensure_arrow_sheet(sheet);
244 if let Some(asheet) = self.engine.arrow_sheets.sheet_mut(sheet) {
245 asheet.insert_rows(before0 as usize, count as usize);
246 }
247 if let Some(undo_ptr) = self.arrow_undo {
248 unsafe { &mut *undo_ptr }.record_insert_rows(sheet_id, before0, count);
249 }
250 Ok(summary)
251 } else {
252 self.engine.insert_rows(sheet, before, count)
253 }
254 }
255
256 #[inline]
257 pub fn delete_rows(
258 &mut self,
259 sheet: &str,
260 start: u32,
261 count: u32,
262 ) -> Result<crate::engine::ShiftSummary, crate::engine::EditorError> {
263 if self.atomic_policy {
264 return Err(crate::engine::EditorError::TransactionUnsupported {
265 reason:
266 "delete_rows is not supported inside atomic actions (conservative rollback policy)"
267 .to_string(),
268 });
269 }
270 self.engine.delete_rows(sheet, start, count)
271 }
272
273 #[inline]
274 pub fn insert_columns(
275 &mut self,
276 sheet: &str,
277 before: u32,
278 count: u32,
279 ) -> Result<crate::engine::ShiftSummary, crate::engine::EditorError> {
280 if self.log.is_some() {
281 let Some(log_ptr) = self.log else {
282 return Err(crate::engine::EditorError::TransactionFailed {
283 reason: "action_atomic: missing ChangeLog".to_string(),
284 });
285 };
286
287 let sheet_id = self.engine.graph.sheet_id_mut(sheet);
288 let before0 = before.saturating_sub(1);
289
290 let summary = {
291 let log = unsafe { &mut *log_ptr };
292 let mut out: Result<crate::engine::ShiftSummary, crate::engine::EditorError> =
293 Ok(crate::engine::ShiftSummary::default());
294 self.engine.edit_with_logger(log, |editor| {
295 out = editor.insert_columns(sheet_id, before0, count);
296 });
297 out?
298 };
299
300 self.engine.ensure_arrow_sheet(sheet);
301 if let Some(asheet) = self.engine.arrow_sheets.sheet_mut(sheet) {
302 asheet.insert_columns(before0 as usize, count as usize);
303 }
304 if let Some(undo_ptr) = self.arrow_undo {
305 unsafe { &mut *undo_ptr }.record_insert_cols(sheet_id, before0, count);
306 }
307 Ok(summary)
308 } else {
309 self.engine.insert_columns(sheet, before, count)
310 }
311 }
312
313 #[inline]
314 pub fn delete_columns(
315 &mut self,
316 sheet: &str,
317 start: u32,
318 count: u32,
319 ) -> Result<crate::engine::ShiftSummary, crate::engine::EditorError> {
320 if self.atomic_policy {
321 return Err(crate::engine::EditorError::TransactionUnsupported {
322 reason:
323 "delete_columns is not supported inside atomic actions (conservative rollback policy)"
324 .to_string(),
325 });
326 }
327 self.engine.delete_columns(sheet, start, count)
328 }
329
330 #[inline]
335 pub fn action<T>(
336 &mut self,
337 name: impl AsRef<str>,
338 f: impl FnOnce(&mut EngineAction<'_, R>) -> Result<T, crate::engine::EditorError>,
339 ) -> Result<T, crate::engine::EditorError> {
340 self.engine.action(name, f)
341 }
342}
343
344struct ActionDepthGuard<'a, R> {
345 engine: *mut Engine<R>,
346 _marker: std::marker::PhantomData<&'a mut Engine<R>>,
347}
348
349impl<'a, R> Drop for ActionDepthGuard<'a, R> {
350 fn drop(&mut self) {
351 unsafe {
354 let e = &mut *self.engine;
355 e.action_depth = e.action_depth.saturating_sub(1);
356 }
357 }
358}
359
360#[derive(Default)]
361struct SourceCache {
362 scalars: FxHashMap<(String, Option<u64>), LiteralValue>,
363 tables: FxHashMap<(String, Option<u64>), Arc<dyn crate::traits::Table>>,
364}
365
366struct SourceCacheSession {
367 cache: Arc<std::sync::RwLock<SourceCache>>,
368}
369
370impl Drop for SourceCacheSession {
371 fn drop(&mut self) {
372 if let Ok(mut g) = self.cache.write() {
373 *g = SourceCache::default();
374 }
375 }
376}
377
378#[derive(Debug)]
379pub struct EvalResult {
380 pub computed_vertices: usize,
381 pub cycle_errors: usize,
382 pub elapsed: std::time::Duration,
383}
384
385#[derive(Debug)]
387pub struct RecalcPlan {
388 schedule: crate::engine::Schedule,
389}
390
391impl RecalcPlan {
392 pub fn layer_count(&self) -> usize {
393 self.schedule.layers.len()
394 }
395}
396
397#[cfg(test)]
398pub(crate) mod criteria_mask_test_hooks {
399 use std::cell::Cell;
400
401 thread_local! {
402 static TEXT_SEGMENTS_TOTAL: Cell<usize> = const { Cell::new(0) };
403 static TEXT_SEGMENTS_ALL_NULL: Cell<usize> = const { Cell::new(0) };
404 }
405
406 pub fn reset_text_segment_counters() {
407 TEXT_SEGMENTS_TOTAL.with(|c| c.set(0));
408 TEXT_SEGMENTS_ALL_NULL.with(|c| c.set(0));
409 }
410
411 pub fn text_segment_counters() -> (usize, usize) {
412 let a = TEXT_SEGMENTS_TOTAL.with(|c| c.get());
413 let b = TEXT_SEGMENTS_ALL_NULL.with(|c| c.get());
414 (a, b)
415 }
416
417 pub(crate) fn inc_total() {
418 TEXT_SEGMENTS_TOTAL.with(|c| c.set(c.get() + 1));
419 }
420 pub(crate) fn inc_all_null() {
421 TEXT_SEGMENTS_ALL_NULL.with(|c| c.set(c.get() + 1));
422 }
423}
424
425fn compute_criteria_mask(
426 view: &RangeView<'_>,
427 col_in_view: usize,
428 pred: &crate::args::CriteriaPredicate,
429) -> Option<std::sync::Arc<arrow_array::BooleanArray>> {
430 use crate::compute_prelude::{boolean, cmp, concat_arrays};
431 use arrow::compute::kernels::comparison::{ilike, nilike};
432 use arrow_array::{
433 Array as _, ArrayRef, BooleanArray, Float64Array, StringArray, builder::BooleanBuilder,
434 };
435
436 fn apply_numeric_pred(
438 chunk: &Float64Array,
439 pred: &crate::args::CriteriaPredicate,
440 ) -> Option<BooleanArray> {
441 match pred {
442 crate::args::CriteriaPredicate::Gt(n) => {
443 cmp::gt(chunk, &Float64Array::new_scalar(*n)).ok()
444 }
445 crate::args::CriteriaPredicate::Ge(n) => {
446 cmp::gt_eq(chunk, &Float64Array::new_scalar(*n)).ok()
447 }
448 crate::args::CriteriaPredicate::Lt(n) => {
449 cmp::lt(chunk, &Float64Array::new_scalar(*n)).ok()
450 }
451 crate::args::CriteriaPredicate::Le(n) => {
452 cmp::lt_eq(chunk, &Float64Array::new_scalar(*n)).ok()
453 }
454 crate::args::CriteriaPredicate::Eq(v) => match v {
455 formualizer_common::LiteralValue::Number(x) => {
456 cmp::eq(chunk, &Float64Array::new_scalar(*x)).ok()
457 }
458 formualizer_common::LiteralValue::Int(i) => {
459 cmp::eq(chunk, &Float64Array::new_scalar(*i as f64)).ok()
460 }
461 _ => None,
462 },
463 crate::args::CriteriaPredicate::Ne(v) => match v {
464 formualizer_common::LiteralValue::Number(x) => {
465 cmp::neq(chunk, &Float64Array::new_scalar(*x)).ok()
466 }
467 formualizer_common::LiteralValue::Int(i) => {
468 cmp::neq(chunk, &Float64Array::new_scalar(*i as f64)).ok()
469 }
470 _ => None,
471 },
472 _ => None,
473 }
474 }
475
476 let is_numeric_pred = matches!(
478 pred,
479 crate::args::CriteriaPredicate::Gt(_)
480 | crate::args::CriteriaPredicate::Ge(_)
481 | crate::args::CriteriaPredicate::Lt(_)
482 | crate::args::CriteriaPredicate::Le(_)
483 | crate::args::CriteriaPredicate::Eq(formualizer_common::LiteralValue::Number(_))
484 | crate::args::CriteriaPredicate::Eq(formualizer_common::LiteralValue::Int(_))
485 | crate::args::CriteriaPredicate::Ne(formualizer_common::LiteralValue::Number(_))
486 | crate::args::CriteriaPredicate::Ne(formualizer_common::LiteralValue::Int(_))
487 );
488
489 if is_numeric_pred {
493 let mut bool_parts: Vec<BooleanArray> = Vec::new();
494 for res in view.numbers_slices() {
495 let (_rs, _rl, cols_seg) = res.ok()?;
496 if col_in_view < cols_seg.len() {
497 let chunk = cols_seg[col_in_view].as_ref();
498 let mask = apply_numeric_pred(chunk, pred)?;
499 bool_parts.push(mask);
500 }
501 }
502
503 if bool_parts.is_empty() {
504 return None;
505 } else if bool_parts.len() == 1 {
506 return Some(std::sync::Arc::new(bool_parts.remove(0)));
507 } else {
508 let anys: Vec<&dyn arrow_array::Array> = bool_parts
510 .iter()
511 .map(|a| a as &dyn arrow_array::Array)
512 .collect();
513 let conc: ArrayRef = concat_arrays(&anys).ok()?;
514 let ba = conc.as_any().downcast_ref::<BooleanArray>()?.clone();
515 return Some(std::sync::Arc::new(ba));
516 }
517 }
518
519 let (text_kind, text_pat, empty_special) = match pred {
522 crate::args::CriteriaPredicate::Eq(formualizer_common::LiteralValue::Text(t)) => {
523 (0u8, t.to_ascii_lowercase(), t.is_empty())
524 }
525 crate::args::CriteriaPredicate::Ne(formualizer_common::LiteralValue::Text(t)) => {
526 (1u8, t.to_ascii_lowercase(), false)
527 }
528 crate::args::CriteriaPredicate::TextLike {
529 pattern,
530 case_insensitive,
531 } => {
532 let p = if *case_insensitive {
533 pattern.to_ascii_lowercase()
534 } else {
535 pattern.clone()
536 };
537 (2u8, p.replace('*', "%").replace('?', "_"), false)
538 }
539 _ => return None,
540 };
541
542 let pat = StringArray::new_scalar(text_pat);
543 let mut bool_parts: Vec<BooleanArray> = Vec::new();
544
545 for res in view.iter_row_chunks() {
546 let cs = res.ok()?;
547 if cs.row_len == 0 {
548 continue;
549 }
550 #[cfg(test)]
551 criteria_mask_test_hooks::inc_total();
552
553 let slices = view.slice_lowered_text(cs.row_start, cs.row_len);
554 if col_in_view >= slices.len() {
555 return None;
556 }
557
558 let seg_opt = slices[col_in_view].as_ref().map(|a| a.as_ref());
559 let seg = match seg_opt {
560 Some(s) => s,
561 None => {
562 #[cfg(test)]
563 criteria_mask_test_hooks::inc_all_null();
564 if text_kind == 0 && empty_special {
565 let mut bb = BooleanBuilder::with_capacity(cs.row_len);
567 bb.append_n(cs.row_len, true);
568 bool_parts.push(bb.finish());
569 } else {
570 bool_parts.push(BooleanArray::new_null(cs.row_len));
572 }
573 continue;
574 }
575 };
576
577 let seg_sa = seg.as_any().downcast_ref::<StringArray>()?;
578 let mut m = match text_kind {
579 0 => ilike(seg_sa, &pat).ok()?,
580 1 => nilike(seg_sa, &pat).ok()?,
581 2 => ilike(seg_sa, &pat).ok()?,
582 _ => return None,
583 };
584
585 if text_kind == 0 && empty_special {
586 let mut bb = BooleanBuilder::with_capacity(seg_sa.len());
588 for i in 0..seg_sa.len() {
589 bb.append_value(seg_sa.is_null(i));
590 }
591 let nulls = bb.finish();
592 m = boolean::or_kleene(&m, &nulls).ok()?;
593 }
594
595 bool_parts.push(m);
596 }
597
598 if bool_parts.is_empty() {
599 None
600 } else if bool_parts.len() == 1 {
601 Some(std::sync::Arc::new(bool_parts.remove(0)))
602 } else {
603 let anys: Vec<&dyn arrow_array::Array> = bool_parts
604 .iter()
605 .map(|a| a as &dyn arrow_array::Array)
606 .collect();
607 let conc: ArrayRef = concat_arrays(&anys).ok()?;
608 let ba = conc.as_any().downcast_ref::<BooleanArray>()?.clone();
609 Some(std::sync::Arc::new(ba))
610 }
611}
612
613#[derive(Debug, Clone)]
614pub struct LayerInfo {
615 pub vertex_count: usize,
616 pub parallel_eligible: bool,
617 pub sample_cells: Vec<String>, }
619
620#[derive(Debug, Clone)]
621pub struct EvalPlan {
622 pub total_vertices_to_evaluate: usize,
623 pub layers: Vec<LayerInfo>,
624 pub cycles_detected: usize,
625 pub dirty_count: usize,
626 pub volatile_count: usize,
627 pub parallel_enabled: bool,
628 pub estimated_parallel_layers: usize,
629 pub target_cells: Vec<String>,
630}
631
632impl<R> Engine<R>
633where
634 R: EvaluationContext,
635{
636 pub fn new(resolver: R, config: EvalConfig) -> Self {
637 crate::builtins::load_builtins();
638
639 let clock = config.deterministic_mode.build_clock().unwrap_or_else(|_| {
640 Arc::new(crate::timezone::SystemClock::new(
641 crate::timezone::TimeZoneSpec::default(),
642 ))
643 });
644
645 let thread_pool = if config.enable_parallel {
647 let mut builder = ThreadPoolBuilder::new();
648 if let Some(max_threads) = config.max_threads {
649 builder = builder.num_threads(max_threads);
650 }
651
652 match builder.build() {
653 Ok(pool) => Some(Arc::new(pool)),
654 Err(_) => {
655 None
657 }
658 }
659 } else {
660 None
661 };
662
663 let mut engine = Self {
664 graph: DependencyGraph::new_with_config(config.clone()),
665 resolver,
666 config,
667 clock,
668 thread_pool,
669 recalc_epoch: 0,
670 snapshot_id: std::sync::atomic::AtomicU64::new(1),
671 spill_mgr: ShimSpillManager::default(),
672 arrow_sheets: SheetStore::default(),
673 has_edited: false,
674 overlay_compactions: 0,
675 computed_overlay_bytes_estimate: 0,
676 computed_overlay_mirroring_disabled: false,
677 force_materialize_range_views: false,
678 row_bounds_cache: std::sync::RwLock::new(None),
679 source_cache: Arc::new(std::sync::RwLock::new(SourceCache::default())),
680 staged_formulas: std::collections::HashMap::new(),
681 active_cancel_flag: None,
682 action_depth: 0,
683 };
684 engine.config.arrow_storage_enabled = true;
686 engine.config.delta_overlay_enabled = true;
687 engine.config.write_formula_overlay_enabled = true;
688 let default_sheet = engine.graph.default_sheet_name().to_string();
689 engine.ensure_arrow_sheet(&default_sheet);
690 engine
691 }
692
693 pub fn with_thread_pool(
695 resolver: R,
696 config: EvalConfig,
697 thread_pool: Arc<rayon::ThreadPool>,
698 ) -> Self {
699 crate::builtins::load_builtins();
700 let clock = config.deterministic_mode.build_clock().unwrap_or_else(|_| {
701 Arc::new(crate::timezone::SystemClock::new(
702 crate::timezone::TimeZoneSpec::default(),
703 ))
704 });
705 let mut engine = Self {
706 graph: DependencyGraph::new_with_config(config.clone()),
707 resolver,
708 config,
709 clock,
710 thread_pool: Some(thread_pool),
711 recalc_epoch: 0,
712 snapshot_id: std::sync::atomic::AtomicU64::new(1),
713 spill_mgr: ShimSpillManager::default(),
714 arrow_sheets: SheetStore::default(),
715 has_edited: false,
716 overlay_compactions: 0,
717 computed_overlay_bytes_estimate: 0,
718 computed_overlay_mirroring_disabled: false,
719 force_materialize_range_views: false,
720 row_bounds_cache: std::sync::RwLock::new(None),
721 source_cache: Arc::new(std::sync::RwLock::new(SourceCache::default())),
722 staged_formulas: std::collections::HashMap::new(),
723 active_cancel_flag: None,
724 action_depth: 0,
725 };
726 engine.config.arrow_storage_enabled = true;
728 engine.config.delta_overlay_enabled = true;
729 engine.config.write_formula_overlay_enabled = true;
730 let default_sheet = engine.graph.default_sheet_name().to_string();
731 engine.ensure_arrow_sheet(&default_sheet);
732 engine
733 }
734
735 fn clear_source_cache(&self) {
736 if let Ok(mut g) = self.source_cache.write() {
737 *g = SourceCache::default();
738 }
739 }
740
741 fn source_cache_session(&self) -> SourceCacheSession {
742 self.clear_source_cache();
743 SourceCacheSession {
744 cache: self.source_cache.clone(),
745 }
746 }
747
748 fn resolve_source_scalar_cached(
749 &self,
750 name: &str,
751 version: Option<u64>,
752 ) -> Result<LiteralValue, ExcelError> {
753 let key = (name.to_string(), version);
754 if let Ok(mut g) = self.source_cache.write() {
755 if let Some(v) = g.scalars.get(&key) {
756 return Ok(v.clone());
757 }
758
759 let v = self.resolver.resolve_source_scalar(name).map_err(|err| {
760 if matches!(err.kind, ExcelErrorKind::Name | ExcelErrorKind::NImpl) {
761 ExcelError::new(ExcelErrorKind::Ref)
762 .with_message(format!("Unresolved source scalar: {name}"))
763 } else {
764 err
765 }
766 })?;
767 g.scalars.insert(key, v.clone());
768 Ok(v)
769 } else {
770 self.resolver.resolve_source_scalar(name).map_err(|err| {
771 if matches!(err.kind, ExcelErrorKind::Name | ExcelErrorKind::NImpl) {
772 ExcelError::new(ExcelErrorKind::Ref)
773 .with_message(format!("Unresolved source scalar: {name}"))
774 } else {
775 err
776 }
777 })
778 }
779 }
780
781 fn resolve_source_table_cached(
782 &self,
783 name: &str,
784 version: Option<u64>,
785 ) -> Result<Arc<dyn crate::traits::Table>, ExcelError> {
786 let key = (name.to_string(), version);
787 if let Ok(mut g) = self.source_cache.write() {
788 if let Some(t) = g.tables.get(&key) {
789 return Ok(t.clone());
790 }
791
792 let t = self.resolver.resolve_source_table(name).map_err(|err| {
793 if matches!(err.kind, ExcelErrorKind::Name | ExcelErrorKind::NImpl) {
794 ExcelError::new(ExcelErrorKind::Ref)
795 .with_message(format!("Unresolved source table: {name}"))
796 } else {
797 err
798 }
799 })?;
800 let t: Arc<dyn crate::traits::Table> = Arc::from(t);
801 g.tables.insert(key, t.clone());
802 Ok(t)
803 } else {
804 self.resolver
805 .resolve_source_table(name)
806 .map_err(|err| {
807 if matches!(err.kind, ExcelErrorKind::Name | ExcelErrorKind::NImpl) {
808 ExcelError::new(ExcelErrorKind::Ref)
809 .with_message(format!("Unresolved source table: {name}"))
810 } else {
811 err
812 }
813 })
814 .map(Arc::from)
815 }
816 }
817
818 fn source_table_to_range_view(
819 &self,
820 table: &dyn crate::traits::Table,
821 spec: &Option<formualizer_parse::parser::TableSpecifier>,
822 ) -> Result<RangeView<'static>, ExcelError> {
823 use formualizer_parse::parser::{SpecialItem, TableSpecifier};
824
825 let owned = match spec {
826 Some(TableSpecifier::Column(c)) => {
827 let c = c.trim();
828 if c == "@" || c.contains('[') || c.contains(']') || c.contains(',') {
829 return Err(ExcelError::new(ExcelErrorKind::NImpl).with_message(
830 "Complex structured references not yet supported".to_string(),
831 ));
832 }
833 table.get_column(c)?.materialise().into_owned()
834 }
835 Some(TableSpecifier::ColumnRange(start, end)) => {
836 let cols = table.columns();
837 let start = start.trim();
838 let end = end.trim();
839 let start_idx = cols.iter().position(|n| n.eq_ignore_ascii_case(start));
840 let end_idx = cols.iter().position(|n| n.eq_ignore_ascii_case(end));
841 if let (Some(mut si), Some(mut ei)) = (start_idx, end_idx) {
842 if si > ei {
843 std::mem::swap(&mut si, &mut ei);
844 }
845 let h = table.data_height();
846 let w = ei - si + 1;
847 let mut rows = vec![vec![LiteralValue::Empty; w]; h];
848 for (offset, ci) in (si..=ei).enumerate() {
849 let cname = &cols[ci];
850 let col_range = table.get_column(cname)?;
851 let (rh, _) = col_range.dimensions();
852 for (r, row) in rows.iter_mut().enumerate().take(h.min(rh)) {
853 row[offset] = col_range.get(r, 0)?;
854 }
855 }
856 rows
857 } else {
858 return Err(ExcelError::new(ExcelErrorKind::Ref)
859 .with_message("Column range refers to unknown column(s)".to_string()));
860 }
861 }
862 Some(TableSpecifier::SpecialItem(SpecialItem::Headers))
863 | Some(TableSpecifier::Headers) => table
864 .headers_row()
865 .map(|r| r.materialise().into_owned())
866 .unwrap_or_default(),
867 Some(TableSpecifier::SpecialItem(SpecialItem::Totals))
868 | Some(TableSpecifier::Totals) => table
869 .totals_row()
870 .map(|r| r.materialise().into_owned())
871 .unwrap_or_default(),
872 Some(TableSpecifier::SpecialItem(SpecialItem::Data)) | Some(TableSpecifier::Data) => {
873 table
874 .data_body()
875 .map(|r| r.materialise().into_owned())
876 .unwrap_or_default()
877 }
878 Some(TableSpecifier::SpecialItem(SpecialItem::All)) | Some(TableSpecifier::All) => {
879 let mut out: Vec<Vec<LiteralValue>> = Vec::new();
880 if let Some(h) = table.headers_row() {
881 out.extend(h.iter_rows());
882 }
883 if let Some(body) = table.data_body() {
884 out.extend(body.iter_rows());
885 }
886 if let Some(tr) = table.totals_row() {
887 out.extend(tr.iter_rows());
888 }
889 out
890 }
891 Some(TableSpecifier::SpecialItem(SpecialItem::ThisRow)) => {
892 return Err(ExcelError::new(ExcelErrorKind::NImpl).with_message(
893 "@ (This Row) requires table-aware context; not yet supported".to_string(),
894 ));
895 }
896 Some(TableSpecifier::Row(_)) | Some(TableSpecifier::Combination(_)) => {
897 return Err(ExcelError::new(ExcelErrorKind::NImpl)
898 .with_message("Complex structured references not yet supported".to_string()));
899 }
900 None => {
901 return Err(ExcelError::new(ExcelErrorKind::NImpl)
902 .with_message("Table reference without specifier is unsupported".to_string()));
903 }
904 };
905
906 Ok(RangeView::from_owned_rows(owned, self.config.date_system))
907 }
908
909 pub fn default_sheet_id(&self) -> SheetId {
910 self.graph.default_sheet_id()
911 }
912
913 pub fn default_sheet_name(&self) -> &str {
914 self.graph.default_sheet_name()
915 }
916
917 pub fn set_workbook_seed(&mut self, seed: u64) {
919 self.config.workbook_seed = seed;
920 }
921
922 pub fn set_volatile_level(&mut self, level: crate::traits::VolatileLevel) {
924 self.config.volatile_level = level;
925 }
926
927 pub fn set_deterministic_mode(
929 &mut self,
930 mode: crate::engine::DeterministicMode,
931 ) -> Result<(), ExcelError> {
932 let clock = mode.build_clock()?;
933 self.config.deterministic_mode = mode;
934 self.clock = clock;
935 Ok(())
936 }
937
938 fn validate_deterministic_mode(&self) -> Result<(), ExcelError> {
939 self.config.deterministic_mode.validate()
940 }
941
942 pub fn sheet_id(&self, name: &str) -> Option<SheetId> {
943 self.graph.sheet_id(name)
944 }
945
946 pub fn sheet_id_mut(&mut self, name: &str) -> SheetId {
947 self.add_sheet(name)
948 .unwrap_or_else(|_| self.graph.sheet_id_mut(name))
949 }
950
951 pub fn sheet_name(&self, id: SheetId) -> &str {
952 self.graph.sheet_name(id)
953 }
954
955 pub fn add_sheet(&mut self, name: &str) -> Result<SheetId, ExcelError> {
956 let id = self.graph.add_sheet(name)?;
957 self.ensure_arrow_sheet(name);
958 Ok(id)
959 }
960
961 fn ensure_arrow_sheet(&mut self, name: &str) {
962 if self.arrow_sheets.sheet(name).is_some() {
963 return;
964 }
965 self.arrow_sheets
966 .sheets
967 .push(crate::arrow_store::ArrowSheet {
968 name: std::sync::Arc::<str>::from(name),
969 columns: Vec::new(),
970 nrows: 0,
971 chunk_starts: Vec::new(),
972 chunk_rows: 32 * 1024,
973 });
974 }
975
976 pub fn remove_sheet(&mut self, sheet_id: SheetId) -> Result<(), ExcelError> {
977 let name = self.graph.sheet_name(sheet_id).to_string();
978 self.graph.remove_sheet(sheet_id)?;
979 self.arrow_sheets.sheets.retain(|s| s.name.as_ref() != name);
980 Ok(())
981 }
982
983 pub fn rename_sheet(&mut self, sheet_id: SheetId, new_name: &str) -> Result<(), ExcelError> {
984 let old_name = self.graph.sheet_name(sheet_id).to_string();
985 self.graph.rename_sheet(sheet_id, new_name)?;
986 self.ensure_arrow_sheet(&old_name);
987 if let Some(asheet) = self.arrow_sheets.sheet_mut(&old_name) {
988 asheet.name = std::sync::Arc::<str>::from(new_name);
989 }
990 Ok(())
991 }
992
993 pub fn named_ranges_iter(
994 &self,
995 ) -> impl Iterator<Item = (&String, &crate::engine::named_range::NamedRange)> {
996 self.graph.named_ranges_iter()
997 }
998
999 pub fn sheet_named_ranges_iter(
1000 &self,
1001 ) -> impl Iterator<Item = (&(SheetId, String), &crate::engine::named_range::NamedRange)> {
1002 self.graph.sheet_named_ranges_iter()
1003 }
1004
1005 pub fn resolve_name_entry(
1006 &self,
1007 name: &str,
1008 current_sheet: SheetId,
1009 ) -> Option<&crate::engine::named_range::NamedRange> {
1010 self.graph.resolve_name_entry(name, current_sheet)
1011 }
1012
1013 pub fn define_name(
1014 &mut self,
1015 name: &str,
1016 definition: NamedDefinition,
1017 scope: NameScope,
1018 ) -> Result<(), ExcelError> {
1019 self.graph.define_name(name, definition, scope)
1020 }
1021
1022 pub fn update_name(
1023 &mut self,
1024 name: &str,
1025 definition: NamedDefinition,
1026 scope: NameScope,
1027 ) -> Result<(), ExcelError> {
1028 self.graph.update_name(name, definition, scope)
1029 }
1030
1031 pub fn delete_name(&mut self, name: &str, scope: NameScope) -> Result<(), ExcelError> {
1032 self.graph.delete_name(name, scope)
1033 }
1034
1035 pub fn define_table(
1036 &mut self,
1037 name: &str,
1038 range: crate::reference::RangeRef,
1039 header_row: bool,
1040 headers: Vec<String>,
1041 totals_row: bool,
1042 ) -> Result<(), ExcelError> {
1043 self.graph
1044 .define_table(name, range, header_row, headers, totals_row)
1045 }
1046
1047 pub fn define_source_scalar(
1048 &mut self,
1049 name: &str,
1050 version: Option<u64>,
1051 ) -> Result<(), ExcelError> {
1052 self.graph.define_source_scalar(name, version)
1053 }
1054
1055 pub fn define_source_table(
1056 &mut self,
1057 name: &str,
1058 version: Option<u64>,
1059 ) -> Result<(), ExcelError> {
1060 self.graph.define_source_table(name, version)
1061 }
1062
1063 pub fn set_source_scalar_version(
1064 &mut self,
1065 name: &str,
1066 version: Option<u64>,
1067 ) -> Result<(), ExcelError> {
1068 self.graph.set_source_scalar_version(name, version)
1069 }
1070
1071 pub fn set_source_table_version(
1072 &mut self,
1073 name: &str,
1074 version: Option<u64>,
1075 ) -> Result<(), ExcelError> {
1076 self.graph.set_source_table_version(name, version)
1077 }
1078
1079 pub fn invalidate_source(&mut self, name: &str) -> Result<(), ExcelError> {
1080 self.graph.invalidate_source(name)
1081 }
1082
1083 pub fn vertex_value(&self, vertex: VertexId) -> Option<LiteralValue> {
1084 self.graph.get_value(vertex)
1085 }
1086
1087 pub fn graph_cell_value(&self, sheet: &str, row: u32, col: u32) -> Option<LiteralValue> {
1088 self.graph.get_cell_value(sheet, row, col)
1089 }
1090
1091 pub fn vertex_for_cell(&self, cell: &CellRef) -> Option<VertexId> {
1092 self.graph.get_vertex_for_cell(cell)
1093 }
1094
1095 pub fn evaluation_vertices(&self) -> Vec<VertexId> {
1096 self.graph.get_evaluation_vertices()
1097 }
1098
1099 pub fn set_first_load_assume_new(&mut self, enabled: bool) {
1100 self.graph.set_first_load_assume_new(enabled);
1101 }
1102
1103 pub fn reset_ensure_touched(&mut self) {
1104 self.graph.reset_ensure_touched();
1105 }
1106
1107 pub fn finalize_sheet_index(&mut self, sheet: &str) {
1108 self.graph.finalize_sheet_index(sheet);
1109 }
1110
1111 pub fn action<T>(
1120 &mut self,
1121 name: impl AsRef<str>,
1122 f: impl FnOnce(&mut EngineAction<'_, R>) -> Result<T, crate::engine::EditorError>,
1123 ) -> Result<T, crate::engine::EditorError> {
1124 if self.action_depth != 0 {
1125 return Err(crate::engine::EditorError::TransactionFailed {
1126 reason: "Nested Engine::action calls are not supported (ticket 614: commit-only surface)"
1127 .to_string(),
1128 });
1129 }
1130
1131 self.action_depth = 1;
1132 let engine_ptr: *mut Engine<R> = self;
1133 let _guard = ActionDepthGuard {
1134 engine: engine_ptr,
1135 _marker: std::marker::PhantomData,
1136 };
1137
1138 let mut tx = EngineAction {
1139 engine: self,
1140 name: name.as_ref().to_string(),
1141 log: None,
1142 arrow_undo: None,
1143 atomic_policy: false,
1144 };
1145 f(&mut tx)
1146 }
1147
1148 pub fn action_atomic<T>(
1152 &mut self,
1153 name: impl Into<String>,
1154 f: impl FnOnce(&mut EngineAction<'_, R>) -> Result<T, crate::engine::EditorError>,
1155 ) -> Result<T, crate::engine::EditorError> {
1156 let (v, _j) = self.action_atomic_journal(name, f)?;
1157 Ok(v)
1158 }
1159
1160 pub fn action_atomic_journal<T>(
1162 &mut self,
1163 name: impl Into<String>,
1164 f: impl FnOnce(&mut EngineAction<'_, R>) -> Result<T, crate::engine::EditorError>,
1165 ) -> Result<(T, crate::engine::ActionJournal), crate::engine::EditorError> {
1166 if self.action_depth != 0 {
1167 return Err(crate::engine::EditorError::TransactionFailed {
1168 reason: "Nested Engine::action calls are not supported (deterministic rule)"
1169 .to_string(),
1170 });
1171 }
1172
1173 self.action_depth = 1;
1174 let engine_ptr: *mut Engine<R> = self;
1175 let _guard = ActionDepthGuard {
1176 engine: engine_ptr,
1177 _marker: std::marker::PhantomData,
1178 };
1179
1180 let name_str = name.into();
1181 let mut log = crate::engine::ChangeLog::new();
1182 let start_len = log.len();
1183 self.action_atomic_impl(&mut log, start_len, name_str, f)
1184 }
1185
1186 fn action_atomic_impl<T>(
1187 &mut self,
1188 log: &mut crate::engine::ChangeLog,
1189 start_len: usize,
1190 name: String,
1191 f: impl FnOnce(&mut EngineAction<'_, R>) -> Result<T, crate::engine::EditorError>,
1192 ) -> Result<(T, crate::engine::ActionJournal), crate::engine::EditorError> {
1193 let mut arrow_undo = crate::engine::ArrowUndoBatch::default();
1194 let arrow_ptr: *mut crate::engine::ArrowUndoBatch = &mut arrow_undo;
1195
1196 let log_ptr: *mut crate::engine::ChangeLog = log;
1197 let mut tx = EngineAction {
1198 engine: self,
1199 name: name.clone(),
1200 log: Some(log_ptr),
1201 arrow_undo: Some(arrow_ptr),
1202 atomic_policy: true,
1203 };
1204
1205 let res = f(&mut tx);
1206
1207 let graph_events: Vec<crate::engine::ChangeEvent> =
1209 unsafe { (&*log_ptr).events() }[start_len..].to_vec();
1210 let graph_batch = crate::engine::GraphUndoBatch {
1211 events: graph_events,
1212 };
1213 let affected_cells = arrow_undo.ops.len();
1214 let journal = crate::engine::ActionJournal {
1215 name,
1216 graph: graph_batch,
1217 arrow: arrow_undo,
1218 affected_cells,
1219 };
1220
1221 match res {
1222 Ok(v) => {
1223 if !journal.graph.is_empty() || !journal.arrow.is_empty() {
1224 self.mark_data_edited();
1225 }
1226 Ok((v, journal))
1227 }
1228 Err(e) => {
1229 if let Err(rb) = self.rollback_from_action_journal(&journal) {
1230 return Err(crate::engine::EditorError::TransactionFailed {
1231 reason: format!(
1232 "Engine::action_atomic rollback failed after error '{e}': {rb}"
1233 ),
1234 });
1235 }
1236 Err(e)
1237 }
1238 }
1239 }
1240
1241 pub fn action_with_logger<T>(
1248 &mut self,
1249 log: &mut crate::engine::ChangeLog,
1250 name: impl AsRef<str>,
1251 f: impl FnOnce(&mut EngineAction<'_, R>) -> Result<T, crate::engine::EditorError>,
1252 ) -> Result<T, crate::engine::EditorError> {
1253 if self.action_depth != 0 {
1254 return Err(crate::engine::EditorError::TransactionFailed {
1255 reason: "Nested Engine::action calls are not supported (deterministic rule)"
1256 .to_string(),
1257 });
1258 }
1259
1260 self.action_depth = 1;
1261 let engine_ptr: *mut Engine<R> = self;
1262 let _guard = ActionDepthGuard {
1263 engine: engine_ptr,
1264 _marker: std::marker::PhantomData,
1265 };
1266
1267 let start_len = log.len();
1268 let name_str = name.as_ref().to_string();
1269 log.begin_compound(name_str.clone());
1270
1271 let res = self.action_atomic_impl(log, start_len, name_str, f);
1274
1275 match res {
1276 Ok((v, _journal)) => {
1277 log.end_compound();
1278 Ok(v)
1279 }
1280 Err(e) => {
1281 log.end_compound();
1283 log.truncate(start_len);
1284 Err(e)
1285 }
1286 }
1287 }
1288
1289 fn rollback_from_action_journal(
1290 &mut self,
1291 journal: &crate::engine::ActionJournal,
1292 ) -> Result<(), crate::engine::EditorError> {
1293 journal.graph.undo(&mut self.graph)?;
1295 self.apply_arrow_undo_batch(&journal.arrow, true);
1297 Ok(())
1298 }
1299
1300 fn rollback_from_change_events(
1301 &mut self,
1302 events: &[crate::engine::ChangeEvent],
1303 ) -> Result<(), crate::engine::EditorError> {
1304 use crate::engine::ChangeEvent;
1305
1306 {
1308 let mut editor = crate::engine::VertexEditor::new(&mut self.graph);
1309 let mut compound_stack: Vec<usize> = Vec::new();
1310 for ev in events.iter().rev() {
1311 match ev {
1312 ChangeEvent::CompoundEnd { depth } => compound_stack.push(*depth),
1313 ChangeEvent::CompoundStart { depth, .. } => {
1314 if compound_stack.last() == Some(depth) {
1315 compound_stack.pop();
1316 }
1317 }
1318 _ => {
1319 editor.apply_inverse(ev.clone())?;
1320 }
1321 }
1322 }
1323 }
1324
1325 for ev in events.iter().rev() {
1327 self.mirror_inverse_change_to_arrow(ev);
1328 }
1329
1330 Ok(())
1331 }
1332
1333 fn read_cell_formula_ast(&self, sheet: &str, row: u32, col: u32) -> Option<ASTNode> {
1334 let sheet_id = self.graph.sheet_id(sheet)?;
1335 let coord = Coord::from_excel(row, col, true, true);
1336 let cell = CellRef::new(sheet_id, coord);
1337 let vid = self.graph.get_vertex_for_cell(&cell)?;
1338 let ast_id = self.graph.get_formula_id(vid)?;
1339 self.graph
1340 .data_store()
1341 .retrieve_ast(ast_id, self.graph.sheet_reg())
1342 }
1343
1344 pub fn edit_with_logger<T>(
1345 &mut self,
1346 log: &mut crate::engine::ChangeLog,
1347 f: impl FnOnce(&mut crate::engine::VertexEditor) -> T,
1348 ) -> T {
1349 let start_len = log.len();
1351
1352 struct ArrowSpillReader<'a> {
1355 sheets: &'a crate::arrow_store::SheetStore,
1356 }
1357 impl crate::engine::graph::editor::vertex_editor::SpillValueReader for ArrowSpillReader<'_> {
1358 fn read_cell_value(
1359 &self,
1360 sheet: &str,
1361 row: u32,
1362 col: u32,
1363 ) -> Option<formualizer_common::LiteralValue> {
1364 use formualizer_common::LiteralValue;
1365 let asheet = self.sheets.sheet(sheet)?;
1366 let r0 = row.saturating_sub(1) as usize;
1367 let c0 = col.saturating_sub(1) as usize;
1368 let v = asheet.get_cell_value(r0, c0);
1369 if matches!(v, LiteralValue::Empty) {
1370 None
1371 } else {
1372 Some(v)
1373 }
1374 }
1375 }
1376
1377 let ret = {
1378 let spill_reader = ArrowSpillReader {
1379 sheets: &self.arrow_sheets,
1380 };
1381 let mut editor = crate::engine::VertexEditor::with_logger_and_spill_reader(
1382 &mut self.graph,
1383 log,
1384 &spill_reader,
1385 );
1386 f(&mut editor)
1387 };
1388
1389 for ev in &log.events()[start_len..] {
1392 self.mirror_forward_change_to_arrow(ev);
1393 }
1394
1395 ret
1396 }
1397
1398 pub fn undo_logged(
1399 &mut self,
1400 undo: &mut crate::engine::graph::editor::undo_engine::UndoEngine,
1401 log: &mut crate::engine::ChangeLog,
1402 ) -> Result<(), crate::engine::EditorError> {
1403 let batch = undo.undo(&mut self.graph, log)?;
1404 self.mirror_undo_batch_to_arrow(&batch);
1405 Ok(())
1406 }
1407
1408 pub fn redo_logged(
1409 &mut self,
1410 undo: &mut crate::engine::graph::editor::undo_engine::UndoEngine,
1411 log: &mut crate::engine::ChangeLog,
1412 ) -> Result<(), crate::engine::EditorError> {
1413 let batch = undo.redo(&mut self.graph, log)?;
1414 self.mirror_redo_batch_to_arrow(&batch);
1415 Ok(())
1416 }
1417
1418 pub fn undo_action(
1422 &mut self,
1423 undo: &mut crate::engine::graph::editor::undo_engine::UndoEngine,
1424 ) -> Result<(), crate::engine::EditorError> {
1425 let Some(journal) = undo.pop_undo_action() else {
1426 return Ok(());
1427 };
1428
1429 journal.graph.undo(&mut self.graph)?;
1430 self.apply_arrow_undo_batch(&journal.arrow, true);
1431
1432 undo.push_redo_action(journal);
1433 Ok(())
1434 }
1435
1436 pub fn redo_action(
1440 &mut self,
1441 undo: &mut crate::engine::graph::editor::undo_engine::UndoEngine,
1442 ) -> Result<(), crate::engine::EditorError> {
1443 let Some(journal) = undo.pop_redo_action() else {
1444 return Ok(());
1445 };
1446
1447 journal.graph.redo(&mut self.graph)?;
1448 self.apply_arrow_undo_batch(&journal.arrow, false);
1449
1450 undo.push_done_action(journal);
1451 Ok(())
1452 }
1453
1454 fn cellref_to_sheet_row_col(&self, addr: &crate::reference::CellRef) -> (String, u32, u32) {
1455 let sheet = self.graph.sheet_name(addr.sheet_id).to_string();
1456 let row = addr.coord.row() + 1;
1458 let col = addr.coord.col() + 1;
1459 (sheet, row, col)
1460 }
1461
1462 fn mirror_undo_batch_to_arrow(
1463 &mut self,
1464 batch: &[crate::engine::graph::editor::undo_engine::UndoBatchItem],
1465 ) {
1466 for item in batch.iter().rev() {
1468 self.mirror_inverse_change_to_arrow(&item.event);
1469 }
1470 }
1471
1472 fn mirror_redo_batch_to_arrow(
1473 &mut self,
1474 batch: &[crate::engine::graph::editor::undo_engine::UndoBatchItem],
1475 ) {
1476 for item in batch.iter() {
1478 self.mirror_forward_change_to_arrow(&item.event);
1479 }
1480 }
1481
1482 fn mirror_inverse_change_to_arrow(&mut self, ev: &crate::engine::ChangeEvent) {
1483 use crate::engine::ChangeEvent;
1484 use formualizer_common::LiteralValue;
1485
1486 match ev {
1487 ChangeEvent::SetValue {
1488 addr,
1489 old_value,
1490 old_formula,
1491 ..
1492 } => {
1493 let (sheet, row, col) = self.cellref_to_sheet_row_col(addr);
1494 if old_formula.is_some() {
1495 self.clear_delta_overlay_cell(&sheet, row, col);
1496 } else {
1497 let v = old_value.clone().unwrap_or(LiteralValue::Empty);
1498 self.mirror_value_to_overlay(&sheet, row, col, &v);
1499 }
1500 }
1501 ChangeEvent::SetFormula {
1502 addr,
1503 old_value,
1504 old_formula,
1505 ..
1506 } => {
1507 let (sheet, row, col) = self.cellref_to_sheet_row_col(addr);
1508 if old_formula.is_some() {
1509 self.clear_delta_overlay_cell(&sheet, row, col);
1510 } else {
1511 let v = old_value.clone().unwrap_or(LiteralValue::Empty);
1512 self.mirror_value_to_overlay(&sheet, row, col, &v);
1513 }
1514 }
1515 ChangeEvent::SpillCommitted { old, new, .. } => {
1516 self.mirror_spill_snapshot(new, true);
1518 if let Some(snap) = old {
1519 self.mirror_spill_snapshot(snap, false);
1520 }
1521 }
1522 ChangeEvent::SpillCleared { old, .. } => {
1523 self.mirror_spill_snapshot(old, false);
1525 }
1526 _ => {}
1527 }
1528 }
1529
1530 fn mirror_forward_change_to_arrow(&mut self, ev: &crate::engine::ChangeEvent) {
1531 use crate::engine::ChangeEvent;
1532
1533 match ev {
1534 ChangeEvent::SetValue { addr, new, .. } => {
1535 let (sheet, row, col) = self.cellref_to_sheet_row_col(addr);
1536 self.mirror_value_to_overlay(&sheet, row, col, new);
1537 }
1538 ChangeEvent::SetFormula { addr, .. } => {
1539 let (sheet, row, col) = self.cellref_to_sheet_row_col(addr);
1540 self.clear_delta_overlay_cell(&sheet, row, col);
1541 }
1543 ChangeEvent::SpillCommitted { old, new, .. } => {
1544 if let Some(snap) = old {
1545 self.mirror_spill_snapshot(snap, true);
1546 }
1547 self.mirror_spill_snapshot(new, false);
1548 }
1549 ChangeEvent::SpillCleared { old, .. } => {
1550 self.mirror_spill_snapshot(old, true);
1551 }
1552 _ => {
1553 }
1555 }
1556 }
1557
1558 fn mirror_spill_snapshot(
1559 &mut self,
1560 snap: &crate::engine::graph::editor::change_log::SpillSnapshot,
1561 clear_only: bool,
1562 ) {
1563 use formualizer_common::LiteralValue;
1564
1565 let mut i = 0usize;
1566 for row in &snap.values {
1567 for v in row {
1568 if let Some(cell) = snap.target_cells.get(i) {
1569 let (sheet, r, c) = self.cellref_to_sheet_row_col(cell);
1570 let out = if clear_only {
1571 LiteralValue::Empty
1572 } else {
1573 v.clone()
1574 };
1575 self.mirror_value_to_computed_overlay(&sheet, r, c, &out);
1576 }
1577 i += 1;
1578 }
1579 }
1580 if clear_only {
1582 for cell in snap.target_cells.iter().skip(i) {
1583 let (sheet, r, c) = self.cellref_to_sheet_row_col(cell);
1584 self.mirror_value_to_computed_overlay(&sheet, r, c, &LiteralValue::Empty);
1585 }
1586 }
1587 }
1588
1589 pub fn set_default_sheet_by_name(&mut self, name: &str) {
1590 self.graph.set_default_sheet_by_name(name);
1591 }
1592
1593 pub fn set_default_sheet_by_id(&mut self, id: SheetId) {
1594 self.graph.set_default_sheet_by_id(id);
1595 }
1596
1597 pub fn set_sheet_index_mode(&mut self, mode: crate::engine::SheetIndexMode) {
1598 self.graph.set_sheet_index_mode(mode);
1599 }
1600
1601 pub fn mark_data_edited(&mut self) {
1603 self.snapshot_id
1604 .fetch_add(1, std::sync::atomic::Ordering::Relaxed);
1605 self.has_edited = true;
1606 }
1607
1608 pub fn sheet_store(&self) -> &SheetStore {
1610 &self.arrow_sheets
1611 }
1612
1613 pub fn sheet_store_mut(&mut self) -> &mut SheetStore {
1615 &mut self.arrow_sheets
1616 }
1617
1618 pub fn stage_formula_text(&mut self, sheet: &str, row: u32, col: u32, text: String) {
1620 self.staged_formulas
1621 .entry(sheet.to_string())
1622 .or_default()
1623 .push((row, col, text));
1624 }
1625
1626 pub fn get_staged_formula_text(&self, sheet: &str, row: u32, col: u32) -> Option<String> {
1628 self.staged_formulas.get(sheet).and_then(|v| {
1629 v.iter()
1630 .find(|(r, c, _)| *r == row && *c == col)
1631 .map(|(_, _, s)| s.clone())
1632 })
1633 }
1634
1635 pub fn build_graph_all(&mut self) -> Result<(), formualizer_parse::ExcelError> {
1637 if self.staged_formulas.is_empty() {
1638 return Ok(());
1639 }
1640 let staged = std::mem::take(&mut self.staged_formulas);
1642 for sheet in staged.keys() {
1643 let _ = self.add_sheet(sheet);
1644 }
1645 let mut builder = self.begin_bulk_ingest();
1646 for (sheet, entries) in staged {
1647 let sid = builder.add_sheet(&sheet);
1648 let mut cache: rustc_hash::FxHashMap<String, formualizer_parse::ASTNode> =
1650 rustc_hash::FxHashMap::default();
1651 cache.reserve(4096);
1652 for (row, col, txt) in entries {
1653 let key = if txt.starts_with('=') {
1654 txt
1655 } else {
1656 format!("={txt}")
1657 };
1658 let ast = if let Some(p) = cache.get(&key) {
1659 p.clone()
1660 } else {
1661 let parsed = formualizer_parse::parser::parse(&key).map_err(|e| {
1662 formualizer_parse::ExcelError::new(formualizer_parse::ExcelErrorKind::Value)
1663 .with_message(e.to_string())
1664 })?;
1665 cache.insert(key.clone(), parsed.clone());
1666 parsed
1667 };
1668 builder.add_formulas(sid, std::iter::once((row, col, ast)));
1669 }
1670 }
1671 let _ = builder.finish();
1672 Ok(())
1673 }
1674
1675 pub fn build_graph_for_sheets<'a, I: IntoIterator<Item = &'a str>>(
1677 &mut self,
1678 sheets: I,
1679 ) -> Result<(), formualizer_parse::ExcelError> {
1680 let mut any = false;
1681 struct SheetFormulas<'s> {
1683 sheet: &'s str,
1684 entries: Vec<(u32, u32, String)>,
1685 }
1686 let mut collected: Vec<SheetFormulas<'_>> = Vec::new();
1687 for s in sheets {
1688 if let Some(entries) = self.staged_formulas.remove(s) {
1689 collected.push(SheetFormulas { sheet: s, entries });
1690 }
1691 }
1692 for SheetFormulas { sheet, .. } in collected.iter() {
1693 let _ = self.add_sheet(sheet);
1694 }
1695 let mut builder = self.begin_bulk_ingest();
1696 let mut cache: rustc_hash::FxHashMap<String, formualizer_parse::ASTNode> =
1698 rustc_hash::FxHashMap::default();
1699 cache.reserve(4096);
1700 for SheetFormulas { sheet: s, entries } in collected.into_iter() {
1701 any = true;
1702 let sid = builder.add_sheet(s);
1703 for (row, col, txt) in entries {
1704 let key = if txt.starts_with('=') {
1705 txt
1706 } else {
1707 format!("={txt}")
1708 };
1709 let ast = if let Some(p) = cache.get(&key) {
1710 p.clone()
1711 } else {
1712 let parsed = formualizer_parse::parser::parse(&key).map_err(|e| {
1713 formualizer_parse::ExcelError::new(formualizer_parse::ExcelErrorKind::Value)
1714 .with_message(e.to_string())
1715 })?;
1716 cache.insert(key.clone(), parsed.clone());
1717 parsed
1718 };
1719 builder.add_formulas(sid, std::iter::once((row, col, ast)));
1720 }
1721 }
1722 if any {
1723 let _ = builder.finish();
1724 }
1725 Ok(())
1726 }
1727
1728 pub fn begin_bulk_ingest_arrow(
1730 &mut self,
1731 ) -> crate::engine::arrow_ingest::ArrowBulkIngestBuilder<'_, R> {
1732 crate::engine::arrow_ingest::ArrowBulkIngestBuilder::new(self)
1733 }
1734
1735 pub fn begin_bulk_update_arrow(
1737 &mut self,
1738 ) -> crate::engine::arrow_ingest::ArrowBulkUpdateBuilder<'_, R> {
1739 crate::engine::arrow_ingest::ArrowBulkUpdateBuilder::new(self)
1740 }
1741
1742 pub fn insert_rows(
1744 &mut self,
1745 sheet: &str,
1746 before: u32,
1747 count: u32,
1748 ) -> Result<crate::engine::graph::editor::vertex_editor::ShiftSummary, crate::engine::EditorError>
1749 {
1750 use crate::engine::graph::editor::vertex_editor::VertexEditor;
1751 let sheet_id = self.graph.sheet_id(sheet).ok_or(
1752 crate::engine::graph::editor::vertex_editor::EditorError::InvalidName {
1753 name: sheet.to_string(),
1754 reason: "Unknown sheet".to_string(),
1755 },
1756 )?;
1757 let mut editor = VertexEditor::new(&mut self.graph);
1758 let before0 = before.saturating_sub(1);
1759 let summary = editor.insert_rows(sheet_id, before0, count)?;
1760 if let Some(asheet) = self.arrow_sheets.sheet_mut(sheet) {
1761 let before0 = before0 as usize;
1762 asheet.insert_rows(before0, count as usize);
1763 }
1764 self.snapshot_id
1765 .fetch_add(1, std::sync::atomic::Ordering::Relaxed);
1766 self.has_edited = true;
1767 Ok(summary)
1768 }
1769
1770 pub fn delete_rows(
1772 &mut self,
1773 sheet: &str,
1774 start: u32,
1775 count: u32,
1776 ) -> Result<crate::engine::graph::editor::vertex_editor::ShiftSummary, crate::engine::EditorError>
1777 {
1778 use crate::engine::graph::editor::vertex_editor::VertexEditor;
1779 let sheet_id = self.graph.sheet_id(sheet).ok_or(
1780 crate::engine::graph::editor::vertex_editor::EditorError::InvalidName {
1781 name: sheet.to_string(),
1782 reason: "Unknown sheet".to_string(),
1783 },
1784 )?;
1785 let mut editor = VertexEditor::new(&mut self.graph);
1786 let start0 = start.saturating_sub(1);
1787 let summary = editor.delete_rows(sheet_id, start0, count)?;
1788 if let Some(asheet) = self.arrow_sheets.sheet_mut(sheet) {
1789 let start0 = start0 as usize;
1790 asheet.delete_rows(start0, count as usize);
1791 }
1792 self.snapshot_id
1793 .fetch_add(1, std::sync::atomic::Ordering::Relaxed);
1794 self.has_edited = true;
1795 Ok(summary)
1796 }
1797
1798 pub fn insert_columns(
1800 &mut self,
1801 sheet: &str,
1802 before: u32,
1803 count: u32,
1804 ) -> Result<crate::engine::graph::editor::vertex_editor::ShiftSummary, crate::engine::EditorError>
1805 {
1806 use crate::engine::graph::editor::vertex_editor::VertexEditor;
1807 let sheet_id = self.graph.sheet_id(sheet).ok_or(
1808 crate::engine::graph::editor::vertex_editor::EditorError::InvalidName {
1809 name: sheet.to_string(),
1810 reason: "Unknown sheet".to_string(),
1811 },
1812 )?;
1813 let mut editor = VertexEditor::new(&mut self.graph);
1814 let before0 = before.saturating_sub(1);
1815 let summary = editor.insert_columns(sheet_id, before0, count)?;
1816 if let Some(asheet) = self.arrow_sheets.sheet_mut(sheet) {
1817 let before0 = before0 as usize;
1818 asheet.insert_columns(before0, count as usize);
1819 }
1820 self.snapshot_id
1821 .fetch_add(1, std::sync::atomic::Ordering::Relaxed);
1822 self.has_edited = true;
1823 Ok(summary)
1824 }
1825
1826 pub fn delete_columns(
1828 &mut self,
1829 sheet: &str,
1830 start: u32,
1831 count: u32,
1832 ) -> Result<crate::engine::graph::editor::vertex_editor::ShiftSummary, crate::engine::EditorError>
1833 {
1834 use crate::engine::graph::editor::vertex_editor::VertexEditor;
1835 let sheet_id = self.graph.sheet_id(sheet).ok_or(
1836 crate::engine::graph::editor::vertex_editor::EditorError::InvalidName {
1837 name: sheet.to_string(),
1838 reason: "Unknown sheet".to_string(),
1839 },
1840 )?;
1841 let mut editor = VertexEditor::new(&mut self.graph);
1842 let start0 = start.saturating_sub(1);
1843 let summary = editor.delete_columns(sheet_id, start0, count)?;
1844 if let Some(asheet) = self.arrow_sheets.sheet_mut(sheet) {
1845 let start0 = start0 as usize;
1846 asheet.delete_columns(start0, count as usize);
1847 }
1848 self.snapshot_id
1849 .fetch_add(1, std::sync::atomic::Ordering::Relaxed);
1850 self.has_edited = true;
1851 Ok(summary)
1852 }
1853 fn arrow_used_row_bounds(
1855 &self,
1856 sheet: &str,
1857 start_col: u32,
1858 end_col: u32,
1859 ) -> Option<(u32, u32)> {
1860 let a = self.sheet_store().sheet(sheet)?;
1861 if a.columns.is_empty() {
1862 return None;
1863 }
1864 let sc0 = start_col.saturating_sub(1) as usize;
1865 let ec0 = end_col.saturating_sub(1) as usize;
1866 let col_hi = a.columns.len().saturating_sub(1);
1867 if sc0 > col_hi {
1868 return None;
1869 }
1870 let ec0 = ec0.min(col_hi);
1871 let snap = self.data_snapshot_id();
1873 let mut min_r0: Option<usize> = None;
1874 for ci in sc0..=ec0 {
1875 let sheet_id = self.graph.sheet_id(sheet)?;
1876 if let Some((Some(mv), _)) = self.row_bounds_cache.read().ok().and_then(|g| {
1877 g.as_ref()
1878 .and_then(|c| c.get_row_bounds(sheet_id, ci, snap))
1879 }) {
1880 let mv = mv as usize;
1881 min_r0 = Some(min_r0.map(|m| m.min(mv)).unwrap_or(mv));
1882 continue;
1883 }
1884 let (min_c, max_c) = Self::scan_column_used_bounds(a, ci);
1886 if let Ok(mut g) = self.row_bounds_cache.write() {
1887 g.get_or_insert_with(|| RowBoundsCache::new(snap))
1888 .put_row_bounds(sheet_id, ci, snap, (min_c, max_c));
1889 }
1890 if let Some(m) = min_c {
1891 min_r0 = Some(min_r0.map(|mm| mm.min(m as usize)).unwrap_or(m as usize));
1892 }
1893 }
1894 min_r0?;
1895 let mut max_r0: Option<usize> = None;
1896 for ci in sc0..=ec0 {
1897 let sheet_id = self.graph.sheet_id(sheet)?;
1898 if let Some((_, Some(mv))) = self.row_bounds_cache.read().ok().and_then(|g| {
1899 g.as_ref()
1900 .and_then(|c| c.get_row_bounds(sheet_id, ci, snap))
1901 }) {
1902 let mv = mv as usize;
1903 max_r0 = Some(max_r0.map(|m| m.max(mv)).unwrap_or(mv));
1904 continue;
1905 }
1906 let (_min_c, max_c) = Self::scan_column_used_bounds(a, ci);
1907 if let Ok(mut g) = self.row_bounds_cache.write() {
1908 g.get_or_insert_with(|| RowBoundsCache::new(snap))
1909 .put_row_bounds(sheet_id, ci, snap, (_min_c, max_c));
1910 }
1911 if let Some(m) = max_c {
1912 max_r0 = Some(max_r0.map(|mm| mm.max(m as usize)).unwrap_or(m as usize));
1913 }
1914 }
1915 match (min_r0, max_r0) {
1916 (Some(a0), Some(b0)) => Some(((a0 as u32) + 1, (b0 as u32) + 1)),
1917 _ => None,
1918 }
1919 }
1920
1921 fn scan_column_used_bounds(
1922 a: &crate::arrow_store::ArrowSheet,
1923 ci: usize,
1924 ) -> (Option<u32>, Option<u32>) {
1925 let col = &a.columns[ci];
1926
1927 let mut min_r0: Option<u32> = None;
1929 for (chunk_idx, chunk) in col.chunks.iter().enumerate() {
1930 let tags = chunk.type_tag.values();
1931 for (off, &t) in tags.iter().enumerate() {
1932 let overlay_non_empty = chunk
1933 .overlay
1934 .get(off)
1935 .map(|ov| !matches!(ov, crate::arrow_store::OverlayValue::Empty))
1936 .unwrap_or(false)
1937 || chunk
1938 .computed_overlay
1939 .get(off)
1940 .map(|ov| !matches!(ov, crate::arrow_store::OverlayValue::Empty))
1941 .unwrap_or(false);
1942 if overlay_non_empty || t != crate::arrow_store::TypeTag::Empty as u8 {
1943 let Some(&chunk_start) = a.chunk_starts.get(chunk_idx) else {
1944 break;
1945 };
1946 let row0 = chunk_start + off;
1947 min_r0 = Some(row0 as u32);
1948 break;
1949 }
1950 }
1951 if min_r0.is_some() {
1952 break;
1953 }
1954 }
1955 if min_r0.is_none() && !col.sparse_chunks.is_empty() {
1956 let mut sparse_idxs: Vec<usize> = col.sparse_chunks.keys().copied().collect();
1957 sparse_idxs.sort_unstable();
1958 for chunk_idx in sparse_idxs {
1959 let Some(chunk) = col.sparse_chunks.get(&chunk_idx) else {
1960 continue;
1961 };
1962 let Some(&chunk_start) = a.chunk_starts.get(chunk_idx) else {
1963 continue;
1964 };
1965 let tags = chunk.type_tag.values();
1966 for (off, &t) in tags.iter().enumerate() {
1967 let overlay_non_empty = chunk
1968 .overlay
1969 .get(off)
1970 .map(|ov| !matches!(ov, crate::arrow_store::OverlayValue::Empty))
1971 .unwrap_or(false)
1972 || chunk
1973 .computed_overlay
1974 .get(off)
1975 .map(|ov| !matches!(ov, crate::arrow_store::OverlayValue::Empty))
1976 .unwrap_or(false);
1977 if overlay_non_empty || t != crate::arrow_store::TypeTag::Empty as u8 {
1978 let row0 = chunk_start + off;
1979 min_r0 = Some(row0 as u32);
1980 break;
1981 }
1982 }
1983 if min_r0.is_some() {
1984 break;
1985 }
1986 }
1987 }
1988
1989 let mut max_r0: Option<u32> = None;
1991 if !col.sparse_chunks.is_empty() {
1992 let mut sparse_idxs: Vec<usize> = col.sparse_chunks.keys().copied().collect();
1993 sparse_idxs.sort_unstable_by(|a, b| b.cmp(a));
1994 for chunk_idx in sparse_idxs {
1995 let Some(chunk) = col.sparse_chunks.get(&chunk_idx) else {
1996 continue;
1997 };
1998 let Some(&chunk_start) = a.chunk_starts.get(chunk_idx) else {
1999 continue;
2000 };
2001 let tags = chunk.type_tag.values();
2002 for (rev_idx, &t) in tags.iter().enumerate().rev() {
2003 let overlay_non_empty = chunk
2004 .overlay
2005 .get(rev_idx)
2006 .map(|ov| !matches!(ov, crate::arrow_store::OverlayValue::Empty))
2007 .unwrap_or(false)
2008 || chunk
2009 .computed_overlay
2010 .get(rev_idx)
2011 .map(|ov| !matches!(ov, crate::arrow_store::OverlayValue::Empty))
2012 .unwrap_or(false);
2013 if overlay_non_empty || t != crate::arrow_store::TypeTag::Empty as u8 {
2014 let row0 = chunk_start + rev_idx;
2015 max_r0 = Some(row0 as u32);
2016 break;
2017 }
2018 }
2019 if max_r0.is_some() {
2020 break;
2021 }
2022 }
2023 }
2024 if max_r0.is_none() {
2025 for (chunk_idx, chunk) in col.chunks.iter().enumerate().rev() {
2026 let tags = chunk.type_tag.values();
2027 for (rev_idx, &t) in tags.iter().enumerate().rev() {
2028 let overlay_non_empty = chunk
2029 .overlay
2030 .get(rev_idx)
2031 .map(|ov| !matches!(ov, crate::arrow_store::OverlayValue::Empty))
2032 .unwrap_or(false)
2033 || chunk
2034 .computed_overlay
2035 .get(rev_idx)
2036 .map(|ov| !matches!(ov, crate::arrow_store::OverlayValue::Empty))
2037 .unwrap_or(false);
2038 if overlay_non_empty || t != crate::arrow_store::TypeTag::Empty as u8 {
2039 let Some(&chunk_start) = a.chunk_starts.get(chunk_idx) else {
2040 break;
2041 };
2042 let row0 = chunk_start + rev_idx;
2043 max_r0 = Some(row0 as u32);
2044 break;
2045 }
2046 }
2047 if max_r0.is_some() {
2048 break;
2049 }
2050 }
2051 }
2052
2053 (min_r0, max_r0)
2054 }
2055
2056 fn arrow_used_col_bounds(
2058 &self,
2059 sheet: &str,
2060 start_row: u32,
2061 end_row: u32,
2062 ) -> Option<(u32, u32)> {
2063 let a = self.sheet_store().sheet(sheet)?;
2064 if a.columns.is_empty() {
2065 return None;
2066 }
2067 let sr0 = start_row.saturating_sub(1) as usize;
2068 let er0 = end_row.saturating_sub(1) as usize;
2069 if sr0 > er0 {
2070 return None;
2071 }
2072 let mut min_c0: Option<usize> = None;
2075 let mut max_c0: Option<usize> = None;
2076 for (ci, col) in a.columns.iter().enumerate() {
2078 let mut any_in_range = false;
2079
2080 let scan_chunk = |chunk_idx: usize, chunk: &crate::arrow_store::ColumnChunk| -> bool {
2081 let Some(&chunk_start) = a.chunk_starts.get(chunk_idx) else {
2082 return false;
2083 };
2084 let chunk_len = chunk.type_tag.len();
2085 if chunk_len == 0 {
2086 return false;
2087 }
2088 let chunk_end = chunk_start + chunk_len.saturating_sub(1);
2089 if sr0 > chunk_end || er0 < chunk_start {
2091 return false;
2092 }
2093 let start_off = sr0.max(chunk_start) - chunk_start;
2094 let end_off = er0.min(chunk_end) - chunk_start;
2095 let tags = chunk.type_tag.values();
2096 for off in start_off..=end_off {
2097 let overlay_non_empty = chunk
2098 .overlay
2099 .get(off)
2100 .map(|ov| !matches!(ov, crate::arrow_store::OverlayValue::Empty))
2101 .unwrap_or(false)
2102 || chunk
2103 .computed_overlay
2104 .get(off)
2105 .map(|ov| !matches!(ov, crate::arrow_store::OverlayValue::Empty))
2106 .unwrap_or(false);
2107 if overlay_non_empty || tags[off] != crate::arrow_store::TypeTag::Empty as u8 {
2108 return true;
2109 }
2110 }
2111 false
2112 };
2113
2114 for (chunk_idx, chunk) in col.chunks.iter().enumerate() {
2115 if scan_chunk(chunk_idx, chunk) {
2116 any_in_range = true;
2117 break;
2118 }
2119 }
2120
2121 if !any_in_range && !col.sparse_chunks.is_empty() {
2122 for (&chunk_idx, chunk) in col.sparse_chunks.iter() {
2123 if scan_chunk(chunk_idx, chunk) {
2124 any_in_range = true;
2125 break;
2126 }
2127 }
2128 }
2129
2130 if any_in_range {
2131 min_c0 = Some(min_c0.map(|m| m.min(ci)).unwrap_or(ci));
2132 max_c0 = Some(max_c0.map(|m| m.max(ci)).unwrap_or(ci));
2133 }
2134 }
2135 match (min_c0, max_c0) {
2136 (Some(a0), Some(b0)) => Some(((a0 as u32) + 1, (b0 as u32) + 1)),
2137 _ => None,
2138 }
2139 }
2140
2141 fn mirror_value_to_overlay(&mut self, sheet: &str, row: u32, col: u32, value: &LiteralValue) {
2144 if !(self.config.arrow_storage_enabled && self.config.delta_overlay_enabled) {
2145 return;
2146 }
2147 if self.arrow_sheets.sheet(sheet).is_none() {
2148 self.arrow_sheets
2149 .sheets
2150 .push(crate::arrow_store::ArrowSheet {
2151 name: std::sync::Arc::<str>::from(sheet),
2152 columns: Vec::new(),
2153 nrows: 0,
2154 chunk_starts: Vec::new(),
2155 chunk_rows: 32 * 1024,
2156 });
2157 }
2158
2159 let row0 = row.saturating_sub(1) as usize;
2160 let col0 = col.saturating_sub(1) as usize;
2161
2162 let asheet = self
2163 .arrow_sheets
2164 .sheet_mut(sheet)
2165 .expect("ArrowSheet must exist");
2166
2167 let cur_cols = asheet.columns.len();
2168 if col0 >= cur_cols {
2169 asheet.insert_columns(cur_cols, (col0 + 1) - cur_cols);
2170 }
2171
2172 if row0 >= asheet.nrows as usize {
2173 if asheet.columns.is_empty() {
2174 asheet.insert_columns(0, 1);
2175 }
2176 asheet.ensure_row_capacity(row0 + 1);
2177 }
2178 if let Some((ch_idx, in_off)) = asheet.chunk_of_row(row0) {
2179 use crate::arrow_store::OverlayValue;
2180 let ov = match value {
2181 LiteralValue::Empty => OverlayValue::Empty,
2182 LiteralValue::Int(i) => OverlayValue::Number(*i as f64),
2183 LiteralValue::Number(n) => OverlayValue::Number(*n),
2184 LiteralValue::Boolean(b) => OverlayValue::Boolean(*b),
2185 LiteralValue::Text(s) => OverlayValue::Text(std::sync::Arc::from(s.clone())),
2186 LiteralValue::Error(e) => {
2187 OverlayValue::Error(crate::arrow_store::map_error_code(e.kind))
2188 }
2189 LiteralValue::Date(d) => {
2190 let dt = d.and_hms_opt(0, 0, 0).unwrap();
2191 let serial = crate::builtins::datetime::datetime_to_serial_for(
2192 self.config.date_system,
2193 &dt,
2194 );
2195 OverlayValue::DateTime(serial)
2196 }
2197 LiteralValue::DateTime(dt) => {
2198 let serial = crate::builtins::datetime::datetime_to_serial_for(
2199 self.config.date_system,
2200 dt,
2201 );
2202 OverlayValue::DateTime(serial)
2203 }
2204 LiteralValue::Time(t) => {
2205 let serial = t.num_seconds_from_midnight() as f64 / 86_400.0;
2206 OverlayValue::DateTime(serial)
2207 }
2208 LiteralValue::Duration(d) => {
2209 let serial = d.num_seconds() as f64 / 86_400.0;
2210 OverlayValue::Duration(serial)
2211 }
2212 LiteralValue::Pending => OverlayValue::Pending,
2213 LiteralValue::Array(_) => OverlayValue::Error(crate::arrow_store::map_error_code(
2214 formualizer_common::ExcelErrorKind::Value,
2215 )),
2216 };
2217 if let Some(ch) = asheet.ensure_column_chunk_mut(col0, ch_idx) {
2218 let _ = ch.overlay.set(in_off, ov);
2219 let _ = ch.computed_overlay.remove(in_off);
2224 } else {
2225 return;
2226 }
2227 let abs_threshold = 1024usize;
2229 let frac_den = 50usize;
2230 let freed = asheet.maybe_compact_chunk(col0, ch_idx, abs_threshold, frac_den);
2231 if freed > 0 {
2232 self.overlay_compactions = self.overlay_compactions.saturating_add(1);
2233 }
2234 }
2235 }
2236
2237 fn clear_delta_overlay_cell(&mut self, sheet: &str, row: u32, col: u32) {
2242 if !(self.config.arrow_storage_enabled && self.config.delta_overlay_enabled) {
2243 return;
2244 }
2245 let Some(asheet) = self.arrow_sheets.sheet_mut(sheet) else {
2246 return;
2247 };
2248 let row0 = row.saturating_sub(1) as usize;
2249 let col0 = col.saturating_sub(1) as usize;
2250 if row0 >= asheet.nrows as usize {
2251 return;
2252 }
2253 if col0 >= asheet.columns.len() {
2254 return;
2255 }
2256 let Some((ch_idx, in_off)) = asheet.chunk_of_row(row0) else {
2257 return;
2258 };
2259 if let Some(ch) = asheet.columns[col0].chunk_mut(ch_idx) {
2260 let _ = ch.overlay.remove(in_off);
2261 }
2262 }
2263
2264 #[inline]
2265 fn overlay_value_to_literal(&self, ov: &crate::arrow_store::OverlayValue) -> LiteralValue {
2266 use crate::arrow_store::OverlayValue;
2267 match ov {
2268 OverlayValue::Empty => LiteralValue::Empty,
2269 OverlayValue::Number(n) => LiteralValue::Number(*n),
2270 OverlayValue::DateTime(serial) => LiteralValue::from_serial_number(*serial),
2271 OverlayValue::Duration(serial) => {
2272 let nanos_f = *serial * 86_400.0 * 1_000_000_000.0;
2273 let nanos = nanos_f.round().clamp(i64::MIN as f64, i64::MAX as f64) as i64;
2274 LiteralValue::Duration(chrono::Duration::nanoseconds(nanos))
2275 }
2276 OverlayValue::Boolean(b) => LiteralValue::Boolean(*b),
2277 OverlayValue::Text(s) => LiteralValue::Text((**s).to_string()),
2278 OverlayValue::Error(code) => {
2279 let kind = crate::arrow_store::unmap_error_code(*code);
2280 LiteralValue::Error(formualizer_common::ExcelError::new(kind))
2281 }
2282 OverlayValue::Pending => LiteralValue::Pending,
2283 }
2284 }
2285
2286 #[inline]
2287 fn literal_to_overlay_value(&self, value: &LiteralValue) -> crate::arrow_store::OverlayValue {
2288 use crate::arrow_store::OverlayValue;
2289 match value {
2290 LiteralValue::Empty => OverlayValue::Empty,
2291 LiteralValue::Int(i) => OverlayValue::Number(*i as f64),
2292 LiteralValue::Number(n) => OverlayValue::Number(*n),
2293 LiteralValue::Boolean(b) => OverlayValue::Boolean(*b),
2294 LiteralValue::Text(s) => OverlayValue::Text(std::sync::Arc::from(s.clone())),
2295 LiteralValue::Error(e) => {
2296 OverlayValue::Error(crate::arrow_store::map_error_code(e.kind))
2297 }
2298 LiteralValue::Date(d) => {
2299 let dt = d.and_hms_opt(0, 0, 0).unwrap();
2300 let serial =
2301 crate::builtins::datetime::datetime_to_serial_for(self.config.date_system, &dt);
2302 OverlayValue::DateTime(serial)
2303 }
2304 LiteralValue::DateTime(dt) => {
2305 let serial =
2306 crate::builtins::datetime::datetime_to_serial_for(self.config.date_system, dt);
2307 OverlayValue::DateTime(serial)
2308 }
2309 LiteralValue::Time(t) => {
2310 let serial = t.num_seconds_from_midnight() as f64 / 86_400.0;
2311 OverlayValue::DateTime(serial)
2312 }
2313 LiteralValue::Duration(d) => {
2314 let serial = d.num_seconds() as f64 / 86_400.0;
2315 OverlayValue::Duration(serial)
2316 }
2317 LiteralValue::Pending => OverlayValue::Pending,
2318 LiteralValue::Array(_) => OverlayValue::Error(crate::arrow_store::map_error_code(
2319 formualizer_common::ExcelErrorKind::Value,
2320 )),
2321 }
2322 }
2323
2324 fn read_delta_overlay_cell(&self, sheet: &str, row: u32, col: u32) -> Option<LiteralValue> {
2327 if !(self.config.arrow_storage_enabled && self.config.delta_overlay_enabled) {
2328 return None;
2329 }
2330 let asheet = self.arrow_sheets.sheet(sheet)?;
2331 let row0 = row.saturating_sub(1) as usize;
2332 let col0 = col.saturating_sub(1) as usize;
2333 if row0 >= asheet.nrows as usize || col0 >= asheet.columns.len() {
2334 return None;
2335 }
2336 let (ch_idx, in_off) = asheet.chunk_of_row(row0)?;
2337 let ch = asheet.columns[col0].chunk(ch_idx)?;
2338 ch.overlay
2339 .get(in_off)
2340 .map(|ov| self.overlay_value_to_literal(ov))
2341 }
2342
2343 fn read_computed_overlay_cell(&self, sheet: &str, row: u32, col: u32) -> Option<LiteralValue> {
2346 if !(self.config.arrow_storage_enabled
2347 && self.config.delta_overlay_enabled
2348 && self.config.write_formula_overlay_enabled)
2349 {
2350 return None;
2351 }
2352 let asheet = self.arrow_sheets.sheet(sheet)?;
2353 let row0 = row.saturating_sub(1) as usize;
2354 let col0 = col.saturating_sub(1) as usize;
2355 if row0 >= asheet.nrows as usize || col0 >= asheet.columns.len() {
2356 return None;
2357 }
2358 let (ch_idx, in_off) = asheet.chunk_of_row(row0)?;
2359 let ch = asheet.columns[col0].chunk(ch_idx)?;
2360 ch.computed_overlay
2361 .get(in_off)
2362 .map(|ov| self.overlay_value_to_literal(ov))
2363 }
2364
2365 fn set_delta_overlay_cell_raw(
2366 &mut self,
2367 sheet: &str,
2368 row: u32,
2369 col: u32,
2370 value: Option<LiteralValue>,
2371 ) {
2372 if !(self.config.arrow_storage_enabled && self.config.delta_overlay_enabled) {
2373 return;
2374 }
2375
2376 self.ensure_arrow_sheet(sheet);
2377 let ov_opt = value.as_ref().map(|v| self.literal_to_overlay_value(v));
2378 let row0 = row.saturating_sub(1) as usize;
2379 let col0 = col.saturating_sub(1) as usize;
2380 let asheet = self
2381 .arrow_sheets
2382 .sheet_mut(sheet)
2383 .expect("ArrowSheet must exist");
2384
2385 let cur_cols = asheet.columns.len();
2386 if col0 >= cur_cols {
2387 asheet.insert_columns(cur_cols, (col0 + 1) - cur_cols);
2388 }
2389 if row0 >= asheet.nrows as usize {
2390 if asheet.columns.is_empty() {
2391 asheet.insert_columns(0, 1);
2392 }
2393 asheet.ensure_row_capacity(row0 + 1);
2394 }
2395
2396 let Some((ch_idx, in_off)) = asheet.chunk_of_row(row0) else {
2397 return;
2398 };
2399 let Some(ch) = asheet.ensure_column_chunk_mut(col0, ch_idx) else {
2400 return;
2401 };
2402
2403 if let Some(ov) = ov_opt {
2404 let _ = ch.overlay.set(in_off, ov);
2405 } else {
2406 let _ = ch.overlay.remove(in_off);
2407 }
2408 }
2409
2410 fn set_computed_overlay_cell_raw(
2411 &mut self,
2412 sheet: &str,
2413 row: u32,
2414 col: u32,
2415 value: Option<LiteralValue>,
2416 ) {
2417 if !(self.config.arrow_storage_enabled
2418 && self.config.delta_overlay_enabled
2419 && self.config.write_formula_overlay_enabled)
2420 {
2421 return;
2422 }
2423
2424 self.ensure_arrow_sheet(sheet);
2425 let ov_opt = value.as_ref().map(|v| self.literal_to_overlay_value(v));
2426 let row0 = row.saturating_sub(1) as usize;
2427 let col0 = col.saturating_sub(1) as usize;
2428 let asheet = self
2429 .arrow_sheets
2430 .sheet_mut(sheet)
2431 .expect("ArrowSheet must exist");
2432
2433 let cur_cols = asheet.columns.len();
2434 if col0 >= cur_cols {
2435 asheet.insert_columns(cur_cols, (col0 + 1) - cur_cols);
2436 }
2437 if row0 >= asheet.nrows as usize {
2438 if asheet.columns.is_empty() {
2439 asheet.insert_columns(0, 1);
2440 }
2441 asheet.ensure_row_capacity(row0 + 1);
2442 }
2443
2444 let Some((ch_idx, in_off)) = asheet.chunk_of_row(row0) else {
2445 return;
2446 };
2447 let Some(ch) = asheet.ensure_column_chunk_mut(col0, ch_idx) else {
2448 return;
2449 };
2450
2451 let delta = if let Some(ov) = ov_opt {
2452 ch.computed_overlay.set(in_off, ov)
2453 } else {
2454 ch.computed_overlay.remove(in_off)
2455 };
2456 self.adjust_computed_overlay_bytes(delta);
2457 }
2458
2459 fn apply_arrow_undo_batch(&mut self, batch: &crate::engine::ArrowUndoBatch, undo: bool) {
2460 use crate::engine::ArrowOp;
2461
2462 let iter: Box<dyn Iterator<Item = &ArrowOp>> = if undo {
2463 Box::new(batch.ops.iter().rev())
2464 } else {
2465 Box::new(batch.ops.iter())
2466 };
2467
2468 for op in iter {
2469 match op {
2470 ArrowOp::SetDeltaCell {
2471 sheet_id,
2472 row0,
2473 col0,
2474 old,
2475 new,
2476 } => {
2477 let sheet = self.graph.sheet_name(*sheet_id).to_string();
2478 let v = if undo { old.clone() } else { new.clone() };
2479 self.set_delta_overlay_cell_raw(&sheet, row0 + 1, col0 + 1, v);
2480 }
2481 ArrowOp::SetComputedCell {
2482 sheet_id,
2483 row0,
2484 col0,
2485 old,
2486 new,
2487 } => {
2488 let sheet = self.graph.sheet_name(*sheet_id).to_string();
2489 let v = if undo { old.clone() } else { new.clone() };
2490 self.set_computed_overlay_cell_raw(&sheet, row0 + 1, col0 + 1, v);
2491 }
2492 ArrowOp::RestoreComputedRect {
2493 sheet_id,
2494 sr0,
2495 sc0,
2496 er0,
2497 ec0,
2498 old,
2499 new,
2500 } => {
2501 let sheet = self.graph.sheet_name(*sheet_id).to_string();
2502 let vals = if undo { old } else { new };
2503 let height = (*er0).saturating_sub(*sr0) as usize + 1;
2504 let width = (*ec0).saturating_sub(*sc0) as usize + 1;
2505 for r in 0..height {
2506 for c in 0..width {
2507 let v = vals
2508 .get(r)
2509 .and_then(|row| row.get(c))
2510 .cloned()
2511 .unwrap_or(LiteralValue::Empty);
2512 self.set_computed_overlay_cell_raw(
2513 &sheet,
2514 *sr0 + 1 + r as u32,
2515 *sc0 + 1 + c as u32,
2516 Some(v),
2517 );
2518 }
2519 }
2520 }
2521 ArrowOp::InsertRows {
2522 sheet_id,
2523 before0,
2524 count,
2525 } => {
2526 let sheet = self.graph.sheet_name(*sheet_id).to_string();
2527 self.ensure_arrow_sheet(&sheet);
2528 if let Some(asheet) = self.arrow_sheets.sheet_mut(&sheet) {
2529 if undo {
2530 asheet.delete_rows(*before0 as usize, *count as usize);
2531 } else {
2532 asheet.insert_rows(*before0 as usize, *count as usize);
2533 }
2534 }
2535 }
2536 ArrowOp::InsertCols {
2537 sheet_id,
2538 before0,
2539 count,
2540 } => {
2541 let sheet = self.graph.sheet_name(*sheet_id).to_string();
2542 self.ensure_arrow_sheet(&sheet);
2543 if let Some(asheet) = self.arrow_sheets.sheet_mut(&sheet) {
2544 if undo {
2545 asheet.delete_columns(*before0 as usize, *count as usize);
2546 } else {
2547 asheet.insert_columns(*before0 as usize, *count as usize);
2548 }
2549 }
2550 }
2551 }
2552 }
2553 }
2554
2555 fn record_spill_ops_into_arrow_undo(
2556 &mut self,
2557 undo: &mut crate::engine::ArrowUndoBatch,
2558 events: &[crate::engine::ChangeEvent],
2559 ) {
2560 use crate::engine::ChangeEvent;
2561 use formualizer_common::LiteralValue;
2562
2563 #[allow(clippy::type_complexity)]
2564 let rect_from_snapshot =
2565 |snap: &crate::engine::graph::editor::change_log::SpillSnapshot|
2566 -> Option<(SheetId, u32, u32, u32, u32, Vec<Vec<LiteralValue>>)> {
2567 if snap.target_cells.is_empty() {
2568 return None;
2569 }
2570 let sheet_id = snap.target_cells[0].sheet_id;
2571 let sr0 = snap.target_cells[0].coord.row();
2572 let sc0 = snap.target_cells[0].coord.col();
2573 if snap.values.is_empty() || snap.values[0].is_empty() {
2574 return None;
2575 }
2576 let h = snap.values.len() as u32;
2577 let w = snap.values[0].len() as u32;
2578 let er0 = sr0.saturating_add(h.saturating_sub(1));
2579 let ec0 = sc0.saturating_add(w.saturating_sub(1));
2580 Some((sheet_id, sr0, sc0, er0, ec0, snap.values.clone()))
2581 };
2582
2583 for ev in events {
2584 match ev {
2585 ChangeEvent::SpillCommitted { old, new, .. } => {
2586 if let Some((sid, sr0, sc0, er0, ec0, new_vals)) = rect_from_snapshot(new) {
2587 let old_vals = if let Some(old_snap) = old {
2588 rect_from_snapshot(old_snap)
2589 .map(|(_, _, _, _, _, v)| v)
2590 .unwrap_or_else(|| {
2591 vec![
2592 vec![LiteralValue::Empty; new_vals[0].len()];
2593 new_vals.len()
2594 ]
2595 })
2596 } else {
2597 vec![vec![LiteralValue::Empty; new_vals[0].len()]; new_vals.len()]
2598 };
2599 undo.record_restore_computed_rect(
2600 sid, sr0, sc0, er0, ec0, old_vals, new_vals,
2601 );
2602 }
2603 }
2604 ChangeEvent::SpillCleared { old, .. } => {
2605 if let Some((sid, sr0, sc0, er0, ec0, old_vals)) = rect_from_snapshot(old) {
2606 let new_vals =
2607 vec![vec![LiteralValue::Empty; old_vals[0].len()]; old_vals.len()];
2608 undo.record_restore_computed_rect(
2609 sid, sr0, sc0, er0, ec0, old_vals, new_vals,
2610 );
2611 }
2612 }
2613 _ => {}
2614 }
2615 }
2616 }
2617
2618 fn mirror_value_to_computed_overlay(
2624 &mut self,
2625 sheet: &str,
2626 row: u32,
2627 col: u32,
2628 value: &LiteralValue,
2629 ) {
2630 if !(self.config.arrow_storage_enabled
2631 && self.config.delta_overlay_enabled
2632 && self.config.write_formula_overlay_enabled)
2633 {
2634 return;
2635 }
2636 if self.computed_overlay_mirroring_disabled {
2637 return;
2638 }
2639
2640 if self.arrow_sheets.sheet(sheet).is_none() {
2641 self.arrow_sheets
2642 .sheets
2643 .push(crate::arrow_store::ArrowSheet {
2644 name: std::sync::Arc::<str>::from(sheet),
2645 columns: Vec::new(),
2646 nrows: 0,
2647 chunk_starts: Vec::new(),
2648 chunk_rows: 32 * 1024,
2649 });
2650 }
2651
2652 let row0 = row.saturating_sub(1) as usize;
2653 let col0 = col.saturating_sub(1) as usize;
2654
2655 let asheet = self
2656 .arrow_sheets
2657 .sheet_mut(sheet)
2658 .expect("ArrowSheet must exist");
2659
2660 let cur_cols = asheet.columns.len();
2661 if col0 >= cur_cols {
2662 asheet.insert_columns(cur_cols, (col0 + 1) - cur_cols);
2663 }
2664
2665 if row0 >= asheet.nrows as usize {
2666 if asheet.columns.is_empty() {
2667 asheet.insert_columns(0, 1);
2668 }
2669 asheet.ensure_row_capacity(row0 + 1);
2670 }
2671
2672 if let Some((ch_idx, in_off)) = asheet.chunk_of_row(row0) {
2673 use crate::arrow_store::OverlayValue;
2674 let ov = match value {
2675 LiteralValue::Empty => OverlayValue::Empty,
2676 LiteralValue::Int(i) => OverlayValue::Number(*i as f64),
2677 LiteralValue::Number(n) => OverlayValue::Number(*n),
2678 LiteralValue::Boolean(b) => OverlayValue::Boolean(*b),
2679 LiteralValue::Text(s) => OverlayValue::Text(std::sync::Arc::from(s.clone())),
2680 LiteralValue::Error(e) => {
2681 OverlayValue::Error(crate::arrow_store::map_error_code(e.kind))
2682 }
2683 LiteralValue::Date(d) => {
2684 let dt = d.and_hms_opt(0, 0, 0).unwrap();
2685 let serial = crate::builtins::datetime::datetime_to_serial_for(
2686 self.config.date_system,
2687 &dt,
2688 );
2689 OverlayValue::DateTime(serial)
2690 }
2691 LiteralValue::DateTime(dt) => {
2692 let serial = crate::builtins::datetime::datetime_to_serial_for(
2693 self.config.date_system,
2694 dt,
2695 );
2696 OverlayValue::DateTime(serial)
2697 }
2698 LiteralValue::Time(t) => {
2699 let serial = t.num_seconds_from_midnight() as f64 / 86_400.0;
2700 OverlayValue::DateTime(serial)
2701 }
2702 LiteralValue::Duration(d) => {
2703 let serial = d.num_seconds() as f64 / 86_400.0;
2704 OverlayValue::Duration(serial)
2705 }
2706 LiteralValue::Pending => OverlayValue::Pending,
2707 LiteralValue::Array(_) => OverlayValue::Error(crate::arrow_store::map_error_code(
2708 formualizer_common::ExcelErrorKind::Value,
2709 )),
2710 };
2711
2712 let Some(ch) = asheet.ensure_column_chunk_mut(col0, ch_idx) else {
2713 return;
2714 };
2715
2716 let delta = ch.computed_overlay.set(in_off, ov);
2717 self.adjust_computed_overlay_bytes(delta);
2718
2719 if let Some(cap) = self.config.max_overlay_memory_bytes
2720 && self.computed_overlay_bytes_estimate > cap
2721 {
2722 self.disable_computed_overlay_mirroring_due_to_budget(cap);
2723 }
2724 }
2725 }
2726
2727 #[inline]
2728 fn adjust_computed_overlay_bytes(&mut self, delta: isize) {
2729 if delta >= 0 {
2730 self.computed_overlay_bytes_estimate = self
2731 .computed_overlay_bytes_estimate
2732 .saturating_add(delta as usize);
2733 } else {
2734 self.computed_overlay_bytes_estimate = self
2735 .computed_overlay_bytes_estimate
2736 .saturating_sub((-delta) as usize);
2737 }
2738 }
2739
2740 fn clear_all_computed_overlays(&mut self) {
2741 let mut freed_total = 0usize;
2742 for sh in self.arrow_sheets.sheets.iter_mut() {
2743 for col in sh.columns.iter_mut() {
2744 for ch in col.chunks.iter_mut() {
2745 freed_total = freed_total.saturating_add(ch.computed_overlay.clear());
2746 }
2747 for ch in col.sparse_chunks.values_mut() {
2748 freed_total = freed_total.saturating_add(ch.computed_overlay.clear());
2749 }
2750 }
2751 }
2752 self.computed_overlay_bytes_estimate = self
2753 .computed_overlay_bytes_estimate
2754 .saturating_sub(freed_total);
2755 }
2756
2757 fn disable_computed_overlay_mirroring_due_to_budget(&mut self, _cap: usize) {
2758 self.compact_all_computed_overlays();
2761 }
2762
2763 fn compact_all_computed_overlays(&mut self) {
2766 let mut freed_total = 0usize;
2767 for sheet in self.arrow_sheets.sheets.iter_mut() {
2768 for col_idx in 0..sheet.columns.len() {
2769 let num_dense = sheet.columns[col_idx].chunks.len();
2771 for ch_idx in 0..num_dense {
2772 freed_total += sheet.compact_computed_overlay_chunk(col_idx, ch_idx);
2773 }
2774 let sparse_keys: Vec<usize> = sheet.columns[col_idx]
2776 .sparse_chunks
2777 .keys()
2778 .copied()
2779 .collect();
2780 for ch_idx in sparse_keys {
2781 freed_total += sheet.compact_computed_overlay_sparse_chunk(col_idx, ch_idx);
2782 }
2783 }
2784 }
2785 self.computed_overlay_bytes_estimate = self
2786 .computed_overlay_bytes_estimate
2787 .saturating_sub(freed_total);
2788 self.overlay_compactions = self.overlay_compactions.saturating_add(1);
2789 }
2790
2791 fn mirror_vertex_value_to_overlay(&mut self, vertex_id: VertexId, value: &LiteralValue) {
2792 if !(self.config.arrow_storage_enabled
2793 && self.config.delta_overlay_enabled
2794 && self.config.write_formula_overlay_enabled)
2795 {
2796 return;
2797 }
2798 if !matches!(
2799 self.graph.get_vertex_kind(vertex_id),
2800 VertexKind::FormulaScalar | VertexKind::FormulaArray
2801 ) {
2802 return;
2803 }
2804 let Some(cell) = self.graph.get_cell_ref(vertex_id) else {
2805 return;
2806 };
2807 let sheet_name = self.graph.sheet_name(cell.sheet_id).to_string();
2808 self.mirror_value_to_computed_overlay(
2809 &sheet_name,
2810 cell.coord.row() + 1,
2811 cell.coord.col() + 1,
2812 value,
2813 );
2814 }
2815
2816 pub fn overlay_memory_usage(&self) -> usize {
2818 self.computed_overlay_bytes_estimate
2819 }
2820
2821 fn resolve_sheet_locator_for_write(
2822 &mut self,
2823 loc: formualizer_common::SheetLocator<'_>,
2824 current_sheet: &str,
2825 ) -> Result<SheetId, ExcelError> {
2826 Ok(match loc {
2827 formualizer_common::SheetLocator::Id(id) => id,
2828 formualizer_common::SheetLocator::Name(name) => self.graph.sheet_id_mut(name.as_ref()),
2829 formualizer_common::SheetLocator::Current => self.graph.sheet_id_mut(current_sheet),
2830 })
2831 }
2832
2833 fn resolve_sheet_locator_for_read(
2834 &self,
2835 loc: formualizer_common::SheetLocator<'_>,
2836 current_sheet: &str,
2837 ) -> Result<SheetId, ExcelError> {
2838 match loc {
2839 formualizer_common::SheetLocator::Id(id) => Ok(id),
2840 formualizer_common::SheetLocator::Name(name) => self
2841 .graph
2842 .sheet_id(name.as_ref())
2843 .ok_or_else(|| ExcelError::new(ExcelErrorKind::Ref)),
2844 formualizer_common::SheetLocator::Current => self
2845 .graph
2846 .sheet_id(current_sheet)
2847 .ok_or_else(|| ExcelError::new(ExcelErrorKind::Ref)),
2848 }
2849 }
2850
2851 pub fn set_cell_value(
2853 &mut self,
2854 sheet: &str,
2855 row: u32,
2856 col: u32,
2857 value: LiteralValue,
2858 ) -> Result<(), ExcelError> {
2859 self.graph.set_cell_value(sheet, row, col, value.clone())?;
2860 self.mirror_value_to_overlay(sheet, row, col, &value);
2862 self.snapshot_id
2864 .fetch_add(1, std::sync::atomic::Ordering::Relaxed);
2865 self.has_edited = true;
2866 Ok(())
2867 }
2868
2869 pub fn set_cell_value_ref(
2870 &mut self,
2871 cell: formualizer_common::SheetCellRef<'_>,
2872 current_sheet: &str,
2873 value: LiteralValue,
2874 ) -> Result<(), ExcelError> {
2875 let owned = cell.into_owned();
2876 let sheet_id = self.resolve_sheet_locator_for_write(owned.sheet, current_sheet)?;
2877 let sheet_name = self.graph.sheet_name(sheet_id).to_string();
2878 self.set_cell_value(
2879 &sheet_name,
2880 owned.coord.row() + 1,
2881 owned.coord.col() + 1,
2882 value,
2883 )
2884 }
2885
2886 pub fn set_cell_formula_ref(
2887 &mut self,
2888 cell: formualizer_common::SheetCellRef<'_>,
2889 current_sheet: &str,
2890 ast: ASTNode,
2891 ) -> Result<(), ExcelError> {
2892 let owned = cell.into_owned();
2893 let sheet_id = self.resolve_sheet_locator_for_write(owned.sheet, current_sheet)?;
2894 let sheet_name = self.graph.sheet_name(sheet_id).to_string();
2895 self.set_cell_formula(
2896 &sheet_name,
2897 owned.coord.row() + 1,
2898 owned.coord.col() + 1,
2899 ast,
2900 )
2901 }
2902
2903 pub fn get_cell_value_ref(
2904 &self,
2905 cell: formualizer_common::SheetCellRef<'_>,
2906 current_sheet: &str,
2907 ) -> Result<Option<LiteralValue>, ExcelError> {
2908 let owned = cell.into_owned();
2909 let sheet_id = self.resolve_sheet_locator_for_read(owned.sheet, current_sheet)?;
2910 let sheet_name = self.graph.sheet_name(sheet_id);
2911 Ok(self.get_cell_value(sheet_name, owned.coord.row() + 1, owned.coord.col() + 1))
2912 }
2913
2914 pub fn resolve_range_view_sheet_ref<'c>(
2915 &'c self,
2916 r: &formualizer_common::SheetRef<'_>,
2917 current_sheet: &str,
2918 ) -> Result<RangeView<'c>, ExcelError> {
2919 use formualizer_common::SheetLocator;
2920
2921 let sheet_to_opt_name = |loc: SheetLocator<'_>| -> Result<Option<String>, ExcelError> {
2922 match loc {
2923 SheetLocator::Current => Ok(None),
2924 SheetLocator::Name(name) => Ok(Some(name.as_ref().to_string())),
2925 SheetLocator::Id(id) => Ok(Some(self.graph.sheet_name(id).to_string())),
2926 }
2927 };
2928
2929 let rt = match r {
2930 formualizer_common::SheetRef::Cell(cell) => ReferenceType::Cell {
2931 sheet: sheet_to_opt_name(cell.sheet.clone())?,
2932 row: cell.coord.row() + 1,
2933 col: cell.coord.col() + 1,
2934 row_abs: cell.coord.row_abs(),
2935 col_abs: cell.coord.col_abs(),
2936 },
2937 formualizer_common::SheetRef::Range(range) => ReferenceType::Range {
2938 sheet: sheet_to_opt_name(range.sheet.clone())?,
2939 start_row: range.start_row.map(|b| b.index + 1),
2940 start_col: range.start_col.map(|b| b.index + 1),
2941 end_row: range.end_row.map(|b| b.index + 1),
2942 end_col: range.end_col.map(|b| b.index + 1),
2943 start_row_abs: range.start_row.map(|b| b.abs).unwrap_or(false),
2944 start_col_abs: range.start_col.map(|b| b.abs).unwrap_or(false),
2945 end_row_abs: range.end_row.map(|b| b.abs).unwrap_or(false),
2946 end_col_abs: range.end_col.map(|b| b.abs).unwrap_or(false),
2947 },
2948 };
2949
2950 crate::traits::EvaluationContext::resolve_range_view(self, &rt, current_sheet)
2951 }
2952
2953 pub fn set_cell_formula(
2955 &mut self,
2956 sheet: &str,
2957 row: u32,
2958 col: u32,
2959 ast: ASTNode,
2960 ) -> Result<(), ExcelError> {
2961 let volatile = self.is_ast_volatile_with_provider(&ast);
2962 self.graph
2963 .set_cell_formula_with_volatility(sheet, row, col, ast, volatile)?;
2964
2965 self.clear_delta_overlay_cell(sheet, row, col);
2970
2971 self.snapshot_id
2973 .fetch_add(1, std::sync::atomic::Ordering::Relaxed);
2974 self.has_edited = true;
2975 Ok(())
2976 }
2977
2978 pub fn bulk_set_formulas<I>(&mut self, sheet: &str, items: I) -> Result<usize, ExcelError>
2980 where
2981 I: IntoIterator<Item = (u32, u32, ASTNode)>,
2982 {
2983 let collected: Vec<(u32, u32, ASTNode)> = items.into_iter().collect();
2984 let vol_flags: Vec<bool> = collected
2985 .iter()
2986 .map(|(_, _, ast)| self.is_ast_volatile_with_provider(ast))
2987 .collect();
2988 let n = self
2989 .graph
2990 .bulk_set_formulas_with_volatility(sheet, collected, vol_flags)?;
2991 self.snapshot_id
2993 .fetch_add(1, std::sync::atomic::Ordering::Relaxed);
2994 if n > 0 {
2995 self.has_edited = true;
2996 }
2997 Ok(n)
2998 }
2999
3000 #[inline]
3001 fn normalize_public_cell_read(v: LiteralValue) -> Option<LiteralValue> {
3002 match v {
3003 LiteralValue::Empty => None,
3004 LiteralValue::Int(i) => Some(LiteralValue::Number(i as f64)),
3005 other => Some(other),
3006 }
3007 }
3008
3009 pub fn get_cell_value(&self, sheet: &str, row: u32, col: u32) -> Option<LiteralValue> {
3011 self.read_cell_value(sheet, row, col)
3012 .and_then(Self::normalize_public_cell_read)
3013 }
3014
3015 pub(crate) fn read_cell_value(&self, sheet: &str, row: u32, col: u32) -> Option<LiteralValue> {
3017 let asheet = self.sheet_store().sheet(sheet)?;
3018 let r0 = row.saturating_sub(1) as usize;
3019 let c0 = col.saturating_sub(1) as usize;
3020 let v = asheet.get_cell_value(r0, c0);
3021 if matches!(v, LiteralValue::Empty) {
3022 None
3023 } else {
3024 Some(v)
3025 }
3026 }
3027
3028 pub(crate) fn read_range_values(
3030 &self,
3031 sheet: &str,
3032 sr: u32,
3033 sc: u32,
3034 er: u32,
3035 ec: u32,
3036 ) -> RangeView<'_> {
3037 let Some(asheet) = self.sheet_store().sheet(sheet) else {
3038 return RangeView::from_owned_rows(Vec::new(), self.config.date_system);
3039 };
3040 if er < sr || ec < sc {
3041 return asheet.range_view(1, 1, 0, 0);
3042 }
3043 let sr0 = sr.saturating_sub(1) as usize;
3044 let sc0 = sc.saturating_sub(1) as usize;
3045 let er0 = er.saturating_sub(1) as usize;
3046 let ec0 = ec.saturating_sub(1) as usize;
3047 asheet.range_view(sr0, sc0, er0, ec0)
3048 }
3049
3050 pub fn get_cell(
3052 &self,
3053 sheet: &str,
3054 row: u32,
3055 col: u32,
3056 ) -> Option<(Option<formualizer_parse::ASTNode>, Option<LiteralValue>)> {
3057 let v = self.get_cell_value(sheet, row, col);
3058 let sheet_id = self.graph.sheet_id(sheet)?;
3059 let coord = Coord::from_excel(row, col, true, true);
3060 let cell = CellRef::new(sheet_id, coord);
3061 let vid = self.graph.get_vertex_for_cell(&cell)?;
3062 let ast = self.graph.get_formula_id(vid).and_then(|ast_id| {
3063 self.graph
3064 .data_store()
3065 .retrieve_ast(ast_id, self.graph.sheet_reg())
3066 });
3067 Some((ast, v))
3068 }
3069
3070 pub fn begin_batch(&mut self) {
3072 self.graph.begin_batch();
3073 }
3074
3075 pub fn end_batch(&mut self) {
3077 self.graph.end_batch();
3078 }
3079
3080 #[inline]
3083 fn record_cell_if_changed(
3084 delta: &mut DeltaCollector,
3085 cell: &CellRef,
3086 old: &LiteralValue,
3087 new: &LiteralValue,
3088 ) {
3089 if old != new {
3090 delta.record_cell(cell.sheet_id, cell.coord.row(), cell.coord.col());
3091 }
3092 }
3093
3094 pub fn evaluate_vertex(&mut self, vertex_id: VertexId) -> Result<LiteralValue, ExcelError> {
3095 self.evaluate_vertex_impl(vertex_id, None)
3096 }
3097
3098 fn evaluate_vertex_impl(
3099 &mut self,
3100 vertex_id: VertexId,
3101 delta: Option<&mut DeltaCollector>,
3102 ) -> Result<LiteralValue, ExcelError> {
3103 let mut delta = delta;
3104 if !self.graph.vertex_exists(vertex_id) {
3106 return Err(ExcelError::new(formualizer_common::ExcelErrorKind::Ref)
3107 .with_message(format!("Vertex not found: {vertex_id:?}")));
3108 }
3109
3110 let kind = self.graph.get_vertex_kind(vertex_id);
3112 let sheet_id = self.graph.get_vertex_sheet_id(vertex_id);
3113
3114 let ast_id = match kind {
3115 VertexKind::FormulaScalar | VertexKind::FormulaArray => {
3116 if let Some(ast_id) = self.graph.get_formula_id(vertex_id) {
3117 ast_id
3118 } else {
3119 return Ok(LiteralValue::Number(0.0));
3120 }
3121 }
3122 VertexKind::Empty | VertexKind::Cell => {
3123 if let Some(cell_ref) = self.graph.get_cell_ref(vertex_id) {
3124 let sheet_name = self.graph.sheet_name(cell_ref.sheet_id);
3125 let row = cell_ref.coord.row() + 1;
3126 let col = cell_ref.coord.col() + 1;
3127 if let Some(v) = self.read_cell_value(sheet_name, row, col) {
3128 return Ok(v);
3129 }
3130 }
3131 return Ok(LiteralValue::Number(0.0));
3132 }
3133 VertexKind::NamedScalar => {
3134 let value = self.evaluate_named_scalar(vertex_id, sheet_id)?;
3135 return Ok(value);
3136 }
3137 VertexKind::NamedArray => {
3138 let value = self.evaluate_named_array(vertex_id, sheet_id)?;
3139 return Ok(value);
3140 }
3141 VertexKind::InfiniteRange
3142 | VertexKind::Range
3143 | VertexKind::External
3144 | VertexKind::Table => {
3145 return Ok(LiteralValue::Number(0.0));
3147 }
3148 };
3149
3150 let sheet_name = self.graph.sheet_name(sheet_id);
3152 let cell_ref = self
3153 .graph
3154 .get_cell_ref(vertex_id)
3155 .expect("cell ref for vertex");
3156 let interpreter = Interpreter::new_with_cell(self, sheet_name, cell_ref);
3157
3158 let result =
3159 interpreter.evaluate_arena_ast(ast_id, self.graph.data_store(), self.graph.sheet_reg());
3160
3161 match result {
3163 Ok(cv) => {
3164 let result_literal = cv.into_literal();
3165 match result_literal {
3166 LiteralValue::Array(rows) => {
3167 self.graph
3169 .set_kind(vertex_id, crate::engine::vertex::VertexKind::FormulaArray);
3170 let anchor = self
3172 .graph
3173 .get_cell_ref(vertex_id)
3174 .expect("cell ref for vertex");
3175 let sheet_id = anchor.sheet_id;
3176 let h = rows.len() as u32;
3177 let w = rows.first().map(|r| r.len()).unwrap_or(0) as u32;
3178
3179 let spill_cells = (h as u64).saturating_mul(w as u64);
3181 if spill_cells > self.config.spill.max_spill_cells as u64 {
3182 self.clear_spill_projection_and_mirror(vertex_id, delta.as_deref_mut());
3183 let spill_err = ExcelError::new(ExcelErrorKind::Spill)
3184 .with_message("SpillTooLarge")
3185 .with_extra(formualizer_common::ExcelErrorExtra::Spill {
3186 expected_rows: h,
3187 expected_cols: w,
3188 });
3189 let spill_val = LiteralValue::Error(spill_err.clone());
3190 if let Some(d) = delta.as_deref_mut() {
3191 let old = self
3192 .read_cell_value(
3193 self.graph.sheet_name(anchor.sheet_id),
3194 anchor.coord.row() + 1,
3195 anchor.coord.col() + 1,
3196 )
3197 .unwrap_or(LiteralValue::Empty);
3198 if old != spill_val {
3199 d.record_cell(
3200 anchor.sheet_id,
3201 anchor.coord.row(),
3202 anchor.coord.col(),
3203 );
3204 }
3205 }
3206 self.graph.update_vertex_value(vertex_id, spill_val.clone());
3207 if self.config.arrow_storage_enabled
3208 && self.config.delta_overlay_enabled
3209 && self.config.write_formula_overlay_enabled
3210 {
3211 let sheet_name = self.graph.sheet_name(anchor.sheet_id).to_string();
3212 self.mirror_value_to_computed_overlay(
3213 &sheet_name,
3214 anchor.coord.row() + 1,
3215 anchor.coord.col() + 1,
3216 &spill_val,
3217 );
3218 }
3219 return Ok(spill_val);
3220 }
3221 const PACKED_MAX_ROW: u32 = 1_048_575; const PACKED_MAX_COL: u32 = 16_383; let end_row = anchor.coord.row().saturating_add(h).saturating_sub(1);
3225 let end_col = anchor.coord.col().saturating_add(w).saturating_sub(1);
3226 if end_row > PACKED_MAX_ROW || end_col > PACKED_MAX_COL {
3227 self.clear_spill_projection_and_mirror(vertex_id, delta.as_deref_mut());
3228 let spill_err = ExcelError::new(ExcelErrorKind::Spill)
3229 .with_message("Spill exceeds sheet bounds")
3230 .with_extra(formualizer_common::ExcelErrorExtra::Spill {
3231 expected_rows: h,
3232 expected_cols: w,
3233 });
3234 let spill_val = LiteralValue::Error(spill_err.clone());
3235 if let Some(d) = delta.as_deref_mut() {
3236 let old = self
3237 .read_cell_value(
3238 self.graph.sheet_name(anchor.sheet_id),
3239 anchor.coord.row() + 1,
3240 anchor.coord.col() + 1,
3241 )
3242 .unwrap_or(LiteralValue::Empty);
3243 if old != spill_val {
3244 d.record_cell(
3245 anchor.sheet_id,
3246 anchor.coord.row(),
3247 anchor.coord.col(),
3248 );
3249 }
3250 }
3251 self.graph.update_vertex_value(vertex_id, spill_val.clone());
3252 if self.config.arrow_storage_enabled
3253 && self.config.delta_overlay_enabled
3254 && self.config.write_formula_overlay_enabled
3255 {
3256 let sheet_name = self.graph.sheet_name(anchor.sheet_id).to_string();
3257 self.mirror_value_to_computed_overlay(
3258 &sheet_name,
3259 anchor.coord.row() + 1,
3260 anchor.coord.col() + 1,
3261 &spill_val,
3262 );
3263 }
3264 return Ok(spill_val);
3265 }
3266 let mut targets = Vec::new();
3267 for r in 0..h {
3268 for c in 0..w {
3269 targets.push(self.graph.make_cell_ref_internal(
3270 sheet_id,
3271 anchor.coord.row() + r,
3272 anchor.coord.col() + c,
3273 ));
3274 }
3275 }
3276
3277 match self.spill_mgr.reserve(
3279 vertex_id,
3280 anchor,
3281 SpillShape { rows: h, cols: w },
3282 SpillMeta {
3283 epoch: self.recalc_epoch,
3284 config: self.config.spill,
3285 },
3286 ) {
3287 Ok(()) => {
3288 if let Err(e) = self.commit_spill_and_mirror(
3292 vertex_id,
3293 &targets,
3294 rows.clone(),
3295 delta.as_deref_mut(),
3296 None,
3297 ) {
3298 self.clear_spill_projection_and_mirror(
3300 vertex_id,
3301 delta.as_deref_mut(),
3302 );
3303 if let Some(d) = delta.as_deref_mut() {
3304 let old = self
3305 .read_cell_value(
3306 self.graph.sheet_name(anchor.sheet_id),
3307 anchor.coord.row() + 1,
3308 anchor.coord.col() + 1,
3309 )
3310 .unwrap_or(LiteralValue::Empty);
3311 let new = LiteralValue::Error(e.clone());
3312 if old != new {
3313 d.record_cell(
3314 anchor.sheet_id,
3315 anchor.coord.row(),
3316 anchor.coord.col(),
3317 );
3318 }
3319 }
3320 let err_val = LiteralValue::Error(e.clone());
3321 self.graph.update_vertex_value(vertex_id, err_val.clone());
3322 if self.config.arrow_storage_enabled
3323 && self.config.delta_overlay_enabled
3324 && self.config.write_formula_overlay_enabled
3325 {
3326 let sheet_name =
3327 self.graph.sheet_name(anchor.sheet_id).to_string();
3328 self.mirror_value_to_computed_overlay(
3329 &sheet_name,
3330 anchor.coord.row() + 1,
3331 anchor.coord.col() + 1,
3332 &err_val,
3333 );
3334 }
3335 return Ok(err_val);
3336 }
3337 let top_left = rows
3339 .first()
3340 .and_then(|r| r.first())
3341 .cloned()
3342 .unwrap_or(LiteralValue::Empty);
3343 self.graph.update_vertex_value(vertex_id, top_left.clone());
3344 Ok(top_left)
3345 }
3346 Err(e) => {
3347 self.clear_spill_projection_and_mirror(
3348 vertex_id,
3349 delta.as_deref_mut(),
3350 );
3351 let spill_err = ExcelError::new(ExcelErrorKind::Spill)
3352 .with_message(
3353 e.message.unwrap_or_else(|| "Spill blocked".to_string()),
3354 )
3355 .with_extra(formualizer_common::ExcelErrorExtra::Spill {
3356 expected_rows: h,
3357 expected_cols: w,
3358 });
3359 let spill_val = LiteralValue::Error(spill_err.clone());
3360 if let Some(d) = delta.as_deref_mut() {
3361 let old = self
3362 .read_cell_value(
3363 self.graph.sheet_name(anchor.sheet_id),
3364 anchor.coord.row() + 1,
3365 anchor.coord.col() + 1,
3366 )
3367 .unwrap_or(LiteralValue::Empty);
3368 if old != spill_val {
3369 d.record_cell(
3370 anchor.sheet_id,
3371 anchor.coord.row(),
3372 anchor.coord.col(),
3373 );
3374 }
3375 }
3376 self.graph.update_vertex_value(vertex_id, spill_val.clone());
3377 if self.config.arrow_storage_enabled
3378 && self.config.delta_overlay_enabled
3379 && self.config.write_formula_overlay_enabled
3380 {
3381 let sheet_name =
3382 self.graph.sheet_name(anchor.sheet_id).to_string();
3383 self.mirror_value_to_computed_overlay(
3384 &sheet_name,
3385 anchor.coord.row() + 1,
3386 anchor.coord.col() + 1,
3387 &spill_val,
3388 );
3389 }
3390 Ok(spill_val)
3391 }
3392 }
3393 }
3394 other => {
3395 let spill_cells = self
3397 .graph
3398 .spill_cells_for_anchor(vertex_id)
3399 .map(|cells| cells.to_vec())
3400 .unwrap_or_default();
3401 if let Some(d) = delta.as_deref_mut()
3402 && let Some(anchor) = self.graph.get_cell_ref_for_vertex(vertex_id)
3403 {
3404 if spill_cells.is_empty() {
3405 let old = self
3406 .read_cell_value(
3407 self.graph.sheet_name(anchor.sheet_id),
3408 anchor.coord.row() + 1,
3409 anchor.coord.col() + 1,
3410 )
3411 .unwrap_or(LiteralValue::Empty);
3412 if old != other {
3413 d.record_cell(
3414 anchor.sheet_id,
3415 anchor.coord.row(),
3416 anchor.coord.col(),
3417 );
3418 }
3419 } else {
3420 for cell in spill_cells.iter() {
3421 let sheet_name = self.graph.sheet_name(cell.sheet_id);
3422 let old = self
3423 .get_cell_value(
3424 sheet_name,
3425 cell.coord.row() + 1,
3426 cell.coord.col() + 1,
3427 )
3428 .unwrap_or(LiteralValue::Empty);
3429 let new = if cell.sheet_id == anchor.sheet_id
3430 && cell.coord.row() == anchor.coord.row()
3431 && cell.coord.col() == anchor.coord.col()
3432 {
3433 other.clone()
3434 } else {
3435 LiteralValue::Empty
3436 };
3437 Self::record_cell_if_changed(d, cell, &old, &new);
3438 }
3439 }
3440 }
3441 self.graph.clear_spill_region(vertex_id);
3442 if self.config.arrow_storage_enabled
3443 && self.config.delta_overlay_enabled
3444 && self.config.write_formula_overlay_enabled
3445 {
3446 let empty = LiteralValue::Empty;
3447 for cell in spill_cells.iter() {
3448 let sheet_name = self.graph.sheet_name(cell.sheet_id).to_string();
3449 self.mirror_value_to_computed_overlay(
3450 &sheet_name,
3451 cell.coord.row() + 1,
3452 cell.coord.col() + 1,
3453 &empty,
3454 );
3455 }
3456 }
3457 self.graph.update_vertex_value(vertex_id, other.clone());
3458 if self.config.arrow_storage_enabled
3460 && self.config.delta_overlay_enabled
3461 && self.config.write_formula_overlay_enabled
3462 {
3463 let anchor = self
3464 .graph
3465 .get_cell_ref(vertex_id)
3466 .expect("cell ref for vertex");
3467 let sheet_name = self.graph.sheet_name(anchor.sheet_id).to_string();
3468 self.mirror_value_to_computed_overlay(
3469 &sheet_name,
3470 anchor.coord.row() + 1,
3471 anchor.coord.col() + 1,
3472 &other,
3473 );
3474 }
3475 Ok(other)
3476 }
3477 }
3478 }
3479 Err(e) => {
3480 let spill_cells = self
3483 .graph
3484 .spill_cells_for_anchor(vertex_id)
3485 .map(|cells| cells.to_vec())
3486 .unwrap_or_default();
3487 let err_val = LiteralValue::Error(e.clone());
3488 if let Some(d) = delta
3489 && let Some(anchor) = self.graph.get_cell_ref_for_vertex(vertex_id)
3490 {
3491 if spill_cells.is_empty() {
3492 let old = self
3493 .read_cell_value(
3494 self.graph.sheet_name(anchor.sheet_id),
3495 anchor.coord.row() + 1,
3496 anchor.coord.col() + 1,
3497 )
3498 .unwrap_or(LiteralValue::Empty);
3499 if old != err_val {
3500 d.record_cell(anchor.sheet_id, anchor.coord.row(), anchor.coord.col());
3501 }
3502 } else {
3503 for cell in spill_cells.iter() {
3504 let sheet_name = self.graph.sheet_name(cell.sheet_id);
3505 let old = self
3506 .get_cell_value(
3507 sheet_name,
3508 cell.coord.row() + 1,
3509 cell.coord.col() + 1,
3510 )
3511 .unwrap_or(LiteralValue::Empty);
3512 let new = if cell.sheet_id == anchor.sheet_id
3513 && cell.coord.row() == anchor.coord.row()
3514 && cell.coord.col() == anchor.coord.col()
3515 {
3516 err_val.clone()
3517 } else {
3518 LiteralValue::Empty
3519 };
3520 Self::record_cell_if_changed(d, cell, &old, &new);
3521 }
3522 }
3523 }
3524 self.graph.clear_spill_region(vertex_id);
3525 if self.config.arrow_storage_enabled
3526 && self.config.delta_overlay_enabled
3527 && self.config.write_formula_overlay_enabled
3528 {
3529 let empty = LiteralValue::Empty;
3530 for cell in spill_cells.iter() {
3531 let sheet_name = self.graph.sheet_name(cell.sheet_id).to_string();
3532 self.mirror_value_to_computed_overlay(
3533 &sheet_name,
3534 cell.coord.row() + 1,
3535 cell.coord.col() + 1,
3536 &empty,
3537 );
3538 }
3539 }
3540 self.graph.update_vertex_value(vertex_id, err_val.clone());
3541 if self.config.arrow_storage_enabled
3542 && self.config.delta_overlay_enabled
3543 && self.config.write_formula_overlay_enabled
3544 {
3545 let anchor = self
3546 .graph
3547 .get_cell_ref(vertex_id)
3548 .expect("cell ref for vertex");
3549 let sheet_name = self.graph.sheet_name(anchor.sheet_id).to_string();
3550 self.mirror_value_to_computed_overlay(
3551 &sheet_name,
3552 anchor.coord.row() + 1,
3553 anchor.coord.col() + 1,
3554 &err_val,
3555 );
3556 }
3557 Ok(err_val)
3558 }
3559 }
3560 }
3561
3562 fn evaluate_named_scalar(
3563 &mut self,
3564 vertex_id: VertexId,
3565 sheet_id: SheetId,
3566 ) -> Result<LiteralValue, ExcelError> {
3567 let named_range = self.graph.named_range_by_vertex(vertex_id).ok_or_else(|| {
3568 ExcelError::new(ExcelErrorKind::Name)
3569 .with_message("Named range metadata missing".to_string())
3570 })?;
3571
3572 match &named_range.definition {
3573 NamedDefinition::Cell(cell_ref) => {
3574 let sheet_name = self.graph.sheet_name(cell_ref.sheet_id);
3575 let row = cell_ref.coord.row() + 1;
3576 let col = cell_ref.coord.col() + 1;
3577
3578 if let Some(dep_vertex) = self.graph.get_vertex_for_cell(cell_ref)
3579 && matches!(
3580 self.graph.get_vertex_kind(dep_vertex),
3581 VertexKind::FormulaScalar | VertexKind::FormulaArray
3582 )
3583 {
3584 let value = self.evaluate_vertex(dep_vertex)?;
3586 self.graph.update_vertex_value(vertex_id, value.clone());
3587 Ok(value)
3588 } else {
3589 let value = self
3590 .get_cell_value(sheet_name, row, col)
3591 .unwrap_or(LiteralValue::Empty);
3592 self.graph.update_vertex_value(vertex_id, value.clone());
3593 Ok(value)
3594 }
3595 }
3596 NamedDefinition::Literal(v) => {
3597 let out = v.clone();
3598 self.graph.update_vertex_value(vertex_id, out.clone());
3599 Ok(out)
3600 }
3601 NamedDefinition::Formula { ast, .. } => {
3602 let context_sheet = match named_range.scope {
3603 NameScope::Sheet(id) => id,
3604 NameScope::Workbook => sheet_id,
3605 };
3606 let sheet_name = self.graph.sheet_name(context_sheet);
3607 let cell_ref = self
3608 .graph
3609 .get_cell_ref(vertex_id)
3610 .unwrap_or_else(|| self.graph.make_cell_ref(sheet_name, 0, 0));
3611 let interpreter = Interpreter::new_with_cell(self, sheet_name, cell_ref);
3612 match interpreter.evaluate_ast(ast) {
3613 Ok(cv) => {
3614 let value = cv.into_literal();
3615 match value {
3616 LiteralValue::Array(_) => {
3617 let err = ExcelError::new(ExcelErrorKind::NImpl)
3618 .with_message("Array result in scalar named range".to_string());
3619 let err_val = LiteralValue::Error(err.clone());
3620 self.graph.update_vertex_value(vertex_id, err_val.clone());
3621 Ok(err_val)
3622 }
3623 other => {
3624 self.graph.update_vertex_value(vertex_id, other.clone());
3625 Ok(other)
3626 }
3627 }
3628 }
3629 Err(err) => {
3630 let err_val = LiteralValue::Error(err.clone());
3631 self.graph.update_vertex_value(vertex_id, err_val.clone());
3632 Ok(err_val)
3633 }
3634 }
3635 }
3636 NamedDefinition::Range(_) => Err(ExcelError::new(ExcelErrorKind::Value)
3637 .with_message("Range-valued name evaluated as scalar".to_string())),
3638 }
3639 }
3640
3641 fn evaluate_named_array(
3642 &mut self,
3643 vertex_id: VertexId,
3644 sheet_id: SheetId,
3645 ) -> Result<LiteralValue, ExcelError> {
3646 let named_range = self.graph.named_range_by_vertex(vertex_id).ok_or_else(|| {
3647 ExcelError::new(ExcelErrorKind::Name)
3648 .with_message("Named range metadata missing".to_string())
3649 })?;
3650
3651 let out = match &named_range.definition {
3652 NamedDefinition::Range(range_ref) => {
3653 if range_ref.start.sheet_id != range_ref.end.sheet_id {
3654 return Err(ExcelError::new(ExcelErrorKind::Ref)
3655 .with_message("Named range cannot span sheets".to_string()));
3656 }
3657
3658 let sheet_name = self.graph.sheet_name(range_ref.start.sheet_id);
3659 let sr0 = range_ref.start.coord.row();
3660 let sc0 = range_ref.start.coord.col();
3661 let er0 = range_ref.end.coord.row();
3662 let ec0 = range_ref.end.coord.col();
3663 if sr0 > er0 || sc0 > ec0 {
3664 return Err(ExcelError::new(ExcelErrorKind::Ref)
3665 .with_message("Invalid named range bounds".to_string()));
3666 }
3667
3668 let h = (er0 - sr0 + 1) as usize;
3669 let w = (ec0 - sc0 + 1) as usize;
3670 let cell_count = (h as u64).saturating_mul(w as u64);
3671 if cell_count > self.config.spill.max_spill_cells as u64 {
3672 return Err(ExcelError::new(ExcelErrorKind::NImpl).with_message(
3673 "Named range too large to materialize as an array".to_string(),
3674 ));
3675 }
3676
3677 let mut rows = Vec::with_capacity(h);
3678 for r0 in sr0..=er0 {
3679 let mut row = Vec::with_capacity(w);
3680 for c0 in sc0..=ec0 {
3681 let v = self
3682 .get_cell_value(sheet_name, r0 + 1, c0 + 1)
3683 .unwrap_or(LiteralValue::Empty);
3684 row.push(v);
3685 }
3686 rows.push(row);
3687 }
3688 LiteralValue::Array(rows)
3689 }
3690 NamedDefinition::Cell(cell_ref) => {
3691 let sheet_name = self.graph.sheet_name(cell_ref.sheet_id);
3692 let row = cell_ref.coord.row() + 1;
3693 let col = cell_ref.coord.col() + 1;
3694 let v = self
3695 .get_cell_value(sheet_name, row, col)
3696 .unwrap_or(LiteralValue::Empty);
3697 LiteralValue::Array(vec![vec![v]])
3698 }
3699 NamedDefinition::Literal(v) => LiteralValue::Array(vec![vec![v.clone()]]),
3700 NamedDefinition::Formula { ast, .. } => {
3701 let context_sheet = match named_range.scope {
3702 NameScope::Sheet(id) => id,
3703 NameScope::Workbook => sheet_id,
3704 };
3705 let sheet_name = self.graph.sheet_name(context_sheet);
3706 let cell_ref = self
3707 .graph
3708 .get_cell_ref(vertex_id)
3709 .unwrap_or_else(|| self.graph.make_cell_ref(sheet_name, 0, 0));
3710 let interpreter = Interpreter::new_with_cell(self, sheet_name, cell_ref);
3711 match interpreter.evaluate_ast(ast) {
3712 Ok(cv) => {
3713 let v = cv.into_literal();
3714 match v {
3715 LiteralValue::Array(_) => v,
3716 other => LiteralValue::Array(vec![vec![other]]),
3717 }
3718 }
3719 Err(err) => LiteralValue::Error(err),
3720 }
3721 }
3722 };
3723
3724 self.graph.update_vertex_value(vertex_id, out.clone());
3725 Ok(out)
3726 }
3727
3728 pub fn evaluate_until(
3730 &mut self,
3731 targets: &[(&str, u32, u32)],
3732 ) -> Result<EvalResult, ExcelError> {
3733 #[cfg(feature = "tracing")]
3734 let _span_eval = tracing::info_span!("evaluate_until", targets = targets.len()).entered();
3735 let start = web_time::Instant::now();
3736 let _source_cache = self.source_cache_session();
3737
3738 let mut target_addrs = Vec::new();
3740 for (sheet, row, col) in targets {
3741 let sheet_id = self.graph.sheet_id_mut(sheet);
3744 let coord = Coord::from_excel(*row, *col, true, true);
3745 target_addrs.push(CellRef::new(sheet_id, coord));
3746 }
3747
3748 let mut target_vertex_ids = Vec::new();
3750 for addr in &target_addrs {
3751 if let Some(vertex_id) = self.graph.get_vertex_id_for_address(addr) {
3752 target_vertex_ids.push(*vertex_id);
3753 }
3754 }
3755
3756 if target_vertex_ids.is_empty() {
3757 return Ok(EvalResult {
3758 computed_vertices: 0,
3759 cycle_errors: 0,
3760 elapsed: start.elapsed(),
3761 });
3762 }
3763
3764 #[cfg(feature = "tracing")]
3766 let _span_sub = tracing::info_span!("demand_subgraph_build").entered();
3767 let (precedents_to_eval, vdeps) = self.build_demand_subgraph(&target_vertex_ids);
3768 #[cfg(feature = "tracing")]
3769 drop(_span_sub);
3770
3771 if precedents_to_eval.is_empty() {
3772 return Ok(EvalResult {
3773 computed_vertices: 0,
3774 cycle_errors: 0,
3775 elapsed: start.elapsed(),
3776 });
3777 }
3778
3779 let scheduler = Scheduler::new(&self.graph);
3781 #[cfg(feature = "tracing")]
3782 let _span_sched =
3783 tracing::info_span!("schedule_build", vertices = precedents_to_eval.len()).entered();
3784 let schedule = scheduler.create_schedule_with_virtual(&precedents_to_eval, &vdeps)?;
3785 #[cfg(feature = "tracing")]
3786 drop(_span_sched);
3787
3788 let mut cycle_errors = 0;
3790 for cycle in &schedule.cycles {
3791 cycle_errors += 1;
3792 let circ_error = LiteralValue::Error(
3793 ExcelError::new(ExcelErrorKind::Circ)
3794 .with_message("Circular dependency detected".to_string()),
3795 );
3796 for &vertex_id in cycle {
3797 self.graph
3798 .update_vertex_value(vertex_id, circ_error.clone());
3799 self.mirror_vertex_value_to_overlay(vertex_id, &circ_error);
3800 }
3801 }
3802
3803 let mut computed_vertices = 0;
3805 for layer in &schedule.layers {
3806 if self.thread_pool.is_some() && layer.vertices.len() > 1 {
3807 computed_vertices += self.evaluate_layer_parallel(layer)?;
3808 } else {
3809 computed_vertices += self.evaluate_layer_sequential(layer)?;
3810 }
3811 }
3812
3813 self.graph.clear_dirty_flags(&precedents_to_eval);
3817
3818 self.graph.redirty_volatiles();
3820
3821 Ok(EvalResult {
3822 computed_vertices,
3823 cycle_errors,
3824 elapsed: start.elapsed(),
3825 })
3826 }
3827
3828 fn evaluate_until_with_delta_collector(
3829 &mut self,
3830 targets: &[(&str, u32, u32)],
3831 delta: &mut DeltaCollector,
3832 ) -> Result<EvalResult, ExcelError> {
3833 #[cfg(feature = "tracing")]
3834 let _span_eval =
3835 tracing::info_span!("evaluate_until_with_delta", targets = targets.len()).entered();
3836 let start = web_time::Instant::now();
3837 let _source_cache = self.source_cache_session();
3838
3839 let mut target_addrs = Vec::new();
3840 for (sheet, row, col) in targets {
3841 let sheet_id = self.graph.sheet_id_mut(sheet);
3842 let coord = Coord::from_excel(*row, *col, true, true);
3843 target_addrs.push(CellRef::new(sheet_id, coord));
3844 }
3845
3846 let mut target_vertex_ids = Vec::new();
3847 for addr in &target_addrs {
3848 if let Some(vertex_id) = self.graph.get_vertex_id_for_address(addr) {
3849 target_vertex_ids.push(*vertex_id);
3850 }
3851 }
3852
3853 if target_vertex_ids.is_empty() {
3854 return Ok(EvalResult {
3855 computed_vertices: 0,
3856 cycle_errors: 0,
3857 elapsed: start.elapsed(),
3858 });
3859 }
3860
3861 let (precedents_to_eval, vdeps) = self.build_demand_subgraph(&target_vertex_ids);
3862
3863 if precedents_to_eval.is_empty() {
3864 return Ok(EvalResult {
3865 computed_vertices: 0,
3866 cycle_errors: 0,
3867 elapsed: start.elapsed(),
3868 });
3869 }
3870
3871 let scheduler = Scheduler::new(&self.graph);
3872 let schedule = scheduler.create_schedule_with_virtual(&precedents_to_eval, &vdeps)?;
3873
3874 let mut cycle_errors = 0;
3875 let circ_error = LiteralValue::Error(
3876 ExcelError::new(ExcelErrorKind::Circ)
3877 .with_message("Circular dependency detected".to_string()),
3878 );
3879 for cycle in &schedule.cycles {
3880 cycle_errors += 1;
3881 for &vertex_id in cycle {
3882 if delta.mode != DeltaMode::Off
3883 && let Some(cell) = self.graph.get_cell_ref_for_vertex(vertex_id)
3884 {
3885 let sheet_name = self.graph.sheet_name(cell.sheet_id);
3886 let old = self
3887 .read_cell_value(sheet_name, cell.coord.row() + 1, cell.coord.col() + 1)
3888 .unwrap_or(LiteralValue::Empty);
3889 if old != circ_error {
3890 delta.record_cell(cell.sheet_id, cell.coord.row(), cell.coord.col());
3891 }
3892 }
3893 self.graph
3894 .update_vertex_value(vertex_id, circ_error.clone());
3895 self.mirror_vertex_value_to_overlay(vertex_id, &circ_error);
3896 }
3897 }
3898
3899 let mut computed_vertices = 0;
3900 for layer in &schedule.layers {
3901 if self.thread_pool.is_some() && layer.vertices.len() > 1 {
3902 computed_vertices += self.evaluate_layer_parallel_with_delta(layer, delta)?;
3903 } else {
3904 computed_vertices += self.evaluate_layer_sequential_with_delta(layer, delta)?;
3905 }
3906 }
3907
3908 self.graph.clear_dirty_flags(&precedents_to_eval);
3909 self.graph.redirty_volatiles();
3910
3911 Ok(EvalResult {
3912 computed_vertices,
3913 cycle_errors,
3914 elapsed: start.elapsed(),
3915 })
3916 }
3917
3918 pub fn build_recalc_plan(&self) -> Result<RecalcPlan, ExcelError> {
3920 let mut vertices: Vec<VertexId> = self.graph.vertices_with_formulas().collect();
3921 vertices.sort_unstable();
3922 if vertices.is_empty() {
3923 return Ok(RecalcPlan {
3924 schedule: crate::engine::Schedule {
3925 layers: Vec::new(),
3926 cycles: Vec::new(),
3927 },
3928 });
3929 }
3930
3931 let scheduler = Scheduler::new(&self.graph);
3932 let schedule = scheduler.create_schedule(&vertices)?;
3933 Ok(RecalcPlan { schedule })
3934 }
3935
3936 pub fn evaluate_recalc_plan(&mut self, plan: &RecalcPlan) -> Result<EvalResult, ExcelError> {
3938 let _source_cache = self.source_cache_session();
3939 self.validate_deterministic_mode()?;
3940 if self.config.defer_graph_building {
3941 self.build_graph_all()?;
3942 }
3943
3944 let start = web_time::Instant::now();
3945 let dirty_vertices = self.graph.get_evaluation_vertices();
3946 if dirty_vertices.is_empty() {
3947 return Ok(EvalResult {
3948 computed_vertices: 0,
3949 cycle_errors: 0,
3950 elapsed: start.elapsed(),
3951 });
3952 }
3953
3954 let dirty_set: FxHashSet<VertexId> = dirty_vertices.iter().copied().collect();
3955 let mut computed_vertices = 0;
3956 let mut cycle_errors = 0;
3957
3958 if !plan.schedule.cycles.is_empty() {
3959 let circ_error = LiteralValue::Error(
3960 ExcelError::new(ExcelErrorKind::Circ)
3961 .with_message("Circular dependency detected".to_string()),
3962 );
3963 for cycle in &plan.schedule.cycles {
3964 if !cycle.iter().any(|v| dirty_set.contains(v)) {
3965 continue;
3966 }
3967 cycle_errors += 1;
3968 for &vertex_id in cycle {
3969 if dirty_set.contains(&vertex_id) {
3970 self.graph
3971 .update_vertex_value(vertex_id, circ_error.clone());
3972 self.mirror_vertex_value_to_overlay(vertex_id, &circ_error);
3973 }
3974 }
3975 }
3976 }
3977
3978 for layer in &plan.schedule.layers {
3979 let work: Vec<VertexId> = layer
3980 .vertices
3981 .iter()
3982 .copied()
3983 .filter(|v| dirty_set.contains(v))
3984 .collect();
3985 if work.is_empty() {
3986 continue;
3987 }
3988 let temp_layer = crate::engine::scheduler::Layer { vertices: work };
3989 if self.thread_pool.is_some() && temp_layer.vertices.len() > 1 {
3990 computed_vertices += self.evaluate_layer_parallel(&temp_layer)?;
3991 } else {
3992 computed_vertices += self.evaluate_layer_sequential(&temp_layer)?;
3993 }
3994 }
3995
3996 self.graph.clear_dirty_flags(&dirty_vertices);
3997 self.graph.redirty_volatiles();
3998
3999 Ok(EvalResult {
4000 computed_vertices,
4001 cycle_errors,
4002 elapsed: start.elapsed(),
4003 })
4004 }
4005
4006 pub fn evaluate_all(&mut self) -> Result<EvalResult, ExcelError> {
4008 let _source_cache = self.source_cache_session();
4009 self.validate_deterministic_mode()?;
4010 if self.config.defer_graph_building {
4011 self.build_graph_all()?;
4013 }
4014 #[cfg(feature = "tracing")]
4015 let _span_eval = tracing::info_span!("evaluate_all").entered();
4016 let start = web_time::Instant::now();
4017 let mut computed_vertices = 0;
4018 let mut cycle_errors = 0;
4019
4020 let to_evaluate = self.graph.get_evaluation_vertices();
4021 if to_evaluate.is_empty() {
4022 return Ok(EvalResult {
4023 computed_vertices,
4024 cycle_errors,
4025 elapsed: start.elapsed(),
4026 });
4027 }
4028
4029 let scheduler = Scheduler::new(&self.graph);
4030 let schedule = scheduler.create_schedule(&to_evaluate)?;
4031
4032 for cycle in &schedule.cycles {
4034 cycle_errors += 1;
4035 let circ_error = LiteralValue::Error(
4036 ExcelError::new(ExcelErrorKind::Circ)
4037 .with_message("Circular dependency detected".to_string()),
4038 );
4039 for &vertex_id in cycle {
4040 self.graph
4041 .update_vertex_value(vertex_id, circ_error.clone());
4042 self.mirror_vertex_value_to_overlay(vertex_id, &circ_error);
4043 }
4044 }
4045
4046 for layer in &schedule.layers {
4048 if self.thread_pool.is_some() && layer.vertices.len() > 1 {
4049 computed_vertices += self.evaluate_layer_parallel(layer)?;
4050 } else {
4051 computed_vertices += self.evaluate_layer_sequential(layer)?;
4052 }
4053 }
4054
4055 self.graph.clear_dirty_flags(&to_evaluate);
4057
4058 self.graph.redirty_volatiles();
4060
4061 self.recalc_epoch = self.recalc_epoch.wrapping_add(1);
4063
4064 Ok(EvalResult {
4065 computed_vertices,
4066 cycle_errors,
4067 elapsed: start.elapsed(),
4068 })
4069 }
4070
4071 pub fn evaluate_all_with_delta(&mut self) -> Result<(EvalResult, EvalDelta), ExcelError> {
4072 let mut collector = DeltaCollector::new(DeltaMode::Cells);
4073 let result = self.evaluate_all_with_delta_collector(&mut collector)?;
4074 Ok((result, collector.finish()))
4075 }
4076
4077 fn evaluate_all_with_delta_collector(
4078 &mut self,
4079 delta: &mut DeltaCollector,
4080 ) -> Result<EvalResult, ExcelError> {
4081 let _source_cache = self.source_cache_session();
4082 if self.config.defer_graph_building {
4083 self.build_graph_all()?;
4084 }
4085 #[cfg(feature = "tracing")]
4086 let _span_eval = tracing::info_span!("evaluate_all_with_delta").entered();
4087 let start = web_time::Instant::now();
4088 let mut computed_vertices = 0;
4089 let mut cycle_errors = 0;
4090
4091 let to_evaluate = self.graph.get_evaluation_vertices();
4092 if to_evaluate.is_empty() {
4093 return Ok(EvalResult {
4094 computed_vertices,
4095 cycle_errors,
4096 elapsed: start.elapsed(),
4097 });
4098 }
4099
4100 let scheduler = Scheduler::new(&self.graph);
4101 let schedule = scheduler.create_schedule(&to_evaluate)?;
4102
4103 let circ_error = LiteralValue::Error(
4104 ExcelError::new(ExcelErrorKind::Circ)
4105 .with_message("Circular dependency detected".to_string()),
4106 );
4107 for cycle in &schedule.cycles {
4108 cycle_errors += 1;
4109 for &vertex_id in cycle {
4110 if delta.mode != DeltaMode::Off
4111 && let Some(cell) = self.graph.get_cell_ref_for_vertex(vertex_id)
4112 {
4113 let sheet_name = self.graph.sheet_name(cell.sheet_id);
4114 let old = self
4115 .read_cell_value(sheet_name, cell.coord.row() + 1, cell.coord.col() + 1)
4116 .unwrap_or(LiteralValue::Empty);
4117 if old != circ_error {
4118 delta.record_cell(cell.sheet_id, cell.coord.row(), cell.coord.col());
4119 }
4120 }
4121 self.graph
4122 .update_vertex_value(vertex_id, circ_error.clone());
4123 self.mirror_vertex_value_to_overlay(vertex_id, &circ_error);
4124 }
4125 }
4126
4127 for layer in &schedule.layers {
4128 if self.thread_pool.is_some() && layer.vertices.len() > 1 {
4129 computed_vertices += self.evaluate_layer_parallel_with_delta(layer, delta)?;
4130 } else {
4131 computed_vertices += self.evaluate_layer_sequential_with_delta(layer, delta)?;
4132 }
4133 }
4134
4135 self.graph.clear_dirty_flags(&to_evaluate);
4136 self.graph.redirty_volatiles();
4137 self.recalc_epoch = self.recalc_epoch.wrapping_add(1);
4138
4139 Ok(EvalResult {
4140 computed_vertices,
4141 cycle_errors,
4142 elapsed: start.elapsed(),
4143 })
4144 }
4145
4146 pub fn evaluate_cell(
4156 &mut self,
4157 sheet: &str,
4158 row: u32,
4159 col: u32,
4160 ) -> Result<Option<LiteralValue>, ExcelError> {
4161 if row == 0 || col == 0 {
4162 return Err(ExcelError::new(ExcelErrorKind::Ref)
4163 .with_message("Row and column must be >= 1".to_string()));
4164 }
4165
4166 if self.config.defer_graph_building {
4167 self.build_graph_for_sheets(std::iter::once(sheet))?;
4168 }
4169
4170 let result = self.evaluate_cells(&[(sheet, row, col)])?;
4171
4172 match result.len() {
4173 0 => Ok(None),
4174 1 => {
4175 let v = result.into_iter().next().unwrap();
4176 Ok(v)
4177 }
4178 _ => unreachable!("evaluate_cells returned unexpected length"),
4179 }
4180 }
4181
4182 pub fn evaluate_cells(
4189 &mut self,
4190 targets: &[(&str, u32, u32)],
4191 ) -> Result<Vec<Option<LiteralValue>>, ExcelError> {
4192 self.validate_deterministic_mode()?;
4193 if targets.is_empty() {
4194 return Ok(Vec::new());
4195 }
4196 if self.config.defer_graph_building {
4197 let mut sheets: rustc_hash::FxHashSet<&str> = rustc_hash::FxHashSet::default();
4198 for (s, _, _) in targets.iter() {
4199 sheets.insert(*s);
4200 }
4201 self.build_graph_for_sheets(sheets.iter().cloned())?;
4202 }
4203 self.evaluate_until(targets)?;
4204 Ok(targets
4205 .iter()
4206 .map(|(s, r, c)| self.get_cell_value(s, *r, *c))
4207 .collect())
4208 }
4209
4210 pub fn evaluate_cells_cancellable(
4211 &mut self,
4212 targets: &[(&str, u32, u32)],
4213 cancel_flag: Arc<AtomicBool>,
4214 ) -> Result<Vec<Option<LiteralValue>>, ExcelError> {
4215 self.active_cancel_flag = Some(cancel_flag.clone());
4216 let res = self.evaluate_cells_cancellable_impl(targets, &cancel_flag);
4217 self.active_cancel_flag = None;
4218 res
4219 }
4220
4221 fn evaluate_cells_cancellable_impl(
4222 &mut self,
4223 targets: &[(&str, u32, u32)],
4224 cancel_flag: &AtomicBool,
4225 ) -> Result<Vec<Option<LiteralValue>>, ExcelError> {
4226 self.validate_deterministic_mode()?;
4227 if targets.is_empty() {
4228 return Ok(Vec::new());
4229 }
4230 if self.config.defer_graph_building {
4231 let mut sheets: rustc_hash::FxHashSet<&str> = rustc_hash::FxHashSet::default();
4232 for (s, _, _) in targets.iter() {
4233 sheets.insert(*s);
4234 }
4235 self.build_graph_for_sheets(sheets.iter().cloned())?;
4236 }
4237
4238 let a1_targets: Vec<String> = targets
4241 .iter()
4242 .map(|(s, r, c)| {
4243 format!("{}!{}", s, col_letters_from_1based(*c).unwrap()) + &r.to_string()
4244 })
4245 .collect();
4246 let a1_refs: Vec<&str> = a1_targets.iter().map(|s| s.as_str()).collect();
4247
4248 self.evaluate_until_cancellable_impl(&a1_refs, cancel_flag)?;
4249
4250 Ok(targets
4251 .iter()
4252 .map(|(s, r, c)| self.get_cell_value(s, *r, *c))
4253 .collect())
4254 }
4255
4256 pub fn evaluate_cells_with_delta(
4257 &mut self,
4258 targets: &[(&str, u32, u32)],
4259 ) -> Result<(Vec<Option<LiteralValue>>, EvalDelta), ExcelError> {
4260 self.validate_deterministic_mode()?;
4261 if targets.is_empty() {
4262 return Ok((Vec::new(), EvalDelta::default()));
4263 }
4264 if self.config.defer_graph_building {
4265 let mut sheets: rustc_hash::FxHashSet<&str> = rustc_hash::FxHashSet::default();
4266 for (s, _, _) in targets.iter() {
4267 sheets.insert(*s);
4268 }
4269 self.build_graph_for_sheets(sheets.iter().cloned())?;
4270 }
4271 let mut collector = DeltaCollector::new(DeltaMode::Cells);
4272 self.evaluate_until_with_delta_collector(targets, &mut collector)?;
4273 let values = targets
4274 .iter()
4275 .map(|(s, r, c)| self.get_cell_value(s, *r, *c))
4276 .collect();
4277 Ok((values, collector.finish()))
4278 }
4279
4280 pub fn get_eval_plan(&self, targets: &[(&str, u32, u32)]) -> Result<EvalPlan, ExcelError> {
4282 if targets.is_empty() {
4283 return Ok(EvalPlan {
4284 total_vertices_to_evaluate: 0,
4285 layers: Vec::new(),
4286 cycles_detected: 0,
4287 dirty_count: 0,
4288 volatile_count: 0,
4289 parallel_enabled: self.config.enable_parallel && self.thread_pool.is_some(),
4290 estimated_parallel_layers: 0,
4291 target_cells: Vec::new(),
4292 });
4293 }
4294 if self.config.defer_graph_building {
4295 return Err(ExcelError::new(ExcelErrorKind::Value).with_message(
4296 "Evaluation plan requested with deferred graph; build first or call evaluate_*",
4297 ));
4298 }
4299
4300 let addresses: Vec<String> = targets
4302 .iter()
4303 .map(|(s, r, c)| format!("{}!{}{}", s, Self::col_to_letters(*c), r))
4304 .collect();
4305
4306 let mut target_addrs = Vec::new();
4308 for (sheet, row, col) in targets {
4309 if let Some(sheet_id) = self.graph.sheet_id(sheet) {
4310 let coord = Coord::from_excel(*row, *col, true, true);
4311 target_addrs.push(CellRef::new(sheet_id, coord));
4312 }
4313 }
4314
4315 let mut target_vertex_ids = Vec::new();
4317 for addr in &target_addrs {
4318 if let Some(vertex_id) = self.graph.get_vertex_id_for_address(addr) {
4319 target_vertex_ids.push(*vertex_id);
4320 }
4321 }
4322
4323 if target_vertex_ids.is_empty() {
4324 return Ok(EvalPlan {
4325 total_vertices_to_evaluate: 0,
4326 layers: Vec::new(),
4327 cycles_detected: 0,
4328 dirty_count: 0,
4329 volatile_count: 0,
4330 parallel_enabled: self.config.enable_parallel && self.thread_pool.is_some(),
4331 estimated_parallel_layers: 0,
4332 target_cells: addresses,
4333 });
4334 }
4335
4336 let (precedents_to_eval, vdeps) = self.build_demand_subgraph(&target_vertex_ids);
4338
4339 if precedents_to_eval.is_empty() {
4340 return Ok(EvalPlan {
4341 total_vertices_to_evaluate: 0,
4342 layers: Vec::new(),
4343 cycles_detected: 0,
4344 dirty_count: 0,
4345 volatile_count: 0,
4346 parallel_enabled: self.config.enable_parallel && self.thread_pool.is_some(),
4347 estimated_parallel_layers: 0,
4348 target_cells: addresses,
4349 });
4350 }
4351
4352 let mut dirty_count = 0;
4354 let mut volatile_count = 0;
4355 for &vertex_id in &precedents_to_eval {
4356 if self.graph.is_dirty(vertex_id) {
4357 dirty_count += 1;
4358 }
4359 if self.graph.is_volatile(vertex_id) {
4360 volatile_count += 1;
4361 }
4362 }
4363
4364 let scheduler = Scheduler::new(&self.graph);
4366 let schedule = scheduler.create_schedule_with_virtual(&precedents_to_eval, &vdeps)?;
4367
4368 let mut layers = Vec::new();
4370 let mut estimated_parallel_layers = 0;
4371 let parallel_enabled = self.config.enable_parallel && self.thread_pool.is_some();
4372
4373 for layer in &schedule.layers {
4374 let parallel_eligible = parallel_enabled && layer.vertices.len() > 1;
4375 if parallel_eligible {
4376 estimated_parallel_layers += 1;
4377 }
4378
4379 let sample_cells: Vec<String> = layer
4381 .vertices
4382 .iter()
4383 .take(5)
4384 .filter_map(|&vertex_id| {
4385 self.graph
4386 .get_cell_ref_for_vertex(vertex_id)
4387 .map(|cell_ref| {
4388 let sheet_name = self.graph.sheet_name(cell_ref.sheet_id);
4389 format!(
4390 "{}!{}{}",
4391 sheet_name,
4392 Self::col_to_letters(cell_ref.coord.col()),
4393 cell_ref.coord.row() + 1
4394 )
4395 })
4396 })
4397 .collect();
4398
4399 layers.push(LayerInfo {
4400 vertex_count: layer.vertices.len(),
4401 parallel_eligible,
4402 sample_cells,
4403 });
4404 }
4405
4406 Ok(EvalPlan {
4407 total_vertices_to_evaluate: precedents_to_eval.len(),
4408 layers,
4409 cycles_detected: schedule.cycles.len(),
4410 dirty_count,
4411 volatile_count,
4412 parallel_enabled,
4413 estimated_parallel_layers,
4414 target_cells: addresses,
4415 })
4416 }
4417
4418 fn build_demand_subgraph(
4421 &self,
4422 target_vertices: &[VertexId],
4423 ) -> (
4424 Vec<VertexId>,
4425 rustc_hash::FxHashMap<VertexId, Vec<VertexId>>,
4426 ) {
4427 #[cfg(feature = "tracing")]
4428 let _span =
4429 tracing::info_span!("demand_subgraph", targets = target_vertices.len()).entered();
4430 use rustc_hash::{FxHashMap, FxHashSet};
4431
4432 let mut to_evaluate: FxHashSet<VertexId> = FxHashSet::default();
4433 let mut visited: FxHashSet<VertexId> = FxHashSet::default();
4434 let mut stack: Vec<VertexId> = Vec::new();
4435 let mut vdeps: FxHashMap<VertexId, Vec<VertexId>> = FxHashMap::default(); for &t in target_vertices {
4438 stack.push(t);
4439 }
4440
4441 while let Some(v) = stack.pop() {
4442 if !visited.insert(v) {
4443 continue;
4444 }
4445 if !self.graph.vertex_exists(v) {
4446 continue;
4447 }
4448 match self.graph.get_vertex_kind(v) {
4450 VertexKind::FormulaScalar | VertexKind::FormulaArray => {
4451 if self.graph.is_dirty(v) || self.graph.is_volatile(v) {
4452 to_evaluate.insert(v);
4453 }
4454 }
4455 _ => {}
4456 }
4457
4458 for dep in self.graph.get_dependencies(v) {
4460 if self.graph.vertex_exists(dep) {
4461 match self.graph.get_vertex_kind(dep) {
4462 VertexKind::FormulaScalar | VertexKind::FormulaArray => {
4463 if !visited.contains(&dep) {
4464 stack.push(dep);
4465 }
4466 }
4467 _ => {}
4468 }
4469 }
4470 }
4471
4472 if let Some(ranges) = self.graph.get_range_dependencies(v) {
4474 let current_sheet_id = self.graph.get_vertex_sheet_id(v);
4475 for r in ranges {
4476 let sheet_id = match r.sheet {
4477 formualizer_common::SheetLocator::Id(id) => id,
4478 _ => current_sheet_id,
4479 };
4480 let sheet_name = self.graph.sheet_name(sheet_id);
4481
4482 let mut sr = r.start_row.map(|b| b.index + 1);
4484 let mut sc = r.start_col.map(|b| b.index + 1);
4485 let mut er = r.end_row.map(|b| b.index + 1);
4486 let mut ec = r.end_col.map(|b| b.index + 1);
4487
4488 if sr.is_none() && er.is_none() {
4489 let scv = sc.unwrap_or(1);
4490 let ecv = ec.unwrap_or(scv);
4491 if let Some((min_r, max_r)) =
4492 self.used_rows_for_columns(sheet_name, scv, ecv)
4493 {
4494 sr = Some(min_r);
4495 er = Some(max_r);
4496 } else if let Some((max_rows, _)) = self.sheet_bounds(sheet_name) {
4497 sr = Some(1);
4498 er = Some(self.config.max_open_ended_rows);
4499 }
4500 }
4501 if sc.is_none() && ec.is_none() {
4502 let srv = sr.unwrap_or(1);
4503 let erv = er.unwrap_or(srv);
4504 if let Some((min_c, max_c)) = self.used_cols_for_rows(sheet_name, srv, erv)
4505 {
4506 sc = Some(min_c);
4507 ec = Some(max_c);
4508 } else if let Some((_, max_cols)) = self.sheet_bounds(sheet_name) {
4509 sc = Some(1);
4510 ec = Some(self.config.max_open_ended_cols);
4511 }
4512 }
4513 if sr.is_some() && er.is_none() {
4514 let scv = sc.unwrap_or(1);
4515 let ecv = ec.unwrap_or(scv);
4516 if let Some((_, max_r)) = self.used_rows_for_columns(sheet_name, scv, ecv) {
4517 er = Some(max_r);
4518 } else if let Some((max_rows, _)) = self.sheet_bounds(sheet_name) {
4519 er = Some(self.config.max_open_ended_rows);
4520 }
4521 }
4522 if er.is_some() && sr.is_none() {
4523 let scv = sc.unwrap_or(1);
4524 let ecv = ec.unwrap_or(scv);
4525 if let Some((min_r, _)) = self.used_rows_for_columns(sheet_name, scv, ecv) {
4526 sr = Some(min_r);
4527 } else {
4528 sr = Some(1);
4529 }
4530 }
4531 if sc.is_some() && ec.is_none() {
4532 let srv = sr.unwrap_or(1);
4533 let erv = er.unwrap_or(srv);
4534 if let Some((_, max_c)) = self.used_cols_for_rows(sheet_name, srv, erv) {
4535 ec = Some(max_c);
4536 } else if let Some((_, max_cols)) = self.sheet_bounds(sheet_name) {
4537 ec = Some(self.config.max_open_ended_cols);
4538 }
4539 }
4540 if ec.is_some() && sc.is_none() {
4541 let srv = sr.unwrap_or(1);
4542 let erv = er.unwrap_or(srv);
4543 if let Some((min_c, _)) = self.used_cols_for_rows(sheet_name, srv, erv) {
4544 sc = Some(min_c);
4545 } else {
4546 sc = Some(1);
4547 }
4548 }
4549
4550 let sr = sr.unwrap_or(1);
4551 let sc = sc.unwrap_or(1);
4552 let er = er.unwrap_or(sr.saturating_sub(1));
4553 let ec = ec.unwrap_or(sc.saturating_sub(1));
4554 if er < sr || ec < sc {
4555 continue;
4556 }
4557
4558 if let Some(index) = self.graph.sheet_index(sheet_id) {
4559 let sr0 = sr.saturating_sub(1);
4560 let er0 = er.saturating_sub(1);
4561 let sc0 = sc.saturating_sub(1);
4562 let ec0 = ec.saturating_sub(1);
4563 for u in index.vertices_in_col_range(sc0, ec0) {
4565 let pc = self.graph.vertex_coord(u);
4566 let row0 = pc.row();
4567 if row0 < sr0 || row0 > er0 {
4568 continue;
4569 }
4570 match self.graph.get_vertex_kind(u) {
4571 VertexKind::FormulaScalar | VertexKind::FormulaArray => {
4572 if (self.graph.is_dirty(u) || self.graph.is_volatile(u))
4574 && u != v
4575 {
4576 vdeps.entry(v).or_default().push(u);
4577 if !visited.contains(&u) {
4578 stack.push(u);
4579 }
4580 }
4581 }
4582 _ => {}
4583 }
4584 }
4585 }
4586 }
4587 }
4588 }
4589
4590 let mut result: Vec<VertexId> = to_evaluate.into_iter().collect();
4591 result.sort_unstable();
4592 for deps in vdeps.values_mut() {
4594 deps.sort_unstable();
4595 deps.dedup();
4596 }
4597 (result, vdeps)
4598 }
4599
4600 fn col_to_letters(col: u32) -> String {
4602 col_letters_from_1based(col).expect("column index must be >= 1")
4603 }
4604
4605 pub fn evaluate_all_cancellable(
4607 &mut self,
4608 cancel_flag: Arc<AtomicBool>,
4609 ) -> Result<EvalResult, ExcelError> {
4610 self.active_cancel_flag = Some(cancel_flag.clone());
4611 let res = self.evaluate_all_cancellable_impl(&cancel_flag);
4612 self.active_cancel_flag = None;
4613 res
4614 }
4615
4616 fn evaluate_all_cancellable_impl(
4617 &mut self,
4618 cancel_flag: &AtomicBool,
4619 ) -> Result<EvalResult, ExcelError> {
4620 self.validate_deterministic_mode()?;
4621 let start = web_time::Instant::now();
4622 let mut computed_vertices = 0;
4623 let mut cycle_errors = 0;
4624
4625 let to_evaluate = self.graph.get_evaluation_vertices();
4626 if to_evaluate.is_empty() {
4627 return Ok(EvalResult {
4628 computed_vertices,
4629 cycle_errors,
4630 elapsed: start.elapsed(),
4631 });
4632 }
4633
4634 let scheduler = Scheduler::new(&self.graph);
4635 let schedule = scheduler.create_schedule(&to_evaluate)?;
4636
4637 for cycle in &schedule.cycles {
4639 if cancel_flag.load(Ordering::Relaxed) {
4641 return Err(ExcelError::new(ExcelErrorKind::Cancelled)
4642 .with_message("Evaluation cancelled during cycle handling".to_string()));
4643 }
4644
4645 cycle_errors += 1;
4646 let circ_error = LiteralValue::Error(
4647 ExcelError::new(ExcelErrorKind::Circ)
4648 .with_message("Circular dependency detected".to_string()),
4649 );
4650 for &vertex_id in cycle {
4651 self.graph
4652 .update_vertex_value(vertex_id, circ_error.clone());
4653 self.mirror_vertex_value_to_overlay(vertex_id, &circ_error);
4654 }
4655 }
4656
4657 for layer in &schedule.layers {
4659 if cancel_flag.load(Ordering::Relaxed) {
4661 return Err(ExcelError::new(ExcelErrorKind::Cancelled)
4662 .with_message("Evaluation cancelled between layers".to_string()));
4663 }
4664
4665 if self.thread_pool.is_some() && layer.vertices.len() > 1 {
4667 computed_vertices +=
4668 self.evaluate_layer_parallel_cancellable(layer, cancel_flag)?;
4669 } else {
4670 computed_vertices +=
4671 self.evaluate_layer_sequential_cancellable(layer, cancel_flag)?;
4672 }
4673 }
4674
4675 self.graph.clear_dirty_flags(&to_evaluate);
4677
4678 self.graph.redirty_volatiles();
4680
4681 Ok(EvalResult {
4682 computed_vertices,
4683 cycle_errors,
4684 elapsed: start.elapsed(),
4685 })
4686 }
4687
4688 pub fn evaluate_until_cancellable(
4690 &mut self,
4691 targets: &[&str],
4692 cancel_flag: Arc<AtomicBool>,
4693 ) -> Result<EvalResult, ExcelError> {
4694 self.active_cancel_flag = Some(cancel_flag.clone());
4695 let res = self.evaluate_until_cancellable_impl(targets, &cancel_flag);
4696 self.active_cancel_flag = None;
4697 res
4698 }
4699
4700 fn evaluate_until_cancellable_impl(
4701 &mut self,
4702 targets: &[&str],
4703 cancel_flag: &AtomicBool,
4704 ) -> Result<EvalResult, ExcelError> {
4705 let start = web_time::Instant::now();
4706
4707 let mut target_addrs = Vec::new();
4709 for target in targets {
4710 let (sheet, row, col) = self.parse_a1_notation(target)?;
4711 let sheet_id = self.graph.sheet_id_mut(&sheet);
4712 let coord = Coord::from_excel(row, col, true, true);
4713 target_addrs.push(CellRef::new(sheet_id, coord));
4714 }
4715
4716 let mut target_vertex_ids = Vec::new();
4718 for addr in &target_addrs {
4719 if let Some(vertex_id) = self.graph.get_vertex_id_for_address(addr) {
4720 target_vertex_ids.push(*vertex_id);
4721 }
4722 }
4723
4724 if target_vertex_ids.is_empty() {
4725 return Ok(EvalResult {
4726 computed_vertices: 0,
4727 cycle_errors: 0,
4728 elapsed: start.elapsed(),
4729 });
4730 }
4731
4732 let (precedents_to_eval, vdeps) = self.build_demand_subgraph(&target_vertex_ids);
4734
4735 if precedents_to_eval.is_empty() {
4736 return Ok(EvalResult {
4737 computed_vertices: 0,
4738 cycle_errors: 0,
4739 elapsed: start.elapsed(),
4740 });
4741 }
4742
4743 let scheduler = Scheduler::new(&self.graph);
4745 let schedule = scheduler.create_schedule_with_virtual(&precedents_to_eval, &vdeps)?;
4746
4747 let mut cycle_errors = 0;
4749 for cycle in &schedule.cycles {
4750 if cancel_flag.load(Ordering::Relaxed) {
4752 return Err(ExcelError::new(ExcelErrorKind::Cancelled).with_message(
4753 "Demand-driven evaluation cancelled during cycle handling".to_string(),
4754 ));
4755 }
4756
4757 cycle_errors += 1;
4758 let circ_error = LiteralValue::Error(
4759 ExcelError::new(ExcelErrorKind::Circ)
4760 .with_message("Circular dependency detected".to_string()),
4761 );
4762 for &vertex_id in cycle {
4763 self.graph
4764 .update_vertex_value(vertex_id, circ_error.clone());
4765 self.mirror_vertex_value_to_overlay(vertex_id, &circ_error);
4766 }
4767 }
4768
4769 let mut computed_vertices = 0;
4771 for layer in &schedule.layers {
4772 if cancel_flag.load(Ordering::Relaxed) {
4774 return Err(ExcelError::new(ExcelErrorKind::Cancelled).with_message(
4775 "Demand-driven evaluation cancelled between layers".to_string(),
4776 ));
4777 }
4778
4779 if self.thread_pool.is_some() && layer.vertices.len() > 1 {
4781 computed_vertices +=
4782 self.evaluate_layer_parallel_cancellable(layer, cancel_flag)?;
4783 } else {
4784 computed_vertices +=
4785 self.evaluate_layer_sequential_cancellable_demand_driven(layer, cancel_flag)?;
4786 }
4787 }
4788
4789 self.graph.clear_dirty_flags(&precedents_to_eval);
4791
4792 self.graph.redirty_volatiles();
4794
4795 Ok(EvalResult {
4796 computed_vertices,
4797 cycle_errors,
4798 elapsed: start.elapsed(),
4799 })
4800 }
4801
4802 fn parse_a1_notation(&self, address: &str) -> Result<(String, u32, u32), ExcelError> {
4803 let mut parts = address.splitn(2, '!');
4804 let first = parts.next().unwrap_or_default();
4805 let remainder = parts.next();
4806
4807 let (sheet, cell_part) = match remainder {
4808 Some(cell) => (first.to_string(), cell),
4809 None => (self.default_sheet_name().to_string(), first),
4810 };
4811
4812 let (row, col, _, _) = parse_a1_1based(cell_part).map_err(|err| {
4813 ExcelError::new(ExcelErrorKind::Ref)
4814 .with_message(format!("Invalid cell reference `{cell_part}`: {err}"))
4815 })?;
4816
4817 Ok((sheet, row, col))
4818 }
4819
4820 fn is_ast_volatile_with_provider(&self, ast: &ASTNode) -> bool {
4822 use formualizer_parse::parser::ASTNodeType;
4823 match &ast.node_type {
4824 ASTNodeType::Function { name, args, .. } => {
4825 if let Some(func) = self
4826 .get_function("", name)
4827 .or_else(|| crate::function_registry::get("", name))
4828 && func.caps().contains(crate::function::FnCaps::VOLATILE)
4829 {
4830 return true;
4831 }
4832 args.iter()
4833 .any(|arg| self.is_ast_volatile_with_provider(arg))
4834 }
4835 ASTNodeType::BinaryOp { left, right, .. } => {
4836 self.is_ast_volatile_with_provider(left)
4837 || self.is_ast_volatile_with_provider(right)
4838 }
4839 ASTNodeType::UnaryOp { expr, .. } => self.is_ast_volatile_with_provider(expr),
4840 ASTNodeType::Array(rows) => rows.iter().any(|row| {
4841 row.iter()
4842 .any(|cell| self.is_ast_volatile_with_provider(cell))
4843 }),
4844 _ => false,
4845 }
4846 }
4847
4848 fn find_dirty_precedents(&self, target_vertices: &[VertexId]) -> Vec<VertexId> {
4850 let mut to_evaluate = FxHashSet::default();
4851 let mut visited = FxHashSet::default();
4852 let mut stack = Vec::new();
4853
4854 for &target in target_vertices {
4856 stack.push(target);
4857 }
4858
4859 while let Some(vertex_id) = stack.pop() {
4860 if !visited.insert(vertex_id) {
4861 continue; }
4863
4864 if self.graph.vertex_exists(vertex_id) {
4865 let kind = self.graph.get_vertex_kind(vertex_id);
4867 let needs_eval = match kind {
4868 super::vertex::VertexKind::FormulaScalar
4869 | super::vertex::VertexKind::FormulaArray => {
4870 self.graph.is_dirty(vertex_id) || self.graph.is_volatile(vertex_id)
4871 }
4872 _ => false, };
4874
4875 if needs_eval {
4876 to_evaluate.insert(vertex_id);
4877 }
4878
4879 let dependencies = self.graph.get_dependencies(vertex_id);
4881 for &dep_id in &dependencies {
4882 if !visited.contains(&dep_id) {
4883 stack.push(dep_id);
4884 }
4885 }
4886 }
4887 }
4888
4889 let mut result: Vec<VertexId> = to_evaluate.into_iter().collect();
4890 result.sort_unstable();
4891 result
4892 }
4893
4894 fn evaluate_layer_sequential(
4896 &mut self,
4897 layer: &super::scheduler::Layer,
4898 ) -> Result<usize, ExcelError> {
4899 self.evaluate_layer_sequential_effects(layer)
4900 }
4901
4902 fn update_vertex_value_with_delta(
4903 &mut self,
4904 vertex_id: VertexId,
4905 new_value: LiteralValue,
4906 delta: &mut DeltaCollector,
4907 ) {
4908 if delta.mode != DeltaMode::Off
4909 && let Some(cell) = self.graph.get_cell_ref_for_vertex(vertex_id)
4910 {
4911 let sheet_name = self.graph.sheet_name(cell.sheet_id);
4912 let old = self
4913 .read_cell_value(sheet_name, cell.coord.row() + 1, cell.coord.col() + 1)
4914 .unwrap_or(LiteralValue::Empty);
4915 if old != new_value {
4916 delta.record_cell(cell.sheet_id, cell.coord.row(), cell.coord.col());
4917 }
4918 }
4919 self.graph.update_vertex_value(vertex_id, new_value.clone());
4920 self.mirror_vertex_value_to_overlay(vertex_id, &new_value);
4921 }
4922
4923 fn evaluate_layer_sequential_with_delta(
4924 &mut self,
4925 layer: &super::scheduler::Layer,
4926 delta: &mut DeltaCollector,
4927 ) -> Result<usize, ExcelError> {
4928 self.evaluate_layer_sequential_with_delta_effects(layer, delta)
4929 }
4930
4931 fn evaluate_layer_sequential_cancellable(
4933 &mut self,
4934 layer: &super::scheduler::Layer,
4935 cancel_flag: &AtomicBool,
4936 ) -> Result<usize, ExcelError> {
4937 self.evaluate_layer_sequential_cancellable_effects(layer, cancel_flag)
4938 }
4939
4940 fn evaluate_layer_sequential_cancellable_demand_driven(
4942 &mut self,
4943 layer: &super::scheduler::Layer,
4944 cancel_flag: &AtomicBool,
4945 ) -> Result<usize, ExcelError> {
4946 self.evaluate_layer_sequential_cancellable_demand_driven_effects(layer, cancel_flag)
4947 }
4948
4949 fn evaluate_layer_parallel(
4951 &mut self,
4952 layer: &super::scheduler::Layer,
4953 ) -> Result<usize, ExcelError> {
4954 self.evaluate_layer_parallel_effects(layer)
4955 }
4956
4957 fn evaluate_layer_parallel_with_delta(
4958 &mut self,
4959 layer: &super::scheduler::Layer,
4960 delta: &mut DeltaCollector,
4961 ) -> Result<usize, ExcelError> {
4962 self.evaluate_layer_parallel_with_delta_effects(layer, delta)
4963 }
4964
4965 fn evaluate_layer_parallel_cancellable(
4967 &mut self,
4968 layer: &super::scheduler::Layer,
4969 cancel_flag: &AtomicBool,
4970 ) -> Result<usize, ExcelError> {
4971 self.evaluate_layer_parallel_cancellable_effects(layer, cancel_flag)
4972 }
4973
4974 fn apply_parallel_vertex_result(
4979 &mut self,
4980 vertex_id: VertexId,
4981 result: LiteralValue,
4982 mut delta: Option<&mut DeltaCollector>,
4983 overwritable_formulas: Option<&rustc_hash::FxHashSet<VertexId>>,
4984 ) -> Result<(), ExcelError> {
4985 if let Some(cell) = self.graph.get_cell_ref(vertex_id)
4988 && let Some(owner) = self.graph.spill_registry_anchor_for_cell(cell)
4989 && owner != vertex_id
4990 {
4991 return Ok(());
4992 }
4993
4994 let kind = self.graph.get_vertex_kind(vertex_id);
4995
4996 let is_formula = matches!(kind, VertexKind::FormulaScalar | VertexKind::FormulaArray);
4998 if is_formula {
4999 match result {
5000 LiteralValue::Array(rows) => {
5001 self.apply_array_result_from_parallel(
5002 vertex_id,
5003 rows,
5004 delta.as_deref_mut(),
5005 overwritable_formulas,
5006 )?;
5007 }
5008 other => {
5009 self.apply_non_array_result_from_parallel(
5010 vertex_id,
5011 other,
5012 delta.as_deref_mut(),
5013 );
5014 }
5015 }
5016 return Ok(());
5017 }
5018
5019 if let Some(d) = delta {
5021 self.update_vertex_value_with_delta(vertex_id, result, d);
5022 } else {
5023 self.graph.update_vertex_value(vertex_id, result.clone());
5024 self.mirror_vertex_value_to_overlay(vertex_id, &result);
5025 }
5026 Ok(())
5027 }
5028
5029 fn apply_non_array_result_from_parallel(
5030 &mut self,
5031 vertex_id: VertexId,
5032 value: LiteralValue,
5033 delta: Option<&mut DeltaCollector>,
5034 ) {
5035 let spill_cells = self
5038 .graph
5039 .spill_cells_for_anchor(vertex_id)
5040 .map(|cells| cells.to_vec())
5041 .unwrap_or_default();
5042
5043 if let Some(d) = delta
5044 && d.mode != DeltaMode::Off
5045 && let Some(anchor) = self.graph.get_cell_ref_for_vertex(vertex_id)
5046 {
5047 if spill_cells.is_empty() {
5048 let old = self
5049 .read_cell_value(
5050 self.graph.sheet_name(anchor.sheet_id),
5051 anchor.coord.row() + 1,
5052 anchor.coord.col() + 1,
5053 )
5054 .unwrap_or(LiteralValue::Empty);
5055 if old != value {
5056 d.record_cell(anchor.sheet_id, anchor.coord.row(), anchor.coord.col());
5057 }
5058 } else {
5059 for cell in spill_cells.iter() {
5060 let sheet_name = self.graph.sheet_name(cell.sheet_id);
5061 let old = self
5062 .get_cell_value(sheet_name, cell.coord.row() + 1, cell.coord.col() + 1)
5063 .unwrap_or(LiteralValue::Empty);
5064 let new = if cell.sheet_id == anchor.sheet_id
5065 && cell.coord.row() == anchor.coord.row()
5066 && cell.coord.col() == anchor.coord.col()
5067 {
5068 value.clone()
5069 } else {
5070 LiteralValue::Empty
5071 };
5072 Self::record_cell_if_changed(d, cell, &old, &new);
5073 }
5074 }
5075 }
5076
5077 self.graph.clear_spill_region(vertex_id);
5078
5079 if self.config.arrow_storage_enabled
5080 && self.config.delta_overlay_enabled
5081 && self.config.write_formula_overlay_enabled
5082 {
5083 let empty = LiteralValue::Empty;
5084 for cell in spill_cells.iter() {
5085 let sheet_name = self.graph.sheet_name(cell.sheet_id).to_string();
5086 self.mirror_value_to_computed_overlay(
5087 &sheet_name,
5088 cell.coord.row() + 1,
5089 cell.coord.col() + 1,
5090 &empty,
5091 );
5092 }
5093 }
5094
5095 self.graph.update_vertex_value(vertex_id, value.clone());
5096 self.mirror_vertex_value_to_overlay(vertex_id, &value);
5097 }
5098
5099 fn apply_array_result_from_parallel(
5100 &mut self,
5101 vertex_id: VertexId,
5102 rows: Vec<Vec<LiteralValue>>,
5103 mut delta: Option<&mut DeltaCollector>,
5104 overwritable_formulas: Option<&rustc_hash::FxHashSet<VertexId>>,
5105 ) -> Result<(), ExcelError> {
5106 self.graph
5108 .set_kind(vertex_id, crate::engine::vertex::VertexKind::FormulaArray);
5109
5110 let anchor = self
5111 .graph
5112 .get_cell_ref(vertex_id)
5113 .expect("cell ref for vertex");
5114 let sheet_id = anchor.sheet_id;
5115 let h = rows.len() as u32;
5116 let w = rows.first().map(|r| r.len()).unwrap_or(0) as u32;
5117
5118 let spill_cells = (h as u64).saturating_mul(w as u64);
5120 if spill_cells > self.config.spill.max_spill_cells as u64 {
5121 self.clear_spill_projection_and_mirror(vertex_id, delta.as_deref_mut());
5122 let spill_err = ExcelError::new(ExcelErrorKind::Spill)
5123 .with_message("SpillTooLarge")
5124 .with_extra(formualizer_common::ExcelErrorExtra::Spill {
5125 expected_rows: h,
5126 expected_cols: w,
5127 });
5128 let spill_val = LiteralValue::Error(spill_err.clone());
5129 if let Some(d) = delta.as_deref_mut()
5130 && d.mode != DeltaMode::Off
5131 {
5132 let old = self
5133 .read_cell_value(
5134 self.graph.sheet_name(anchor.sheet_id),
5135 anchor.coord.row() + 1,
5136 anchor.coord.col() + 1,
5137 )
5138 .unwrap_or(LiteralValue::Empty);
5139 if old != spill_val {
5140 d.record_cell(anchor.sheet_id, anchor.coord.row(), anchor.coord.col());
5141 }
5142 }
5143 self.graph.update_vertex_value(vertex_id, spill_val.clone());
5144 self.mirror_vertex_value_to_overlay(vertex_id, &spill_val);
5145 return Ok(());
5146 }
5147
5148 const PACKED_MAX_ROW: u32 = 1_048_575; const PACKED_MAX_COL: u32 = 16_383; let end_row = anchor.coord.row().saturating_add(h).saturating_sub(1);
5152 let end_col = anchor.coord.col().saturating_add(w).saturating_sub(1);
5153 if end_row > PACKED_MAX_ROW || end_col > PACKED_MAX_COL {
5154 self.clear_spill_projection_and_mirror(vertex_id, delta.as_deref_mut());
5155 let spill_err = ExcelError::new(ExcelErrorKind::Spill)
5156 .with_message("Spill exceeds sheet bounds")
5157 .with_extra(formualizer_common::ExcelErrorExtra::Spill {
5158 expected_rows: h,
5159 expected_cols: w,
5160 });
5161 let spill_val = LiteralValue::Error(spill_err.clone());
5162 if let Some(d) = delta.as_deref_mut()
5163 && d.mode != DeltaMode::Off
5164 {
5165 let old = self
5166 .read_cell_value(
5167 self.graph.sheet_name(anchor.sheet_id),
5168 anchor.coord.row() + 1,
5169 anchor.coord.col() + 1,
5170 )
5171 .unwrap_or(LiteralValue::Empty);
5172 if old != spill_val {
5173 d.record_cell(anchor.sheet_id, anchor.coord.row(), anchor.coord.col());
5174 }
5175 }
5176 self.graph.update_vertex_value(vertex_id, spill_val.clone());
5177 self.mirror_vertex_value_to_overlay(vertex_id, &spill_val);
5178 return Ok(());
5179 }
5180
5181 let mut targets = Vec::new();
5182 for r in 0..h {
5183 for c in 0..w {
5184 targets.push(self.graph.make_cell_ref_internal(
5185 sheet_id,
5186 anchor.coord.row() + r,
5187 anchor.coord.col() + c,
5188 ));
5189 }
5190 }
5191
5192 match self.spill_mgr.reserve(
5193 vertex_id,
5194 anchor,
5195 SpillShape { rows: h, cols: w },
5196 SpillMeta {
5197 epoch: self.recalc_epoch,
5198 config: self.config.spill,
5199 },
5200 ) {
5201 Ok(()) => {
5202 if let Err(e) = self.commit_spill_and_mirror(
5203 vertex_id,
5204 &targets,
5205 rows.clone(),
5206 delta.as_deref_mut(),
5207 overwritable_formulas,
5208 ) {
5209 self.clear_spill_projection_and_mirror(vertex_id, delta.as_deref_mut());
5210 let err_val = LiteralValue::Error(e.clone());
5211 if let Some(d) = delta.as_deref_mut()
5212 && d.mode != DeltaMode::Off
5213 {
5214 let old = self
5215 .read_cell_value(
5216 self.graph.sheet_name(anchor.sheet_id),
5217 anchor.coord.row() + 1,
5218 anchor.coord.col() + 1,
5219 )
5220 .unwrap_or(LiteralValue::Empty);
5221 if old != err_val {
5222 d.record_cell(anchor.sheet_id, anchor.coord.row(), anchor.coord.col());
5223 }
5224 }
5225 self.graph.update_vertex_value(vertex_id, err_val.clone());
5226 self.mirror_vertex_value_to_overlay(vertex_id, &err_val);
5227 return Ok(());
5228 }
5229
5230 let top_left = rows
5232 .first()
5233 .and_then(|r| r.first())
5234 .cloned()
5235 .unwrap_or(LiteralValue::Empty);
5236 self.graph.update_vertex_value(vertex_id, top_left.clone());
5237 self.mirror_vertex_value_to_overlay(vertex_id, &top_left);
5238 Ok(())
5239 }
5240 Err(e) => {
5241 self.clear_spill_projection_and_mirror(vertex_id, delta.as_deref_mut());
5242 let spill_err = ExcelError::new(ExcelErrorKind::Spill)
5243 .with_message(e.message.unwrap_or_else(|| "Spill blocked".to_string()))
5244 .with_extra(formualizer_common::ExcelErrorExtra::Spill {
5245 expected_rows: h,
5246 expected_cols: w,
5247 });
5248 let spill_val = LiteralValue::Error(spill_err.clone());
5249 if let Some(d) = delta
5250 && d.mode != DeltaMode::Off
5251 {
5252 let old = self
5253 .read_cell_value(
5254 self.graph.sheet_name(anchor.sheet_id),
5255 anchor.coord.row() + 1,
5256 anchor.coord.col() + 1,
5257 )
5258 .unwrap_or(LiteralValue::Empty);
5259 if old != spill_val {
5260 d.record_cell(anchor.sheet_id, anchor.coord.row(), anchor.coord.col());
5261 }
5262 }
5263 self.graph.update_vertex_value(vertex_id, spill_val.clone());
5264 self.mirror_vertex_value_to_overlay(vertex_id, &spill_val);
5265 Ok(())
5266 }
5267 }
5268 }
5269
5270 fn evaluate_vertex_immutable(&self, vertex_id: VertexId) -> Result<LiteralValue, ExcelError> {
5272 if !self.graph.vertex_exists(vertex_id) {
5274 return Err(ExcelError::new(formualizer_common::ExcelErrorKind::Ref)
5275 .with_message(format!("Vertex not found: {vertex_id:?}")));
5276 }
5277
5278 let kind = self.graph.get_vertex_kind(vertex_id);
5280 let sheet_id = self.graph.get_vertex_sheet_id(vertex_id);
5281
5282 let ast_id = match kind {
5283 VertexKind::FormulaScalar | VertexKind::FormulaArray => {
5284 if let Some(ast_id) = self.graph.get_formula_id(vertex_id) {
5285 ast_id
5286 } else {
5287 return Ok(LiteralValue::Number(0.0));
5288 }
5289 }
5290 VertexKind::Empty | VertexKind::Cell => {
5291 if let Some(cell_ref) = self.graph.get_cell_ref(vertex_id) {
5292 let sheet_name = self.graph.sheet_name(cell_ref.sheet_id);
5293 let row = cell_ref.coord.row() + 1;
5294 let col = cell_ref.coord.col() + 1;
5295 if let Some(v) = self.read_cell_value(sheet_name, row, col) {
5296 return Ok(v);
5297 }
5298 }
5299 return Ok(LiteralValue::Number(0.0));
5300 }
5301 VertexKind::NamedScalar => {
5302 let named_range = self.graph.named_range_by_vertex(vertex_id).ok_or_else(|| {
5303 ExcelError::new(ExcelErrorKind::Name)
5304 .with_message("Named range metadata missing".to_string())
5305 })?;
5306
5307 return match &named_range.definition {
5308 NamedDefinition::Cell(cell_ref) => {
5309 let sheet_name = self.graph.sheet_name(cell_ref.sheet_id);
5310 Ok(self
5311 .get_cell_value(
5312 sheet_name,
5313 cell_ref.coord.row() + 1,
5314 cell_ref.coord.col() + 1,
5315 )
5316 .unwrap_or(LiteralValue::Empty))
5317 }
5318 NamedDefinition::Literal(v) => Ok(v.clone()),
5319 NamedDefinition::Formula { ast, .. } => {
5320 let context_sheet = match named_range.scope {
5321 NameScope::Sheet(id) => id,
5322 NameScope::Workbook => sheet_id,
5323 };
5324 let sheet_name = self.graph.sheet_name(context_sheet);
5325 let cell_ref = self
5326 .graph
5327 .get_cell_ref(vertex_id)
5328 .unwrap_or_else(|| self.graph.make_cell_ref(sheet_name, 0, 0));
5329 let interpreter = Interpreter::new_with_cell(self, sheet_name, cell_ref);
5330 interpreter.evaluate_ast(ast).map(|cv| cv.into_literal())
5331 }
5332 NamedDefinition::Range(_) => Err(ExcelError::new(ExcelErrorKind::Value)
5333 .with_message("Range-valued name evaluated as scalar".to_string())),
5334 };
5335 }
5336 VertexKind::NamedArray => {
5337 let named_range = self.graph.named_range_by_vertex(vertex_id).ok_or_else(|| {
5338 ExcelError::new(ExcelErrorKind::Name)
5339 .with_message("Named range metadata missing".to_string())
5340 })?;
5341
5342 return match &named_range.definition {
5343 NamedDefinition::Range(range_ref) => {
5344 if range_ref.start.sheet_id != range_ref.end.sheet_id {
5345 return Err(ExcelError::new(ExcelErrorKind::Ref)
5346 .with_message("Named range cannot span sheets".to_string()));
5347 }
5348 let sheet_name = self.graph.sheet_name(range_ref.start.sheet_id);
5349 let sr0 = range_ref.start.coord.row();
5350 let sc0 = range_ref.start.coord.col();
5351 let er0 = range_ref.end.coord.row();
5352 let ec0 = range_ref.end.coord.col();
5353 if sr0 > er0 || sc0 > ec0 {
5354 return Err(ExcelError::new(ExcelErrorKind::Ref)
5355 .with_message("Invalid named range bounds".to_string()));
5356 }
5357
5358 let h = (er0 - sr0 + 1) as usize;
5359 let w = (ec0 - sc0 + 1) as usize;
5360 let cell_count = (h as u64).saturating_mul(w as u64);
5361 if cell_count > self.config.spill.max_spill_cells as u64 {
5362 return Err(ExcelError::new(ExcelErrorKind::NImpl).with_message(
5363 "Named range too large to materialize as an array".to_string(),
5364 ));
5365 }
5366
5367 let mut rows = Vec::with_capacity(h);
5368 for r0 in sr0..=er0 {
5369 let mut row = Vec::with_capacity(w);
5370 for c0 in sc0..=ec0 {
5371 let v = self
5372 .get_cell_value(sheet_name, r0 + 1, c0 + 1)
5373 .unwrap_or(LiteralValue::Empty);
5374 row.push(v);
5375 }
5376 rows.push(row);
5377 }
5378 Ok(LiteralValue::Array(rows))
5379 }
5380 NamedDefinition::Cell(cell_ref) => {
5381 let sheet_name = self.graph.sheet_name(cell_ref.sheet_id);
5382 let row = cell_ref.coord.row() + 1;
5383 let col = cell_ref.coord.col() + 1;
5384 let v = self
5385 .get_cell_value(sheet_name, row, col)
5386 .unwrap_or(LiteralValue::Empty);
5387 Ok(LiteralValue::Array(vec![vec![v]]))
5388 }
5389 NamedDefinition::Literal(v) => Ok(LiteralValue::Array(vec![vec![v.clone()]])),
5390 NamedDefinition::Formula { ast, .. } => {
5391 let context_sheet = match named_range.scope {
5392 NameScope::Sheet(id) => id,
5393 NameScope::Workbook => sheet_id,
5394 };
5395 let sheet_name = self.graph.sheet_name(context_sheet);
5396 let cell_ref = self
5397 .graph
5398 .get_cell_ref(vertex_id)
5399 .unwrap_or_else(|| self.graph.make_cell_ref(sheet_name, 0, 0));
5400 let interpreter = Interpreter::new_with_cell(self, sheet_name, cell_ref);
5401 match interpreter.evaluate_ast(ast) {
5402 Ok(cv) => {
5403 let v = cv.into_literal();
5404 match v {
5405 LiteralValue::Array(_) => Ok(v),
5406 other => Ok(LiteralValue::Array(vec![vec![other]])),
5407 }
5408 }
5409 Err(err) => Ok(LiteralValue::Error(err)),
5410 }
5411 }
5412 };
5413 }
5414 VertexKind::InfiniteRange
5415 | VertexKind::Range
5416 | VertexKind::External
5417 | VertexKind::Table => {
5418 return Ok(LiteralValue::Number(0.0));
5420 }
5421 };
5422
5423 let sheet_name = self.graph.sheet_name(sheet_id);
5425 let cell_ref = self
5426 .graph
5427 .get_cell_ref(vertex_id)
5428 .expect("cell ref for vertex");
5429 let interpreter = Interpreter::new_with_cell(self, sheet_name, cell_ref);
5430
5431 interpreter
5432 .evaluate_arena_ast(ast_id, self.graph.data_store(), self.graph.sheet_reg())
5433 .map(|cv| cv.into_literal())
5434 }
5435
5436 pub fn thread_pool(&self) -> Option<&Arc<rayon::ThreadPool>> {
5438 self.thread_pool.as_ref()
5439 }
5440}
5441
5442#[derive(Default)]
5443struct RowBoundsCache {
5444 snapshot: u64,
5445 map: rustc_hash::FxHashMap<(u32, usize), (Option<u32>, Option<u32>)>,
5447}
5448
5449impl RowBoundsCache {
5450 fn new(snapshot: u64) -> Self {
5451 Self {
5452 snapshot,
5453 map: Default::default(),
5454 }
5455 }
5456 fn get_row_bounds(
5457 &self,
5458 sheet_id: SheetId,
5459 col_idx: usize,
5460 snapshot: u64,
5461 ) -> Option<(Option<u32>, Option<u32>)> {
5462 if self.snapshot != snapshot {
5463 return None;
5464 }
5465 self.map.get(&(sheet_id as u32, col_idx)).copied()
5466 }
5467 fn put_row_bounds(
5468 &mut self,
5469 sheet_id: SheetId,
5470 col_idx: usize,
5471 snapshot: u64,
5472 bounds: (Option<u32>, Option<u32>),
5473 ) {
5474 if self.snapshot != snapshot {
5475 self.snapshot = snapshot;
5476 self.map.clear();
5477 }
5478 self.map.insert((sheet_id as u32, col_idx), bounds);
5479 }
5480}
5481
5482#[derive(Default)]
5484pub struct ShimSpillManager {
5485 region_locks: RegionLockManager,
5486 pub(crate) active_locks: rustc_hash::FxHashMap<VertexId, u64>,
5487}
5488
5489impl ShimSpillManager {
5490 pub(crate) fn reserve(
5491 &mut self,
5492 owner: VertexId,
5493 anchor_cell: CellRef,
5494 shape: SpillShape,
5495 _meta: SpillMeta,
5496 ) -> Result<(), ExcelError> {
5497 let region = crate::engine::spill::Region {
5499 sheet_id: anchor_cell.sheet_id as u32,
5500 row_start: anchor_cell.coord.row(),
5501 row_end: anchor_cell
5502 .coord
5503 .row()
5504 .saturating_add(shape.rows)
5505 .saturating_sub(1),
5506 col_start: anchor_cell.coord.col(),
5507 col_end: anchor_cell
5508 .coord
5509 .col()
5510 .saturating_add(shape.cols)
5511 .saturating_sub(1),
5512 };
5513 match self.region_locks.reserve(region, owner) {
5514 Ok(id) => {
5515 if id != 0 {
5516 self.active_locks.insert(owner, id);
5517 }
5518 Ok(())
5519 }
5520 Err(e) => Err(e),
5521 }
5522 }
5523
5524 pub(crate) fn commit_array_with_value_probe<F>(
5525 &mut self,
5526 graph: &mut DependencyGraph,
5527 anchor_vertex: VertexId,
5528 targets: &[CellRef],
5529 rows: Vec<Vec<LiteralValue>>,
5530 overwritable_formulas: Option<&rustc_hash::FxHashSet<VertexId>>,
5531 mut value_probe: F,
5532 ) -> Result<(), ExcelError>
5533 where
5534 F: FnMut(&DependencyGraph, &CellRef) -> Option<LiteralValue>,
5535 {
5536 use formualizer_common::{ExcelErrorExtra, ExcelErrorKind};
5537
5538 let plan_res = graph.plan_spill_region_allowing_formula_overwrite(
5542 anchor_vertex,
5543 targets,
5544 overwritable_formulas,
5545 );
5546 if let Err(e) = plan_res {
5547 if let Some(id) = self.active_locks.remove(&anchor_vertex) {
5548 self.region_locks.release(id);
5549 }
5550 return Err(e);
5551 }
5552
5553 if !graph.value_cache_enabled() {
5554 let (expected_rows, expected_cols) = if targets.is_empty() {
5556 (0u32, 0u32)
5557 } else {
5558 let mut min_r = u32::MAX;
5559 let mut max_r = 0u32;
5560 let mut min_c = u32::MAX;
5561 let mut max_c = 0u32;
5562 for cell in targets {
5563 let r = cell.coord.row();
5564 let c = cell.coord.col();
5565 min_r = min_r.min(r);
5566 max_r = max_r.max(r);
5567 min_c = min_c.min(c);
5568 max_c = max_c.max(c);
5569 }
5570 (
5571 max_r.saturating_sub(min_r).saturating_add(1),
5572 max_c.saturating_sub(min_c).saturating_add(1),
5573 )
5574 };
5575
5576 let anchor_cell = graph
5577 .get_cell_ref(anchor_vertex)
5578 .expect("anchor cell ref for spill commit");
5579
5580 for cell in targets {
5581 if *cell == anchor_cell {
5583 continue;
5584 }
5585 if graph.spill_registry_anchor_for_cell(*cell).is_some() {
5587 continue;
5588 }
5589 if let Some(&vid) = graph.get_vertex_id_for_address(cell)
5591 && vid != anchor_vertex
5592 {
5593 match graph.get_vertex_kind(vid) {
5594 crate::engine::vertex::VertexKind::FormulaScalar
5595 | crate::engine::vertex::VertexKind::FormulaArray => {
5596 continue;
5598 }
5599 _ => {}
5600 }
5601 }
5602
5603 if let Some(v) = value_probe(graph, cell)
5604 && !matches!(v, LiteralValue::Empty)
5605 {
5606 if let Some(id) = self.active_locks.remove(&anchor_vertex) {
5607 self.region_locks.release(id);
5608 }
5609 return Err(ExcelError::new(ExcelErrorKind::Spill)
5610 .with_message("BlockedByValue")
5611 .with_extra(ExcelErrorExtra::Spill {
5612 expected_rows,
5613 expected_cols,
5614 }));
5615 }
5616 }
5617 }
5618
5619 let commit_res = graph.commit_spill_region_atomic_with_fault(
5620 anchor_vertex,
5621 targets.to_vec(),
5622 rows,
5623 None,
5624 );
5625 if let Some(id) = self.active_locks.remove(&anchor_vertex) {
5626 self.region_locks.release(id);
5627 }
5628 commit_res.map(|_| ())
5629 }
5630
5631 pub(crate) fn commit_array_with_overlay<R: EvaluationContext>(
5633 &mut self,
5634 engine: &mut Engine<R>,
5635 anchor_vertex: VertexId,
5636 targets: &[CellRef],
5637 rows: Vec<Vec<LiteralValue>>,
5638 overwritable_formulas: Option<&rustc_hash::FxHashSet<VertexId>>,
5639 ) -> Result<(), ExcelError> {
5640 let plan_res = engine.graph.plan_spill_region_allowing_formula_overwrite(
5642 anchor_vertex,
5643 targets,
5644 overwritable_formulas,
5645 );
5646 if let Err(e) = plan_res {
5647 if let Some(id) = self.active_locks.remove(&anchor_vertex) {
5648 self.region_locks.release(id);
5649 }
5650 return Err(e);
5651 }
5652
5653 let commit_res = engine.graph.commit_spill_region_atomic_with_fault(
5654 anchor_vertex,
5655 targets.to_vec(),
5656 rows.clone(),
5657 None,
5658 );
5659 if let Some(id) = self.active_locks.remove(&anchor_vertex) {
5660 self.region_locks.release(id);
5661 }
5662 commit_res.map(|_| ())?;
5663
5664 if engine.config.arrow_storage_enabled
5666 && engine.config.delta_overlay_enabled
5667 && engine.config.write_formula_overlay_enabled
5668 {
5669 for (idx, cell) in targets.iter().enumerate() {
5671 let (r_off, c_off) = {
5672 if rows.is_empty() || rows[0].is_empty() {
5673 (0usize, 0usize)
5674 } else {
5675 let width = rows[0].len();
5676 (idx / width, idx % width)
5677 }
5678 };
5679 let v = rows
5680 .get(r_off)
5681 .and_then(|r| r.get(c_off))
5682 .cloned()
5683 .unwrap_or(LiteralValue::Empty);
5684 let sheet_name = engine.graph.sheet_name(cell.sheet_id).to_string();
5685 engine.mirror_value_to_computed_overlay(
5686 &sheet_name,
5687 cell.coord.row() + 1,
5688 cell.coord.col() + 1,
5689 &v,
5690 );
5691 }
5692 }
5693 Ok(())
5694 }
5695}
5696
5697impl<R> Engine<R>
5698where
5699 R: EvaluationContext,
5700{
5701 fn resolve_shared_ref(
5702 &self,
5703 reference: &ReferenceType,
5704 current_sheet: &str,
5705 ) -> Result<formualizer_common::SheetRef<'static>, ExcelError> {
5706 use formualizer_common::{
5707 SheetCellRef as SharedCellRef, SheetLocator, SheetRangeRef as SharedRangeRef,
5708 SheetRef as SharedRef,
5709 };
5710
5711 let sr = match reference {
5713 ReferenceType::Cell {
5714 sheet,
5715 row,
5716 col,
5717 row_abs,
5718 col_abs,
5719 } => {
5720 let row0 = row
5721 .checked_sub(1)
5722 .ok_or_else(|| ExcelError::new(ExcelErrorKind::Ref))?;
5723 let col0 = col
5724 .checked_sub(1)
5725 .ok_or_else(|| ExcelError::new(ExcelErrorKind::Ref))?;
5726 let sheet_loc = match sheet.as_deref() {
5727 Some(name) => SheetLocator::from_name(name),
5728 None => SheetLocator::Current,
5729 };
5730 let coord = formualizer_common::RelativeCoord::new(row0, col0, *row_abs, *col_abs);
5731 SharedRef::Cell(SharedCellRef::new(sheet_loc, coord))
5732 }
5733 ReferenceType::Range {
5734 sheet,
5735 start_row,
5736 start_col,
5737 end_row,
5738 end_col,
5739 start_row_abs,
5740 start_col_abs,
5741 end_row_abs,
5742 end_col_abs,
5743 } => {
5744 let sheet_loc = match sheet.as_deref() {
5745 Some(name) => SheetLocator::from_name(name),
5746 None => SheetLocator::Current,
5747 };
5748 let sr = start_row
5749 .map(|r| {
5750 r.checked_sub(1)
5751 .ok_or_else(|| ExcelError::new(ExcelErrorKind::Ref))
5752 })
5753 .transpose()?;
5754 let sc = start_col
5755 .map(|c| {
5756 c.checked_sub(1)
5757 .ok_or_else(|| ExcelError::new(ExcelErrorKind::Ref))
5758 })
5759 .transpose()?;
5760 let er = end_row
5761 .map(|r| {
5762 r.checked_sub(1)
5763 .ok_or_else(|| ExcelError::new(ExcelErrorKind::Ref))
5764 })
5765 .transpose()?;
5766 let ec = end_col
5767 .map(|c| {
5768 c.checked_sub(1)
5769 .ok_or_else(|| ExcelError::new(ExcelErrorKind::Ref))
5770 })
5771 .transpose()?;
5772 let range = SharedRangeRef::from_parts(
5773 sheet_loc,
5774 sr.map(|idx| formualizer_common::AxisBound::new(idx, *start_row_abs)),
5775 sc.map(|idx| formualizer_common::AxisBound::new(idx, *start_col_abs)),
5776 er.map(|idx| formualizer_common::AxisBound::new(idx, *end_row_abs)),
5777 ec.map(|idx| formualizer_common::AxisBound::new(idx, *end_col_abs)),
5778 )
5779 .map_err(|_| ExcelError::new(ExcelErrorKind::Ref))?;
5780 SharedRef::Range(range)
5781 }
5782 _ => return Err(ExcelError::new(ExcelErrorKind::Ref)),
5783 };
5784
5785 let current_id = self
5786 .graph
5787 .sheet_id(current_sheet)
5788 .ok_or_else(|| ExcelError::new(ExcelErrorKind::Ref))?;
5789
5790 let resolve_loc = |loc: SheetLocator<'_>| -> Result<SheetLocator<'static>, ExcelError> {
5791 match loc {
5792 SheetLocator::Current => Ok(SheetLocator::Id(current_id)),
5793 SheetLocator::Id(id) => Ok(SheetLocator::Id(id)),
5794 SheetLocator::Name(name) => {
5795 let n = name.as_ref();
5796 self.graph
5797 .sheet_id(n)
5798 .map(SheetLocator::Id)
5799 .ok_or_else(|| ExcelError::new(ExcelErrorKind::Ref))
5800 }
5801 }
5802 };
5803
5804 match sr {
5805 SharedRef::Cell(cell) => {
5806 let owned = cell.into_owned();
5807 let sheet = resolve_loc(owned.sheet)?;
5808 Ok(SharedRef::Cell(SharedCellRef::new(sheet, owned.coord)))
5809 }
5810 SharedRef::Range(range) => {
5811 let owned = range.into_owned();
5812 let sheet = resolve_loc(owned.sheet)?;
5813 Ok(SharedRef::Range(SharedRangeRef {
5814 sheet,
5815 start_row: owned.start_row,
5816 start_col: owned.start_col,
5817 end_row: owned.end_row,
5818 end_col: owned.end_col,
5819 }))
5820 }
5821 }
5822 }
5823}
5824
5825impl<R> crate::traits::ReferenceResolver for Engine<R>
5828where
5829 R: EvaluationContext,
5830{
5831 fn resolve_cell_reference(
5832 &self,
5833 sheet: Option<&str>,
5834 row: u32,
5835 col: u32,
5836 ) -> Result<LiteralValue, ExcelError> {
5837 let sheet_name = sheet.unwrap_or_else(|| self.default_sheet_name()); if let Some(v) = self.get_cell_value(sheet_name, row, col) {
5841 Ok(v)
5842 } else {
5843 Ok(LiteralValue::Number(0.0))
5845 }
5846 }
5847}
5848
5849impl<R> crate::traits::RangeResolver for Engine<R>
5850where
5851 R: EvaluationContext,
5852{
5853 fn resolve_range_reference(
5854 &self,
5855 sheet: Option<&str>,
5856 sr: Option<u32>,
5857 sc: Option<u32>,
5858 er: Option<u32>,
5859 ec: Option<u32>,
5860 ) -> Result<Box<dyn crate::traits::Range>, ExcelError> {
5861 self.resolver.resolve_range_reference(sheet, sr, sc, er, ec)
5864 }
5865}
5866
5867impl<R> crate::traits::NamedRangeResolver for Engine<R>
5868where
5869 R: EvaluationContext,
5870{
5871 fn resolve_named_range_reference(
5872 &self,
5873 name: &str,
5874 ) -> Result<Vec<Vec<LiteralValue>>, ExcelError> {
5875 self.resolver.resolve_named_range_reference(name)
5876 }
5877}
5878
5879impl<R> crate::traits::TableResolver for Engine<R>
5880where
5881 R: EvaluationContext,
5882{
5883 fn resolve_table_reference(
5884 &self,
5885 tref: &formualizer_parse::parser::TableReference,
5886 ) -> Result<Box<dyn crate::traits::Table>, ExcelError> {
5887 self.resolver.resolve_table_reference(tref)
5888 }
5889}
5890
5891impl<R> crate::traits::SourceResolver for Engine<R>
5892where
5893 R: EvaluationContext,
5894{
5895 fn source_scalar_version(&self, name: &str) -> Option<u64> {
5896 self.resolver.source_scalar_version(name)
5897 }
5898
5899 fn resolve_source_scalar(&self, name: &str) -> Result<LiteralValue, ExcelError> {
5900 self.resolver.resolve_source_scalar(name)
5901 }
5902
5903 fn source_table_version(&self, name: &str) -> Option<u64> {
5904 self.resolver.source_table_version(name)
5905 }
5906
5907 fn resolve_source_table(
5908 &self,
5909 name: &str,
5910 ) -> Result<Box<dyn crate::traits::Table>, ExcelError> {
5911 self.resolver.resolve_source_table(name)
5912 }
5913}
5914
5915impl<R> crate::traits::Resolver for Engine<R> where R: EvaluationContext {}
5917
5918impl<R> crate::traits::FunctionProvider for Engine<R>
5920where
5921 R: EvaluationContext,
5922{
5923 fn get_function(
5924 &self,
5925 prefix: &str,
5926 name: &str,
5927 ) -> Option<std::sync::Arc<dyn crate::function::Function>> {
5928 self.resolver.get_function(prefix, name)
5929 }
5930}
5931
5932impl<R> crate::traits::EvaluationContext for Engine<R>
5934where
5935 R: EvaluationContext,
5936{
5937 fn clock(&self) -> &dyn crate::timezone::ClockProvider {
5938 self.clock.as_ref()
5939 }
5940
5941 fn thread_pool(&self) -> Option<&Arc<rayon::ThreadPool>> {
5942 self.thread_pool.as_ref()
5943 }
5944
5945 fn cancellation_token(&self) -> Option<Arc<std::sync::atomic::AtomicBool>> {
5946 self.active_cancel_flag.clone()
5947 }
5948
5949 fn chunk_hint(&self) -> Option<usize> {
5950 let hint =
5952 (self.config.stripe_height as usize).saturating_mul(self.config.stripe_width as usize);
5953 Some(hint.clamp(1024, 1 << 20)) }
5955
5956 fn volatile_level(&self) -> crate::traits::VolatileLevel {
5957 self.config.volatile_level
5958 }
5959
5960 fn workbook_seed(&self) -> u64 {
5961 self.config.workbook_seed
5962 }
5963
5964 fn recalc_epoch(&self) -> u64 {
5965 self.recalc_epoch
5966 }
5967
5968 fn used_rows_for_columns(
5969 &self,
5970 sheet: &str,
5971 start_col: u32,
5972 end_col: u32,
5973 ) -> Option<(u32, u32)> {
5974 let sheet_id = self.graph.sheet_id(sheet)?;
5976 let arrow_ok = self.sheet_store().sheet(sheet).is_some();
5977 if arrow_ok && let Some(bounds) = self.arrow_used_row_bounds(sheet, start_col, end_col) {
5978 return Some(bounds);
5979 }
5980 let sc0 = start_col.saturating_sub(1);
5981 let ec0 = end_col.saturating_sub(1);
5982 self.graph
5983 .used_row_bounds_for_columns(sheet_id, sc0, ec0)
5984 .map(|(a0, b0)| (a0 + 1, b0 + 1))
5985 }
5986
5987 fn used_cols_for_rows(&self, sheet: &str, start_row: u32, end_row: u32) -> Option<(u32, u32)> {
5988 let sheet_id = self.graph.sheet_id(sheet)?;
5990 let arrow_ok = self.sheet_store().sheet(sheet).is_some();
5991 if arrow_ok && let Some(bounds) = self.arrow_used_col_bounds(sheet, start_row, end_row) {
5992 return Some(bounds);
5993 }
5994 let sr0 = start_row.saturating_sub(1);
5995 let er0 = end_row.saturating_sub(1);
5996 self.graph
5997 .used_col_bounds_for_rows(sheet_id, sr0, er0)
5998 .map(|(a0, b0)| (a0 + 1, b0 + 1))
5999 }
6000
6001 fn sheet_bounds(&self, sheet: &str) -> Option<(u32, u32)> {
6002 let _ = self.graph.sheet_id(sheet)?;
6003 Some((1_048_576, 16_384)) }
6007
6008 fn data_snapshot_id(&self) -> u64 {
6009 self.snapshot_id.load(std::sync::atomic::Ordering::Relaxed)
6010 }
6011
6012 fn backend_caps(&self) -> crate::traits::BackendCaps {
6013 crate::traits::BackendCaps {
6014 streaming: true,
6015 used_region: true,
6016 write: false,
6017 tables: false,
6018 async_stream: false,
6019 }
6020 }
6021
6022 fn date_system(&self) -> crate::engine::DateSystem {
6025 self.config.date_system
6026 }
6027 fn resolve_range_view<'c>(
6029 &'c self,
6030 reference: &ReferenceType,
6031 current_sheet: &str,
6032 ) -> Result<RangeView<'c>, ExcelError> {
6033 match reference {
6034 ReferenceType::External(ext) => {
6035 let name = ext.raw.as_str();
6036 match ext.kind {
6037 formualizer_parse::parser::ExternalRefKind::Cell { .. } => {
6038 let Some(source) = self.graph.resolve_source_scalar_entry(name) else {
6039 return Err(ExcelError::new(ExcelErrorKind::Name)
6040 .with_message(format!("Undefined name: {name}")));
6041 };
6042 let version = source
6043 .version
6044 .or_else(|| self.resolver.source_scalar_version(name));
6045 let v = self.resolve_source_scalar_cached(name, version)?;
6046 Ok(RangeView::from_owned_rows(
6047 vec![vec![v]],
6048 self.config.date_system,
6049 ))
6050 }
6051 formualizer_parse::parser::ExternalRefKind::Range { .. } => {
6052 let Some(source) = self.graph.resolve_source_table_entry(name) else {
6053 return Err(ExcelError::new(ExcelErrorKind::Name)
6054 .with_message(format!("Undefined table: {name}")));
6055 };
6056 let version = source
6057 .version
6058 .or_else(|| self.resolver.source_table_version(name));
6059 let table = self.resolve_source_table_cached(name, version)?;
6060 let spec = Some(formualizer_parse::parser::TableSpecifier::Data);
6061 self.source_table_to_range_view(table.as_ref(), &spec)
6062 }
6063 }
6064 }
6065 ReferenceType::Range { .. } => {
6066 let shared = self.resolve_shared_ref(reference, current_sheet)?;
6067 let formualizer_common::SheetRef::Range(range) = shared else {
6068 return Err(ExcelError::new(ExcelErrorKind::Ref));
6069 };
6070 let sheet_id = match range.sheet {
6071 formualizer_common::SheetLocator::Id(id) => id,
6072 _ => return Err(ExcelError::new(ExcelErrorKind::Ref)),
6073 };
6074 let sheet_name = self.graph.sheet_name(sheet_id);
6075
6076 let bounded_range = if range.start_row.is_some()
6077 && range.start_col.is_some()
6078 && range.end_row.is_some()
6079 && range.end_col.is_some()
6080 {
6081 Some(RangeRef::try_from_shared(range.as_ref())?)
6082 } else {
6083 None
6084 };
6085
6086 let mut sr = bounded_range
6087 .as_ref()
6088 .map(|r| r.start.coord.row() + 1)
6089 .or_else(|| range.start_row.map(|b| b.index + 1));
6090 let mut sc = bounded_range
6091 .as_ref()
6092 .map(|r| r.start.coord.col() + 1)
6093 .or_else(|| range.start_col.map(|b| b.index + 1));
6094 let mut er = bounded_range
6095 .as_ref()
6096 .map(|r| r.end.coord.row() + 1)
6097 .or_else(|| range.end_row.map(|b| b.index + 1));
6098 let mut ec = bounded_range
6099 .as_ref()
6100 .map(|r| r.end.coord.col() + 1)
6101 .or_else(|| range.end_col.map(|b| b.index + 1));
6102
6103 if sr.is_none() && er.is_none() {
6104 let scv = sc.unwrap_or(1);
6106 let ecv = ec.unwrap_or(scv);
6107 sr = Some(1);
6108 if let Some((_, max_r)) = self.used_rows_for_columns(sheet_name, scv, ecv) {
6109 er = Some(max_r);
6110 } else if let Some((max_rows, _)) = self.sheet_bounds(sheet_name) {
6111 er = Some(self.config.max_open_ended_rows);
6112 }
6113 }
6114 if sc.is_none() && ec.is_none() {
6115 let srv = sr.unwrap_or(1);
6117 let erv = er.unwrap_or(srv);
6118 sc = Some(1);
6119 if let Some((_, max_c)) = self.used_cols_for_rows(sheet_name, srv, erv) {
6120 ec = Some(max_c);
6121 } else if let Some((_, max_cols)) = self.sheet_bounds(sheet_name) {
6122 ec = Some(self.config.max_open_ended_cols);
6123 }
6124 }
6125 if sr.is_some() && er.is_none() {
6126 let scv = sc.unwrap_or(1);
6127 let ecv = ec.unwrap_or(scv);
6128 if let Some((_, max_r)) = self.used_rows_for_columns(sheet_name, scv, ecv) {
6129 er = Some(max_r);
6130 } else if let Some((max_rows, _)) = self.sheet_bounds(sheet_name) {
6131 er = Some(self.config.max_open_ended_rows);
6132 }
6133 }
6134 if er.is_some() && sr.is_none() {
6135 sr = Some(1);
6137 }
6138 if sc.is_some() && ec.is_none() {
6139 let srv = sr.unwrap_or(1);
6140 let erv = er.unwrap_or(srv);
6141 if let Some((_, max_c)) = self.used_cols_for_rows(sheet_name, srv, erv) {
6142 ec = Some(max_c);
6143 } else if let Some((_, max_cols)) = self.sheet_bounds(sheet_name) {
6144 ec = Some(self.config.max_open_ended_cols);
6145 }
6146 }
6147 if ec.is_some() && sc.is_none() {
6148 sc = Some(1);
6150 }
6151
6152 let sr = sr.unwrap_or(1);
6153 let sc = sc.unwrap_or(1);
6154 let er = er.unwrap_or(sr.saturating_sub(1));
6155 let ec = ec.unwrap_or(sc.saturating_sub(1));
6156
6157 if self.force_materialize_range_views {
6158 if er < sr || ec < sc {
6159 return Ok(RangeView::from_owned_rows(
6160 Vec::new(),
6161 self.config.date_system,
6162 ));
6163 }
6164 let h = (er - sr + 1) as u64;
6165 let w = (ec - sc + 1) as u64;
6166 let cell_count = h.saturating_mul(w);
6167 if cell_count <= self.config.spill.max_spill_cells as u64 {
6168 let mut rows: Vec<Vec<LiteralValue>> = Vec::with_capacity(h as usize);
6169 for r in sr..=er {
6170 let mut rowv: Vec<LiteralValue> = Vec::with_capacity(w as usize);
6171 for c in sc..=ec {
6172 rowv.push(
6173 self.get_cell_value(sheet_name, r, c)
6174 .unwrap_or(LiteralValue::Empty),
6175 );
6176 }
6177 rows.push(rowv);
6178 }
6179 return Ok(RangeView::from_owned_rows(rows, self.config.date_system));
6180 }
6181 }
6182
6183 let Some(asheet) = self.sheet_store().sheet(sheet_name) else {
6184 return Ok(RangeView::from_owned_rows(
6185 Vec::new(),
6186 self.config.date_system,
6187 ));
6188 };
6189
6190 let rv = if er < sr || ec < sc {
6191 asheet.range_view(1, 1, 0, 0)
6192 } else {
6193 let sr0 = sr.saturating_sub(1) as usize;
6194 let sc0 = sc.saturating_sub(1) as usize;
6195 let er0 = er.saturating_sub(1) as usize;
6196 let ec0 = ec.saturating_sub(1) as usize;
6197 asheet.range_view(sr0, sc0, er0, ec0)
6198 };
6199
6200 Ok(rv)
6201 }
6202 ReferenceType::Cell { .. } => {
6203 let shared = self.resolve_shared_ref(reference, current_sheet)?;
6204 let formualizer_common::SheetRef::Cell(cell) = shared else {
6205 return Err(ExcelError::new(ExcelErrorKind::Ref));
6206 };
6207 let addr = CellRef::try_from_shared(cell)?;
6208 let sheet_id = addr.sheet_id;
6209 let sheet_name = self.graph.sheet_name(sheet_id);
6210 let row = addr.coord.row() + 1;
6211 let col = addr.coord.col() + 1;
6212
6213 if self.force_materialize_range_views {
6214 let v = self
6215 .get_cell_value(sheet_name, row, col)
6216 .unwrap_or(LiteralValue::Empty);
6217 return Ok(RangeView::from_owned_rows(
6218 vec![vec![v]],
6219 self.config.date_system,
6220 ));
6221 }
6222
6223 if let Some(asheet) = self.sheet_store().sheet(sheet_name) {
6224 let r0 = row.saturating_sub(1) as usize;
6225 let c0 = col.saturating_sub(1) as usize;
6226 let rv = asheet.range_view(r0, c0, r0, c0);
6227 Ok(rv)
6228 } else {
6229 let v = self
6230 .get_cell_value(sheet_name, row, col)
6231 .unwrap_or(LiteralValue::Empty);
6232 Ok(RangeView::from_owned_rows(
6233 vec![vec![v]],
6234 self.config.date_system,
6235 ))
6236 }
6237 }
6238 ReferenceType::NamedRange(name) => {
6239 if let Some(current_id) = self.graph.sheet_id(current_sheet)
6240 && let Some(named) = self.graph.resolve_name_entry(name, current_id)
6241 {
6242 match &named.definition {
6243 NamedDefinition::Cell(cell_ref) => {
6244 let sheet_name = self.graph.sheet_name(cell_ref.sheet_id);
6245 if self.force_materialize_range_views {
6246 let v = self
6247 .get_cell_value(
6248 sheet_name,
6249 cell_ref.coord.row() + 1,
6250 cell_ref.coord.col() + 1,
6251 )
6252 .unwrap_or(LiteralValue::Empty);
6253 return Ok(RangeView::from_owned_rows(
6254 vec![vec![v]],
6255 self.config.date_system,
6256 ));
6257 } else {
6258 let asheet = self
6259 .sheet_store()
6260 .sheet(sheet_name)
6261 .expect("Arrow sheet missing for named cell");
6262 let r0 = cell_ref.coord.row() as usize;
6263 let c0 = cell_ref.coord.col() as usize;
6264 let rv = asheet.range_view(r0, c0, r0, c0);
6265 return Ok(rv);
6266 }
6267 }
6268 NamedDefinition::Range(range_ref) => {
6269 let sheet_name = self.graph.sheet_name(range_ref.start.sheet_id);
6270 let sr = range_ref.start.coord.row() + 1;
6271 let sc = range_ref.start.coord.col() + 1;
6272 let er = range_ref.end.coord.row() + 1;
6273 let ec = range_ref.end.coord.col() + 1;
6274 if self.force_materialize_range_views {
6275 let h = (er.saturating_sub(sr) + 1) as u64;
6276 let w = (ec.saturating_sub(sc) + 1) as u64;
6277 let cell_count = h.saturating_mul(w);
6278 if cell_count <= self.config.spill.max_spill_cells as u64 {
6279 let mut rows: Vec<Vec<LiteralValue>> =
6280 Vec::with_capacity(h as usize);
6281 for r in sr..=er {
6282 let mut rowv: Vec<LiteralValue> =
6283 Vec::with_capacity(w as usize);
6284 for c in sc..=ec {
6285 rowv.push(
6286 self.get_cell_value(sheet_name, r, c)
6287 .unwrap_or(LiteralValue::Empty),
6288 );
6289 }
6290 rows.push(rowv);
6291 }
6292 return Ok(RangeView::from_owned_rows(
6293 rows,
6294 self.config.date_system,
6295 ));
6296 }
6297 }
6298 let asheet = self
6299 .sheet_store()
6300 .sheet(sheet_name)
6301 .expect("Arrow sheet missing for named range");
6302 let sr0 = range_ref.start.coord.row() as usize;
6303 let sc0 = range_ref.start.coord.col() as usize;
6304 let er0 = range_ref.end.coord.row() as usize;
6305 let ec0 = range_ref.end.coord.col() as usize;
6306 let rv = asheet.range_view(sr0, sc0, er0, ec0);
6307 return Ok(rv);
6308 }
6309 NamedDefinition::Literal(v) => {
6310 return Ok(RangeView::from_owned_rows(
6311 vec![vec![v.clone()]],
6312 self.config.date_system,
6313 ));
6314 }
6315 NamedDefinition::Formula { .. } => {
6316 if let Some(value) = self.graph.get_value(named.vertex) {
6317 return Ok(RangeView::from_owned_rows(
6318 vec![vec![value]],
6319 self.config.date_system,
6320 ));
6321 }
6322 }
6323 }
6324 }
6325
6326 if let Some(source) = self.graph.resolve_source_scalar_entry(name) {
6327 let version = source
6328 .version
6329 .or_else(|| self.resolver.source_scalar_version(name));
6330 let v = self.resolve_source_scalar_cached(name, version)?;
6331 return Ok(RangeView::from_owned_rows(
6332 vec![vec![v]],
6333 self.config.date_system,
6334 ));
6335 }
6336
6337 let data = self.resolver.resolve_named_range_reference(name)?;
6338 Ok(RangeView::from_owned_rows(data, self.config.date_system))
6339 }
6340 ReferenceType::Table(tref) => {
6341 if let Some(table) = self.graph.resolve_table_entry(&tref.name) {
6342 let sheet_name = self.graph.sheet_name(table.range.start.sheet_id);
6343 let asheet = self
6344 .sheet_store()
6345 .sheet(sheet_name)
6346 .expect("Arrow sheet missing for table reference");
6347
6348 let sr0 = table.range.start.coord.row() as usize;
6349 let sc0 = table.range.start.coord.col() as usize;
6350 let er0 = table.range.end.coord.row() as usize;
6351 let ec0 = table.range.end.coord.col() as usize;
6352
6353 let has_totals = table.totals_row;
6354 let has_headers = table.header_row;
6355 let data_sr = if has_headers {
6356 sr0.saturating_add(1)
6357 } else {
6358 sr0
6359 };
6360 let data_er = if has_totals {
6361 er0.saturating_sub(1)
6362 } else {
6363 er0
6364 };
6365
6366 let select = |sr: usize, sc: usize, er: usize, ec: usize| {
6367 if sr > er || sc > ec {
6368 asheet.range_view(1, 1, 0, 0)
6369 } else {
6370 asheet.range_view(sr, sc, er, ec)
6371 }
6372 };
6373
6374 let av = match &tref.specifier {
6375 None => {
6376 return Err(ExcelError::new(ExcelErrorKind::NImpl).with_message(
6377 "Table reference without specifier is unsupported".to_string(),
6378 ));
6379 }
6380 Some(formualizer_parse::parser::TableSpecifier::Column(col)) => {
6381 let Some(idx) = table.col_index(col) else {
6382 return Err(ExcelError::new(ExcelErrorKind::Ref).with_message(
6383 "Column refers to unknown table column".to_string(),
6384 ));
6385 };
6386 let c0 = sc0 + idx;
6387 select(data_sr, c0, data_er, c0)
6388 }
6389 Some(formualizer_parse::parser::TableSpecifier::ColumnRange(
6390 start,
6391 end,
6392 )) => {
6393 let Some(si) = table.col_index(start) else {
6394 return Err(ExcelError::new(ExcelErrorKind::Ref).with_message(
6395 "Column range refers to unknown column(s)".to_string(),
6396 ));
6397 };
6398 let Some(ei) = table.col_index(end) else {
6399 return Err(ExcelError::new(ExcelErrorKind::Ref).with_message(
6400 "Column range refers to unknown column(s)".to_string(),
6401 ));
6402 };
6403 let (mut a, mut b) = (si, ei);
6404 if a > b {
6405 std::mem::swap(&mut a, &mut b);
6406 }
6407 let c_start = sc0 + a;
6408 let c_end = sc0 + b;
6409 select(data_sr, c_start, data_er, c_end)
6410 }
6411 Some(formualizer_parse::parser::TableSpecifier::All)
6412 | Some(formualizer_parse::parser::TableSpecifier::SpecialItem(
6413 formualizer_parse::parser::SpecialItem::All,
6414 )) => select(sr0, sc0, er0, ec0),
6415 Some(formualizer_parse::parser::TableSpecifier::Data)
6416 | Some(formualizer_parse::parser::TableSpecifier::SpecialItem(
6417 formualizer_parse::parser::SpecialItem::Data,
6418 )) => select(data_sr, sc0, data_er, ec0),
6419 Some(formualizer_parse::parser::TableSpecifier::Headers)
6420 | Some(formualizer_parse::parser::TableSpecifier::SpecialItem(
6421 formualizer_parse::parser::SpecialItem::Headers,
6422 )) => {
6423 if !has_headers {
6424 asheet.range_view(1, 1, 0, 0)
6425 } else {
6426 select(sr0, sc0, sr0, ec0)
6427 }
6428 }
6429 Some(formualizer_parse::parser::TableSpecifier::Totals)
6430 | Some(formualizer_parse::parser::TableSpecifier::SpecialItem(
6431 formualizer_parse::parser::SpecialItem::Totals,
6432 )) => {
6433 if !has_totals {
6434 asheet.range_view(1, 1, 0, 0)
6435 } else {
6436 select(er0, sc0, er0, ec0)
6437 }
6438 }
6439 Some(formualizer_parse::parser::TableSpecifier::SpecialItem(
6440 formualizer_parse::parser::SpecialItem::ThisRow,
6441 )) => {
6442 return Err(ExcelError::new(ExcelErrorKind::NImpl).with_message(
6443 "@ (This Row) requires table-aware context; not yet supported"
6444 .to_string(),
6445 ));
6446 }
6447 Some(formualizer_parse::parser::TableSpecifier::Row(_))
6448 | Some(formualizer_parse::parser::TableSpecifier::Combination(_)) => {
6449 return Err(ExcelError::new(ExcelErrorKind::NImpl).with_message(
6450 "Complex structured references not yet supported".to_string(),
6451 ));
6452 }
6453 };
6454
6455 return Ok(av);
6456 }
6457
6458 if let Some(source) = self.graph.resolve_source_table_entry(&tref.name) {
6459 let version = source
6460 .version
6461 .or_else(|| self.resolver.source_table_version(&tref.name));
6462 let table = self.resolve_source_table_cached(&tref.name, version)?;
6463 return self.source_table_to_range_view(table.as_ref(), &tref.specifier);
6464 }
6465
6466 let boxed = self.resolve_range_like(&ReferenceType::Table(tref.clone()))?;
6468 let owned = boxed.materialise().into_owned();
6469 Ok(RangeView::from_owned_rows(owned, self.config.date_system))
6470 }
6471 }
6472 }
6473
6474 fn build_criteria_mask(
6475 &self,
6476 view: &RangeView<'_>,
6477 col_in_view: usize,
6478 pred: &crate::args::CriteriaPredicate,
6479 ) -> Option<std::sync::Arc<arrow_array::BooleanArray>> {
6480 if view.dims().1 == 0 {
6481 return None;
6482 }
6483 let sheet_rows = view.sheet().nrows as usize;
6486 if sheet_rows == 0 || view.start_row() >= sheet_rows {
6487 return Some(std::sync::Arc::new(arrow_array::BooleanArray::new_null(0)));
6488 }
6489 compute_criteria_mask(view, col_in_view, pred)
6490 }
6491}
6492
6493impl<R> Engine<R>
6494where
6495 R: EvaluationContext,
6496{
6497 fn clear_spill_projection_and_mirror(
6498 &mut self,
6499 anchor_vertex: VertexId,
6500 delta: Option<&mut DeltaCollector>,
6501 ) {
6502 let spill_cells = self
6503 .graph
6504 .spill_cells_for_anchor(anchor_vertex)
6505 .map(|cells| cells.to_vec())
6506 .unwrap_or_default();
6507 if spill_cells.is_empty() {
6508 return;
6509 }
6510
6511 if let Some(delta) = delta
6512 && delta.mode != DeltaMode::Off
6513 {
6514 let empty = LiteralValue::Empty;
6515 for cell in spill_cells.iter() {
6516 let sheet_name = self.graph.sheet_name(cell.sheet_id);
6517 let old = self
6518 .get_cell_value(sheet_name, cell.coord.row() + 1, cell.coord.col() + 1)
6519 .unwrap_or(LiteralValue::Empty);
6520 if old != empty {
6521 delta.record_cell(cell.sheet_id, cell.coord.row(), cell.coord.col());
6522 }
6523 }
6524 }
6525
6526 self.graph.clear_spill_region(anchor_vertex);
6527
6528 if self.config.arrow_storage_enabled
6529 && self.config.delta_overlay_enabled
6530 && self.config.write_formula_overlay_enabled
6531 {
6532 let empty = LiteralValue::Empty;
6533 for cell in spill_cells.iter() {
6534 let sheet_name = self.graph.sheet_name(cell.sheet_id).to_string();
6535 self.mirror_value_to_computed_overlay(
6536 &sheet_name,
6537 cell.coord.row() + 1,
6538 cell.coord.col() + 1,
6539 &empty,
6540 );
6541 }
6542 }
6543 }
6544
6545 fn commit_spill_and_mirror(
6547 &mut self,
6548 anchor_vertex: VertexId,
6549 targets: &[CellRef],
6550 rows: Vec<Vec<LiteralValue>>,
6551 delta: Option<&mut DeltaCollector>,
6552 overwritable_formulas: Option<&rustc_hash::FxHashSet<VertexId>>,
6553 ) -> Result<(), ExcelError> {
6554 let prev_spill_cells = self
6555 .graph
6556 .spill_cells_for_anchor(anchor_vertex)
6557 .map(|cells| cells.to_vec())
6558 .unwrap_or_default();
6559
6560 if let Some(delta) = delta
6561 && delta.mode != DeltaMode::Off
6562 {
6563 let target_set: FxHashSet<CellRef> = targets.iter().copied().collect();
6564 let empty = LiteralValue::Empty;
6565
6566 for cell in prev_spill_cells.iter() {
6568 if target_set.contains(cell) {
6569 continue;
6570 }
6571 let sheet_name = self.graph.sheet_name(cell.sheet_id);
6572 let old = self
6573 .get_cell_value(sheet_name, cell.coord.row() + 1, cell.coord.col() + 1)
6574 .unwrap_or(LiteralValue::Empty);
6575 if old != empty {
6576 delta.record_cell(cell.sheet_id, cell.coord.row(), cell.coord.col());
6577 }
6578 }
6579
6580 if !targets.is_empty() && !rows.is_empty() && !rows[0].is_empty() {
6582 let width = rows[0].len();
6583 for (idx, cell) in targets.iter().enumerate() {
6584 let r_off = idx / width;
6585 let c_off = idx % width;
6586 let new = rows
6587 .get(r_off)
6588 .and_then(|r| r.get(c_off))
6589 .cloned()
6590 .unwrap_or(LiteralValue::Empty);
6591 let sheet_name = self.graph.sheet_name(cell.sheet_id);
6592 let old = self
6593 .get_cell_value(sheet_name, cell.coord.row() + 1, cell.coord.col() + 1)
6594 .unwrap_or(LiteralValue::Empty);
6595 if old != new {
6596 delta.record_cell(cell.sheet_id, cell.coord.row(), cell.coord.col());
6597 }
6598 }
6599 } else {
6600 for cell in targets.iter() {
6602 let sheet_name = self.graph.sheet_name(cell.sheet_id);
6603 let old = self
6604 .get_cell_value(sheet_name, cell.coord.row() + 1, cell.coord.col() + 1)
6605 .unwrap_or(LiteralValue::Empty);
6606 if !matches!(old, LiteralValue::Empty) {
6607 delta.record_cell(cell.sheet_id, cell.coord.row(), cell.coord.col());
6608 }
6609 }
6610 }
6611 }
6612
6613 let arrow_sheets = &self.arrow_sheets;
6616 self.spill_mgr.commit_array_with_value_probe(
6617 &mut self.graph,
6618 anchor_vertex,
6619 targets,
6620 rows.clone(),
6621 overwritable_formulas,
6622 |g, cell| {
6623 let sheet_name = g.sheet_name(cell.sheet_id);
6624 let asheet = arrow_sheets.sheet(sheet_name)?;
6625 let r0 = cell.coord.row() as usize;
6626 let c0 = cell.coord.col() as usize;
6627 let v = asheet.get_cell_value(r0, c0);
6628 if matches!(v, LiteralValue::Empty) {
6629 None
6630 } else {
6631 Some(v)
6632 }
6633 },
6634 )?;
6635
6636 if self.config.arrow_storage_enabled
6637 && self.config.delta_overlay_enabled
6638 && self.config.write_formula_overlay_enabled
6639 {
6640 if !prev_spill_cells.is_empty() {
6641 let target_set: FxHashSet<CellRef> = targets.iter().copied().collect();
6642 let empty = LiteralValue::Empty;
6643 for cell in prev_spill_cells.iter() {
6644 if !target_set.contains(cell) {
6645 let sheet_name = self.graph.sheet_name(cell.sheet_id).to_string();
6646 self.mirror_value_to_computed_overlay(
6647 &sheet_name,
6648 cell.coord.row() + 1,
6649 cell.coord.col() + 1,
6650 &empty,
6651 );
6652 }
6653 }
6654 }
6655
6656 for (idx, cell) in targets.iter().enumerate() {
6657 if rows.is_empty() || rows[0].is_empty() {
6658 break;
6659 }
6660 let width = rows[0].len();
6661 let r_off = idx / width;
6662 let c_off = idx % width;
6663 let v = rows[r_off][c_off].clone();
6664 let sheet_name = self.graph.sheet_name(cell.sheet_id).to_string();
6665 self.mirror_value_to_computed_overlay(
6666 &sheet_name,
6667 cell.coord.row() + 1,
6668 cell.coord.col() + 1,
6669 &v,
6670 );
6671 }
6672 }
6673 Ok(())
6674 }
6675}
6676
6677use crate::engine::effects::Effect;
6682use crate::engine::graph::editor::change_log::{ChangeEvent, ChangeLog, SpillSnapshot};
6683
6684impl<R> Engine<R>
6685where
6686 R: EvaluationContext,
6687{
6688 pub(crate) fn plan_vertex_effects(
6695 &mut self,
6696 vertex_id: VertexId,
6697 computed_value: LiteralValue,
6698 overwritable_formulas: Option<&rustc_hash::FxHashSet<VertexId>>,
6699 ) -> Result<Vec<Effect>, ExcelError> {
6700 let kind = self.graph.get_vertex_kind(vertex_id);
6701 let is_formula = matches!(kind, VertexKind::FormulaScalar | VertexKind::FormulaArray);
6702
6703 if !is_formula {
6707 if let Some(cell) = self.graph.get_cell_ref(vertex_id)
6708 && let Some(owner) = self.graph.spill_registry_anchor_for_cell(cell)
6709 && owner != vertex_id
6710 {
6711 return Ok(Vec::new());
6712 }
6713 return Ok(vec![Effect::WriteCell {
6715 vertex_id,
6716 value: computed_value,
6717 }]);
6718 }
6719
6720 match computed_value {
6721 LiteralValue::Array(rows) => {
6722 self.plan_array_effects(vertex_id, rows, overwritable_formulas)
6723 }
6724 other => self.plan_scalar_effects(vertex_id, other),
6725 }
6726 }
6727
6728 fn plan_scalar_effects(
6730 &self,
6731 vertex_id: VertexId,
6732 value: LiteralValue,
6733 ) -> Result<Vec<Effect>, ExcelError> {
6734 let has_spill = self
6735 .graph
6736 .spill_cells_for_anchor(vertex_id)
6737 .is_some_and(|c| !c.is_empty());
6738
6739 let mut effects = Vec::new();
6740 if has_spill {
6741 effects.push(Effect::SpillClear {
6742 anchor_vertex: vertex_id,
6743 });
6744 }
6745 effects.push(Effect::WriteCell { vertex_id, value });
6746 Ok(effects)
6747 }
6748
6749 fn plan_array_effects(
6751 &mut self,
6752 vertex_id: VertexId,
6753 rows: Vec<Vec<LiteralValue>>,
6754 overwritable_formulas: Option<&rustc_hash::FxHashSet<VertexId>>,
6755 ) -> Result<Vec<Effect>, ExcelError> {
6756 self.graph.set_kind(vertex_id, VertexKind::FormulaArray);
6758
6759 let anchor = self
6760 .graph
6761 .get_cell_ref(vertex_id)
6762 .expect("cell ref for vertex");
6763 let sheet_id = anchor.sheet_id;
6764 let h = rows.len() as u32;
6765 let w = rows.first().map(|r| r.len()).unwrap_or(0) as u32;
6766
6767 let spill_cells = (h as u64).saturating_mul(w as u64);
6769 if spill_cells > self.config.spill.max_spill_cells as u64 {
6770 return self.plan_spill_error_effects(vertex_id, "SpillTooLarge", h, w);
6771 }
6772
6773 const PACKED_MAX_ROW: u32 = 1_048_575;
6775 const PACKED_MAX_COL: u32 = 16_383;
6776 let end_row = anchor.coord.row().saturating_add(h).saturating_sub(1);
6777 let end_col = anchor.coord.col().saturating_add(w).saturating_sub(1);
6778 if end_row > PACKED_MAX_ROW || end_col > PACKED_MAX_COL {
6779 return self.plan_spill_error_effects(vertex_id, "Spill exceeds sheet bounds", h, w);
6780 }
6781
6782 let mut targets = Vec::new();
6783 for r in 0..h {
6784 for c in 0..w {
6785 targets.push(self.graph.make_cell_ref_internal(
6786 sheet_id,
6787 anchor.coord.row() + r,
6788 anchor.coord.col() + c,
6789 ));
6790 }
6791 }
6792
6793 match self.spill_mgr.reserve(
6795 vertex_id,
6796 anchor,
6797 SpillShape { rows: h, cols: w },
6798 SpillMeta {
6799 epoch: self.recalc_epoch,
6800 config: self.config.spill,
6801 },
6802 ) {
6803 Ok(()) => {
6804 if let Err(_e) = self.graph.plan_spill_region_allowing_formula_overwrite(
6806 vertex_id,
6807 &targets,
6808 overwritable_formulas,
6809 ) {
6810 return self.plan_spill_error_effects(vertex_id, "Spill blocked", h, w);
6811 }
6812
6813 if !self.graph.value_cache_enabled() {
6817 let sheet_name = self.graph.sheet_name(sheet_id);
6818 if let Some(asheet) = self.sheet_store().sheet(sheet_name) {
6819 for cell in targets.iter() {
6820 if *cell == anchor {
6822 continue;
6823 }
6824 if self.graph.spill_registry_anchor_for_cell(*cell).is_some() {
6826 continue;
6827 }
6828 if let Some(&vid) = self.graph.get_vertex_id_for_address(cell)
6830 && vid != vertex_id
6831 {
6832 match self.graph.get_vertex_kind(vid) {
6833 VertexKind::FormulaScalar | VertexKind::FormulaArray => {
6834 continue;
6835 }
6836 _ => {}
6837 }
6838 }
6839
6840 let v = asheet.get_cell_value(
6841 cell.coord.row() as usize,
6842 cell.coord.col() as usize,
6843 );
6844 if !matches!(v, LiteralValue::Empty) {
6845 return self.plan_spill_error_effects(
6846 vertex_id,
6847 "BlockedByValue",
6848 h,
6849 w,
6850 );
6851 }
6852 }
6853 }
6854 }
6855
6856 let top_left = rows
6857 .first()
6858 .and_then(|r| r.first())
6859 .cloned()
6860 .unwrap_or(LiteralValue::Empty);
6861
6862 let mut effects = Vec::new();
6863 let has_prev = self
6865 .graph
6866 .spill_cells_for_anchor(vertex_id)
6867 .is_some_and(|c| !c.is_empty());
6868 if has_prev {
6869 effects.push(Effect::SpillClear {
6870 anchor_vertex: vertex_id,
6871 });
6872 }
6873 effects.push(Effect::SpillCommit {
6874 anchor_vertex: vertex_id,
6875 anchor_cell: anchor,
6876 target_cells: targets,
6877 values: rows,
6878 });
6879 effects.push(Effect::WriteCell {
6880 vertex_id,
6881 value: top_left,
6882 });
6883 Ok(effects)
6884 }
6885 Err(e) => {
6886 let msg = e.message.unwrap_or_else(|| "Spill blocked".to_string());
6887 self.plan_spill_error_effects(vertex_id, &msg, h, w)
6888 }
6889 }
6890 }
6891
6892 fn plan_spill_error_effects(
6894 &self,
6895 vertex_id: VertexId,
6896 message: &str,
6897 expected_rows: u32,
6898 expected_cols: u32,
6899 ) -> Result<Vec<Effect>, ExcelError> {
6900 let spill_err = ExcelError::new(ExcelErrorKind::Spill)
6901 .with_message(message)
6902 .with_extra(formualizer_common::ExcelErrorExtra::Spill {
6903 expected_rows,
6904 expected_cols,
6905 });
6906 let spill_val = LiteralValue::Error(spill_err);
6907
6908 let effects = vec![
6909 Effect::SpillClear {
6910 anchor_vertex: vertex_id,
6911 },
6912 Effect::WriteCell {
6913 vertex_id,
6914 value: spill_val,
6915 },
6916 ];
6917 Ok(effects)
6918 }
6919
6920 pub(crate) fn apply_effect(
6922 &mut self,
6923 effect: &Effect,
6924 delta: Option<&mut DeltaCollector>,
6925 log: Option<&mut ChangeLog>,
6926 ) -> Result<(), ExcelError> {
6927 match effect {
6928 Effect::WriteCell { vertex_id, value } => {
6929 self.apply_write_cell(*vertex_id, value, delta);
6930 }
6931 Effect::SpillClear { anchor_vertex } => {
6932 self.apply_spill_clear(*anchor_vertex, delta, log);
6933 }
6934 Effect::SpillCommit {
6935 anchor_vertex,
6936 anchor_cell: _,
6937 target_cells,
6938 values,
6939 } => {
6940 self.apply_spill_commit(*anchor_vertex, target_cells, values.clone(), delta, log)?;
6941 }
6942 }
6943 Ok(())
6944 }
6945
6946 fn apply_write_cell(
6948 &mut self,
6949 vertex_id: VertexId,
6950 value: &LiteralValue,
6951 delta: Option<&mut DeltaCollector>,
6952 ) {
6953 if let Some(d) = delta
6954 && d.mode != DeltaMode::Off
6955 && let Some(cell) = self.graph.get_cell_ref_for_vertex(vertex_id)
6956 {
6957 let sheet_name = self.graph.sheet_name(cell.sheet_id);
6958 let old = self
6959 .read_cell_value(sheet_name, cell.coord.row() + 1, cell.coord.col() + 1)
6960 .unwrap_or(LiteralValue::Empty);
6961 if old != *value {
6962 d.record_cell(cell.sheet_id, cell.coord.row(), cell.coord.col());
6963 }
6964 }
6965 self.graph.update_vertex_value(vertex_id, value.clone());
6966 self.mirror_vertex_value_to_overlay(vertex_id, value);
6967 }
6968
6969 fn apply_spill_clear(
6971 &mut self,
6972 anchor_vertex: VertexId,
6973 delta: Option<&mut DeltaCollector>,
6974 log: Option<&mut ChangeLog>,
6975 ) {
6976 let spill_cells = self
6977 .graph
6978 .spill_cells_for_anchor(anchor_vertex)
6979 .map(|cells| cells.to_vec())
6980 .unwrap_or_default();
6981 if spill_cells.is_empty() {
6982 return;
6983 }
6984
6985 let snapshot = if log.is_some() {
6987 self.snapshot_spill_for_anchor(anchor_vertex)
6988 } else {
6989 None
6990 };
6991
6992 if let Some(d) = delta
6994 && d.mode != DeltaMode::Off
6995 {
6996 let empty = LiteralValue::Empty;
6997 for cell in spill_cells.iter() {
6998 let sheet_name = self.graph.sheet_name(cell.sheet_id);
6999 let old = self
7000 .get_cell_value(sheet_name, cell.coord.row() + 1, cell.coord.col() + 1)
7001 .unwrap_or(LiteralValue::Empty);
7002 if old != empty {
7003 d.record_cell(cell.sheet_id, cell.coord.row(), cell.coord.col());
7004 }
7005 }
7006 }
7007
7008 self.graph.clear_spill_region(anchor_vertex);
7009
7010 if self.config.arrow_storage_enabled
7012 && self.config.delta_overlay_enabled
7013 && self.config.write_formula_overlay_enabled
7014 {
7015 let empty = LiteralValue::Empty;
7016 for cell in spill_cells.iter() {
7017 let sheet_name = self.graph.sheet_name(cell.sheet_id).to_string();
7018 self.mirror_value_to_computed_overlay(
7019 &sheet_name,
7020 cell.coord.row() + 1,
7021 cell.coord.col() + 1,
7022 &empty,
7023 );
7024 }
7025 }
7026
7027 if let Some(log) = log
7029 && let Some(old) = snapshot
7030 {
7031 log.record(ChangeEvent::SpillCleared {
7032 anchor: anchor_vertex,
7033 old,
7034 });
7035 }
7036 }
7037
7038 fn apply_spill_commit(
7040 &mut self,
7041 anchor_vertex: VertexId,
7042 target_cells: &[CellRef],
7043 values: Vec<Vec<LiteralValue>>,
7044 delta: Option<&mut DeltaCollector>,
7045 log: Option<&mut ChangeLog>,
7046 ) -> Result<(), ExcelError> {
7047 let old_snapshot = if log.is_some() {
7049 self.snapshot_spill_for_anchor(anchor_vertex)
7050 } else {
7051 None
7052 };
7053
7054 self.commit_spill_and_mirror(
7056 anchor_vertex,
7057 target_cells,
7058 values.clone(),
7059 delta,
7060 None, )?;
7062
7063 if let Some(log) = log {
7065 log.record(ChangeEvent::SpillCommitted {
7066 anchor: anchor_vertex,
7067 old: old_snapshot,
7068 new: SpillSnapshot {
7069 target_cells: target_cells.to_vec(),
7070 values,
7071 },
7072 });
7073 }
7074 Ok(())
7075 }
7076
7077 fn snapshot_spill_for_anchor(&self, anchor: VertexId) -> Option<SpillSnapshot> {
7082 let cells = self.graph.spill_cells_for_anchor(anchor)?.to_vec();
7083 if cells.is_empty() {
7084 return None;
7085 }
7086
7087 let max = self.config.spill.max_spill_cells as usize;
7088 let mut cells = cells;
7089 if cells.len() > max {
7090 cells.truncate(max);
7091 }
7092
7093 let first = *cells.first().expect("non-empty spill cells");
7094 let sheet_name = self.graph.sheet_name(first.sheet_id).to_string();
7095 let row0 = first.coord.row();
7096 let col0 = first.coord.col();
7097
7098 let mut max_row = row0;
7099 let mut max_col = col0;
7100 let mut by_coord: FxHashMap<(u32, u32), LiteralValue> = FxHashMap::default();
7101 for cell in &cells {
7102 max_row = max_row.max(cell.coord.row());
7103 max_col = max_col.max(cell.coord.col());
7104 let v = self
7105 .get_cell_value(&sheet_name, cell.coord.row() + 1, cell.coord.col() + 1)
7106 .unwrap_or(LiteralValue::Empty);
7107 by_coord.insert((cell.coord.row(), cell.coord.col()), v);
7108 }
7109
7110 let rows = (max_row - row0 + 1) as usize;
7111 let cols = (max_col - col0 + 1) as usize;
7112 let mut values: Vec<Vec<LiteralValue>> = Vec::with_capacity(rows);
7113 for r in 0..rows {
7114 let mut row: Vec<LiteralValue> = Vec::with_capacity(cols);
7115 for c in 0..cols {
7116 row.push(
7117 by_coord
7118 .get(&(row0 + r as u32, col0 + c as u32))
7119 .cloned()
7120 .unwrap_or(LiteralValue::Empty),
7121 );
7122 }
7123 values.push(row);
7124 }
7125
7126 Some(SpillSnapshot {
7127 target_cells: cells,
7128 values,
7129 })
7130 }
7131
7132 fn evaluate_layer_sequential_effects(
7136 &mut self,
7137 layer: &super::scheduler::Layer,
7138 ) -> Result<usize, ExcelError> {
7139 for &vertex_id in &layer.vertices {
7140 let value = match self.evaluate_vertex_immutable(vertex_id) {
7141 Ok(v) => v,
7142 Err(e) => LiteralValue::Error(e),
7143 };
7144 let effects = self.plan_vertex_effects(vertex_id, value, None)?;
7145 for effect in &effects {
7146 self.apply_effect(effect, None, None)?;
7147 }
7148 }
7149 Ok(layer.vertices.len())
7150 }
7151
7152 fn evaluate_layer_sequential_with_delta_effects(
7154 &mut self,
7155 layer: &super::scheduler::Layer,
7156 delta: &mut DeltaCollector,
7157 ) -> Result<usize, ExcelError> {
7158 for &vertex_id in &layer.vertices {
7159 let value = match self.evaluate_vertex_immutable(vertex_id) {
7160 Ok(v) => v,
7161 Err(e) => LiteralValue::Error(e),
7162 };
7163 let effects = self.plan_vertex_effects(vertex_id, value, None)?;
7164 for effect in &effects {
7165 self.apply_effect(effect, Some(delta), None)?;
7166 }
7167 }
7168 Ok(layer.vertices.len())
7169 }
7170
7171 fn evaluate_layer_sequential_cancellable_effects(
7173 &mut self,
7174 layer: &super::scheduler::Layer,
7175 cancel_flag: &AtomicBool,
7176 ) -> Result<usize, ExcelError> {
7177 for (i, &vertex_id) in layer.vertices.iter().enumerate() {
7178 if i % 256 == 0 && cancel_flag.load(Ordering::Relaxed) {
7179 return Err(ExcelError::new(ExcelErrorKind::Cancelled)
7180 .with_message("Evaluation cancelled within layer".to_string()));
7181 }
7182 let value = match self.evaluate_vertex_immutable(vertex_id) {
7183 Ok(v) => v,
7184 Err(e) => LiteralValue::Error(e),
7185 };
7186 let effects = self.plan_vertex_effects(vertex_id, value, None)?;
7187 for effect in &effects {
7188 self.apply_effect(effect, None, None)?;
7189 }
7190 }
7191 Ok(layer.vertices.len())
7192 }
7193
7194 fn evaluate_layer_sequential_cancellable_demand_driven_effects(
7196 &mut self,
7197 layer: &super::scheduler::Layer,
7198 cancel_flag: &AtomicBool,
7199 ) -> Result<usize, ExcelError> {
7200 for (i, &vertex_id) in layer.vertices.iter().enumerate() {
7201 if i % 128 == 0 && cancel_flag.load(Ordering::Relaxed) {
7202 return Err(ExcelError::new(ExcelErrorKind::Cancelled)
7203 .with_message("Demand-driven evaluation cancelled within layer".to_string()));
7204 }
7205 let value = match self.evaluate_vertex_immutable(vertex_id) {
7206 Ok(v) => v,
7207 Err(e) => LiteralValue::Error(e),
7208 };
7209 let effects = self.plan_vertex_effects(vertex_id, value, None)?;
7210 for effect in &effects {
7211 self.apply_effect(effect, None, None)?;
7212 }
7213 }
7214 Ok(layer.vertices.len())
7215 }
7216
7217 fn evaluate_layer_parallel_effects(
7219 &mut self,
7220 layer: &super::scheduler::Layer,
7221 ) -> Result<usize, ExcelError> {
7222 use rayon::prelude::*;
7223
7224 let thread_pool = self.thread_pool.as_ref().unwrap().clone();
7225
7226 let mut phase1: Vec<VertexId> = Vec::new();
7227 let mut phase2: Vec<VertexId> = Vec::new();
7228 for &vid in &layer.vertices {
7229 if self.graph.get_range_dependencies(vid).is_some() {
7230 phase2.push(vid);
7231 } else {
7232 phase1.push(vid);
7233 }
7234 }
7235
7236 let inflight: rustc_hash::FxHashSet<VertexId> = layer.vertices.iter().copied().collect();
7237 let mut applied = 0usize;
7238
7239 for group in [&phase1[..], &phase2[..]] {
7240 if group.is_empty() {
7241 continue;
7242 }
7243
7244 let results: Result<Vec<(VertexId, LiteralValue)>, ExcelError> =
7245 thread_pool.install(|| {
7246 group
7247 .par_iter()
7248 .map(
7249 |&vertex_id| match self.evaluate_vertex_immutable(vertex_id) {
7250 Ok(v) => Ok((vertex_id, v)),
7251 Err(e) => Ok((vertex_id, LiteralValue::Error(e))),
7252 },
7253 )
7254 .collect()
7255 });
7256
7257 match results {
7258 Ok(vertex_results) => {
7259 let mut arrays: Vec<(VertexId, LiteralValue)> = Vec::new();
7262 let mut others: Vec<(VertexId, LiteralValue)> = Vec::new();
7263 for (vertex_id, result) in vertex_results {
7264 if matches!(result, LiteralValue::Array(_)) {
7265 arrays.push((vertex_id, result));
7266 } else {
7267 others.push((vertex_id, result));
7268 }
7269 }
7270 for (vertex_id, result) in arrays {
7271 let effects =
7272 self.plan_vertex_effects(vertex_id, result, Some(&inflight))?;
7273 for effect in &effects {
7274 self.apply_effect(effect, None, None)?;
7275 }
7276 applied = applied.saturating_add(1);
7277 }
7278 for (vertex_id, result) in others {
7279 let effects =
7280 self.plan_vertex_effects(vertex_id, result, Some(&inflight))?;
7281 for effect in &effects {
7282 self.apply_effect(effect, None, None)?;
7283 }
7284 applied = applied.saturating_add(1);
7285 }
7286 }
7287 Err(e) => return Err(e),
7288 }
7289 }
7290
7291 Ok(applied)
7292 }
7293
7294 fn evaluate_layer_parallel_with_delta_effects(
7296 &mut self,
7297 layer: &super::scheduler::Layer,
7298 delta: &mut DeltaCollector,
7299 ) -> Result<usize, ExcelError> {
7300 use rayon::prelude::*;
7301
7302 let thread_pool = self.thread_pool.as_ref().unwrap().clone();
7303
7304 let mut phase1: Vec<VertexId> = Vec::new();
7305 let mut phase2: Vec<VertexId> = Vec::new();
7306 for &vid in &layer.vertices {
7307 if self.graph.get_range_dependencies(vid).is_some() {
7308 phase2.push(vid);
7309 } else {
7310 phase1.push(vid);
7311 }
7312 }
7313
7314 let inflight: rustc_hash::FxHashSet<VertexId> = layer.vertices.iter().copied().collect();
7315 let mut applied = 0usize;
7316
7317 for group in [&phase1[..], &phase2[..]] {
7318 if group.is_empty() {
7319 continue;
7320 }
7321 let results: Result<Vec<(VertexId, LiteralValue)>, ExcelError> =
7322 thread_pool.install(|| {
7323 group
7324 .par_iter()
7325 .map(
7326 |&vertex_id| match self.evaluate_vertex_immutable(vertex_id) {
7327 Ok(v) => Ok((vertex_id, v)),
7328 Err(e) => Ok((vertex_id, LiteralValue::Error(e))),
7329 },
7330 )
7331 .collect()
7332 });
7333
7334 match results {
7335 Ok(vertex_results) => {
7336 let mut arrays: Vec<(VertexId, LiteralValue)> = Vec::new();
7337 let mut others: Vec<(VertexId, LiteralValue)> = Vec::new();
7338 for (vertex_id, result) in vertex_results {
7339 if matches!(result, LiteralValue::Array(_)) {
7340 arrays.push((vertex_id, result));
7341 } else {
7342 others.push((vertex_id, result));
7343 }
7344 }
7345 for (vertex_id, result) in arrays {
7346 let effects =
7347 self.plan_vertex_effects(vertex_id, result, Some(&inflight))?;
7348 for effect in &effects {
7349 self.apply_effect(effect, Some(delta), None)?;
7350 }
7351 applied = applied.saturating_add(1);
7352 }
7353 for (vertex_id, result) in others {
7354 let effects =
7355 self.plan_vertex_effects(vertex_id, result, Some(&inflight))?;
7356 for effect in &effects {
7357 self.apply_effect(effect, Some(delta), None)?;
7358 }
7359 applied = applied.saturating_add(1);
7360 }
7361 }
7362 Err(e) => return Err(e),
7363 }
7364 }
7365
7366 Ok(applied)
7367 }
7368
7369 fn evaluate_layer_parallel_cancellable_effects(
7371 &mut self,
7372 layer: &super::scheduler::Layer,
7373 cancel_flag: &AtomicBool,
7374 ) -> Result<usize, ExcelError> {
7375 use rayon::prelude::*;
7376
7377 let thread_pool = self.thread_pool.as_ref().unwrap().clone();
7378
7379 if cancel_flag.load(Ordering::Relaxed) {
7380 return Err(ExcelError::new(ExcelErrorKind::Cancelled)
7381 .with_message("Parallel evaluation cancelled before starting".to_string()));
7382 }
7383
7384 let mut phase1: Vec<VertexId> = Vec::new();
7385 let mut phase2: Vec<VertexId> = Vec::new();
7386 for &vid in &layer.vertices {
7387 if self.graph.get_range_dependencies(vid).is_some() {
7388 phase2.push(vid);
7389 } else {
7390 phase1.push(vid);
7391 }
7392 }
7393
7394 let inflight: rustc_hash::FxHashSet<VertexId> = layer.vertices.iter().copied().collect();
7395 let mut applied = 0usize;
7396
7397 for group in [&phase1[..], &phase2[..]] {
7398 if group.is_empty() {
7399 continue;
7400 }
7401
7402 let results: Result<Vec<(VertexId, LiteralValue)>, ExcelError> =
7403 thread_pool.install(|| {
7404 group
7405 .par_iter()
7406 .map(|&vertex_id| {
7407 if cancel_flag.load(Ordering::Relaxed) {
7408 return Err(ExcelError::new(ExcelErrorKind::Cancelled)
7409 .with_message(
7410 "Parallel evaluation cancelled during execution"
7411 .to_string(),
7412 ));
7413 }
7414 match self.evaluate_vertex_immutable(vertex_id) {
7415 Ok(v) => Ok((vertex_id, v)),
7416 Err(e) => Ok((vertex_id, LiteralValue::Error(e))),
7417 }
7418 })
7419 .collect()
7420 });
7421
7422 match results {
7423 Ok(vertex_results) => {
7424 let mut arrays: Vec<(VertexId, LiteralValue)> = Vec::new();
7425 let mut others: Vec<(VertexId, LiteralValue)> = Vec::new();
7426 for (vertex_id, result) in vertex_results {
7427 if matches!(result, LiteralValue::Array(_)) {
7428 arrays.push((vertex_id, result));
7429 } else {
7430 others.push((vertex_id, result));
7431 }
7432 }
7433 for (vertex_id, result) in arrays {
7434 let effects =
7435 self.plan_vertex_effects(vertex_id, result, Some(&inflight))?;
7436 for effect in &effects {
7437 self.apply_effect(effect, None, None)?;
7438 }
7439 applied = applied.saturating_add(1);
7440 }
7441 for (vertex_id, result) in others {
7442 let effects =
7443 self.plan_vertex_effects(vertex_id, result, Some(&inflight))?;
7444 for effect in &effects {
7445 self.apply_effect(effect, None, None)?;
7446 }
7447 applied = applied.saturating_add(1);
7448 }
7449 }
7450 Err(e) => return Err(e),
7451 }
7452 }
7453
7454 Ok(applied)
7455 }
7456
7457 pub fn evaluate_all_logged(&mut self, log: &mut ChangeLog) -> Result<EvalResult, ExcelError> {
7464 let _source_cache = self.source_cache_session();
7465 self.validate_deterministic_mode()?;
7466 if self.config.defer_graph_building {
7467 self.build_graph_all()?;
7468 }
7469 let start = web_time::Instant::now();
7470 let mut computed_vertices = 0;
7471 let mut cycle_errors = 0;
7472
7473 let to_evaluate = self.graph.get_evaluation_vertices();
7474 if to_evaluate.is_empty() {
7475 return Ok(EvalResult {
7476 computed_vertices,
7477 cycle_errors,
7478 elapsed: start.elapsed(),
7479 });
7480 }
7481
7482 let scheduler = Scheduler::new(&self.graph);
7483 let schedule = scheduler.create_schedule(&to_evaluate)?;
7484
7485 log.begin_compound(format!("evaluate_all(epoch={})", self.recalc_epoch));
7486
7487 let circ_error = LiteralValue::Error(
7489 ExcelError::new(ExcelErrorKind::Circ)
7490 .with_message("Circular dependency detected".to_string()),
7491 );
7492 for cycle in &schedule.cycles {
7493 cycle_errors += 1;
7494 for &vertex_id in cycle {
7495 self.graph
7496 .update_vertex_value(vertex_id, circ_error.clone());
7497 self.mirror_vertex_value_to_overlay(vertex_id, &circ_error);
7498 }
7499 }
7500
7501 for layer in &schedule.layers {
7503 computed_vertices += self.evaluate_layer_logged(layer, log)?;
7504 }
7505
7506 log.end_compound();
7507
7508 self.graph.clear_dirty_flags(&to_evaluate);
7509 self.graph.redirty_volatiles();
7510 self.recalc_epoch = self.recalc_epoch.wrapping_add(1);
7511
7512 Ok(EvalResult {
7513 computed_vertices,
7514 cycle_errors,
7515 elapsed: start.elapsed(),
7516 })
7517 }
7518
7519 fn evaluate_layer_logged(
7521 &mut self,
7522 layer: &super::scheduler::Layer,
7523 log: &mut ChangeLog,
7524 ) -> Result<usize, ExcelError> {
7525 for &vertex_id in &layer.vertices {
7526 let value = match self.evaluate_vertex_immutable(vertex_id) {
7527 Ok(v) => v,
7528 Err(e) => LiteralValue::Error(e),
7529 };
7530 let effects = self.plan_vertex_effects(vertex_id, value, None)?;
7531 for effect in &effects {
7532 self.apply_effect(effect, None, Some(log))?;
7533 }
7534 }
7535 Ok(layer.vertices.len())
7536 }
7537}