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