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