1use crate::SheetId;
2use crate::engine::TombstoneRegistry;
3use crate::engine::named_range::{NameScope, NamedDefinition, NamedRange};
4use crate::engine::sheet_registry::SheetRegistry;
5use crate::formula_plane::authority::FormulaAuthority;
6use formualizer_common::{
7 CoordBuildHasher, ExcelError, ExcelErrorKind, LiteralValue, PackedSheetCell,
8};
9use formualizer_parse::parser::{ASTNode, ASTNodeType, ReferenceType};
10use rustc_hash::{FxHashMap, FxHashSet};
11
12#[cfg(debug_assertions)]
13use std::sync::atomic::{AtomicU64, Ordering};
14
15#[cfg(test)]
16#[derive(Debug, Default, Clone)]
17pub struct GraphInstrumentation {
18 pub edges_added: u64,
19 pub stripe_inserts: u64,
20 pub stripe_removes: u64,
21 pub dependents_scan_fallback_calls: u64,
22 pub dependents_scan_vertices_scanned: u64,
23}
24
25mod ast_utils;
26pub mod editor;
27mod formula_analysis;
28mod names;
29mod range_deps;
30mod sheets;
31pub mod snapshot;
32mod sources;
33mod tables;
34
35use super::arena::{AstNodeId, DataStore, ValueRef};
36use super::delta_edges::CsrMutableEdges;
37use super::ingest_pipeline::{DependencyPlanRow, FormulaAstInput};
38use super::sheet_index::SheetIndex;
39use super::vertex::{VertexId, VertexKind};
40use super::vertex_store::{FIRST_NORMAL_VERTEX, VertexStore};
41use crate::engine::topo::{
42 GraphAdapter,
43 pk::{DynamicTopo, PkConfig},
44};
45use crate::reference::{CellRef, Coord, SharedRangeRef, SharedRef, SharedSheetLocator};
46use formualizer_common::Coord as AbsCoord;
47struct RegistryFunctionProvider;
50
51impl crate::traits::FunctionProvider for RegistryFunctionProvider {
52 fn get_function(
53 &self,
54 ns: &str,
55 name: &str,
56 ) -> Option<std::sync::Arc<dyn crate::function::Function>> {
57 crate::function_registry::get(ns, name)
58 }
59}
60
61#[inline]
62fn normalize_stored_literal(value: LiteralValue) -> LiteralValue {
63 match value {
64 LiteralValue::Int(i) => LiteralValue::Number(i as f64),
66 other => other,
67 }
68}
69
70pub use editor::change_log::{ChangeEvent, ChangeLog};
71
72#[derive(Debug, Clone, PartialEq, Eq, Hash)]
76pub enum DependencyRef {
77 Cell(VertexId),
79 Range {
81 sheet: String,
82 start_row: u32,
83 start_col: u32,
84 end_row: u32, end_col: u32, },
87 WholeColumn { sheet: String, col: u32 },
89 WholeRow { sheet: String, row: u32 },
91}
92
93#[derive(Debug, Clone, Hash, PartialEq, Eq)]
95pub struct StripeKey {
96 pub sheet_id: SheetId,
97 pub stripe_type: StripeType,
98 pub index: u32, }
100
101#[derive(Debug, Clone, Hash, PartialEq, Eq)]
102pub enum StripeType {
103 Row,
104 Column,
105 Block, }
107
108const BLOCK_H: u32 = 256;
110const BLOCK_W: u32 = 256;
111
112pub fn block_index(row: u32, col: u32) -> u32 {
113 (row / BLOCK_H) << 16 | (col / BLOCK_W)
114}
115
116#[derive(Debug, Clone)]
119pub struct OperationSummary {
120 pub affected_vertices: Vec<VertexId>,
122 pub created_placeholders: Vec<CellRef>,
124}
125
126#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
131pub struct GraphBaselineStats {
132 pub graph_vertex_count: usize,
133 pub graph_formula_vertex_count: usize,
134 pub graph_edge_count: usize,
135 pub dirty_vertex_count: usize,
136 pub evaluation_vertex_count: usize,
137 pub formula_ast_root_count: usize,
138 pub formula_ast_node_count: usize,
139}
140
141#[derive(Debug)]
143pub struct DependencyGraph {
144 store: VertexStore,
146
147 edges: CsrMutableEdges,
149
150 data_store: DataStore,
152 vertex_values: FxHashMap<VertexId, ValueRef>,
153 vertex_formulas: FxHashMap<VertexId, AstNodeId>,
154
155 value_cache_enabled: bool,
160
161 #[cfg(debug_assertions)]
164 graph_value_read_attempts: AtomicU64,
165
166 cell_to_vertex: std::collections::HashMap<CellRef, VertexId, CoordBuildHasher>,
170 load_packed_to_vertex: std::collections::HashMap<PackedSheetCell, VertexId, CoordBuildHasher>,
171
172 dirty_vertices: FxHashSet<VertexId>,
174 volatile_vertices: FxHashSet<VertexId>,
175
176 ref_error_vertices: FxHashSet<VertexId>,
182
183 formula_to_range_deps: FxHashMap<VertexId, Vec<SharedRangeRef<'static>>>,
186
187 stripe_to_dependents: FxHashMap<StripeKey, FxHashSet<VertexId>>,
190
191 sheet_indexes: FxHashMap<SheetId, SheetIndex>,
194
195 sheet_reg: SheetRegistry,
197 default_sheet_id: SheetId,
198
199 named_ranges: FxHashMap<String, NamedRange>,
202
203 named_ranges_lookup: FxHashMap<String, String>,
208
209 sheet_named_ranges: FxHashMap<(SheetId, String), NamedRange>,
211
212 sheet_named_ranges_lookup: FxHashMap<(SheetId, String), String>,
217
218 vertex_to_names: FxHashMap<VertexId, Vec<VertexId>>,
220
221 name_vertex_lookup: FxHashMap<VertexId, (NameScope, String)>,
223
224 pending_name_links: FxHashMap<String, FxHashSet<(SheetId, VertexId)>>,
229
230 vertex_to_pending_names: FxHashMap<VertexId, FxHashSet<String>>,
233
234 tables: FxHashMap<String, tables::TableEntry>,
236 tables_lookup: FxHashMap<String, String>,
238 table_vertex_lookup: FxHashMap<VertexId, String>,
239
240 source_scalars: FxHashMap<String, sources::SourceScalarEntry>,
242 source_tables: FxHashMap<String, sources::SourceTableEntry>,
243 source_vertex_lookup: FxHashMap<VertexId, String>,
244
245 name_vertex_seq: u32,
247
248 source_vertex_seq: u32,
250
251 cell_to_name_dependents: FxHashMap<VertexId, FxHashSet<VertexId>>,
253 name_to_cell_dependencies: FxHashMap<VertexId, Vec<VertexId>>,
255
256 config: super::EvalConfig,
258
259 formula_authority: FormulaAuthority,
261
262 pk_order: Option<DynamicTopo<VertexId>>,
264
265 spill_anchor_to_cells: FxHashMap<VertexId, Vec<CellRef>>,
269 spill_cell_to_anchor: std::collections::HashMap<CellRef, VertexId, CoordBuildHasher>,
270
271 first_load_assume_new: bool,
273 ensure_touched_sheets: FxHashSet<SheetId>,
274
275 pub tombstone_registry: TombstoneRegistry,
277
278 #[cfg(test)]
279 instr: std::sync::Mutex<GraphInstrumentation>,
280}
281
282impl Default for DependencyGraph {
283 fn default() -> Self {
284 Self::new()
285 }
286}
287
288impl DependencyGraph {
289 pub fn range_expansion_limit(&self) -> usize {
291 self.config.range_expansion_limit
292 }
293
294 pub fn get_config(&self) -> &super::EvalConfig {
295 &self.config
296 }
297
298 pub(crate) fn formula_authority(&self) -> &FormulaAuthority {
299 &self.formula_authority
300 }
301
302 pub(crate) fn formula_authority_mut(&mut self) -> &mut FormulaAuthority {
303 &mut self.formula_authority
304 }
305
306 pub fn baseline_stats(&self) -> GraphBaselineStats {
308 let data_stats = self.data_store.memory_usage();
309 GraphBaselineStats {
310 graph_vertex_count: self.store.len(),
311 graph_formula_vertex_count: self.vertex_formulas.len(),
312 graph_edge_count: self.edges.num_edges_exact(),
313 dirty_vertex_count: self.dirty_vertices.len(),
314 evaluation_vertex_count: self.get_evaluation_vertices().len(),
315 formula_ast_root_count: self.vertex_formulas.len(),
316 formula_ast_node_count: data_stats.total_ast_nodes,
317 }
318 }
319
320 #[inline]
321 pub(crate) fn value_cache_enabled(&self) -> bool {
322 self.value_cache_enabled
323 }
324
325 #[cfg(test)]
329 pub fn debug_graph_value_read_attempts(&self) -> u64 {
330 #[cfg(debug_assertions)]
331 {
332 self.graph_value_read_attempts.load(Ordering::Relaxed)
333 }
334 #[cfg(not(debug_assertions))]
335 {
336 0
337 }
338 }
339
340 pub fn plan_dependencies<'a, I>(
342 &mut self,
343 items: I,
344 policy: &formualizer_parse::parser::CollectPolicy,
345 volatile: Option<&[bool]>,
346 ) -> Result<crate::engine::plan::DependencyPlan, formualizer_common::ExcelError>
347 where
348 I: IntoIterator<Item = (&'a str, u32, u32, &'a formualizer_parse::parser::ASTNode)>,
349 {
350 crate::engine::plan::build_dependency_plan(
351 &mut self.sheet_reg,
352 items.into_iter(),
353 policy,
354 volatile,
355 )
356 }
357
358 pub fn plan_dependencies_mixed<'a, I>(
359 &mut self,
360 items: I,
361 policy: &formualizer_parse::parser::CollectPolicy,
362 volatile: Option<&[bool]>,
363 ) -> Result<crate::engine::plan::DependencyPlan, formualizer_common::ExcelError>
364 where
365 I: IntoIterator<
366 Item = (
367 &'a str,
368 u32,
369 u32,
370 crate::engine::plan::DependencyPlanAst<'a>,
371 ),
372 >,
373 {
374 crate::engine::plan::build_dependency_plan_mixed(
375 &mut self.sheet_reg,
376 &self.data_store,
377 items.into_iter(),
378 policy,
379 volatile,
380 )
381 }
382
383 pub fn ensure_vertices_batch(
386 &mut self,
387 coords: &[(SheetId, AbsCoord)],
388 ) -> Vec<(AbsCoord, u32)> {
389 self.ensure_vertices_batch_ordered(coords).1
390 }
391
392 pub fn ensure_vertices_batch_packed_ordered(
396 &mut self,
397 packed_cells: &[PackedSheetCell],
398 ) -> (Vec<VertexId>, Vec<(AbsCoord, u32)>) {
399 #[cfg(feature = "perf_instrumentation")]
400 use crate::instant::FzInstant as PerfInstant;
401 use rustc_hash::FxHashMap;
402
403 #[cfg(feature = "perf_instrumentation")]
404 let debug = std::env::var("FZ_DEBUG_LOAD")
405 .ok()
406 .is_some_and(|v| v != "0");
407 #[cfg(feature = "perf_instrumentation")]
408 let t0 = PerfInstant::now();
409
410 let mut ordered: Vec<Option<VertexId>> = vec![None; packed_cells.len()];
411 if packed_cells.is_empty() {
412 return (Vec::new(), Vec::new());
413 }
414
415 let first_sid = packed_cells[0].sheet_id();
416 let single_sheet = packed_cells.iter().all(|cell| cell.sheet_id() == first_sid);
417 let mut add_batch: Vec<(AbsCoord, u32)> = Vec::new();
418
419 #[cfg(feature = "perf_instrumentation")]
420 let mut packed_hits = 0usize;
421 #[cfg(feature = "perf_instrumentation")]
422 let mut generic_hits = 0usize;
423 #[cfg(feature = "perf_instrumentation")]
424 let mut missing = 0usize;
425 #[cfg(feature = "perf_instrumentation")]
426 let mut t_packed_lookup_us = 0u128;
427 #[cfg(feature = "perf_instrumentation")]
428 let mut t_generic_lookup_us = 0u128;
429 #[cfg(feature = "perf_instrumentation")]
430 let mut t_alloc_us = 0u128;
431 #[cfg(feature = "perf_instrumentation")]
432 let mut t_map_insert_us = 0u128;
433 #[cfg(feature = "perf_instrumentation")]
434 let mut t_index_insert_us = 0u128;
435 #[cfg(feature = "perf_instrumentation")]
436 let mut t_edge_register_us = 0u128;
437
438 if single_sheet {
439 let sid = first_sid;
440 let mut missing_items: Vec<(usize, PackedSheetCell)> =
441 Vec::with_capacity(packed_cells.len());
442
443 for (idx, packed) in packed_cells.iter().copied().enumerate() {
444 #[cfg(feature = "perf_instrumentation")]
445 let tl0 = PerfInstant::now();
446 if self.first_load_assume_new
447 && let Some(&existing) = self.load_packed_to_vertex.get(&packed)
448 {
449 ordered[idx] = Some(existing);
450 #[cfg(feature = "perf_instrumentation")]
451 {
452 packed_hits += 1;
453 t_packed_lookup_us += tl0.elapsed().as_micros();
454 }
455 continue;
456 }
457 #[cfg(feature = "perf_instrumentation")]
458 {
459 t_packed_lookup_us += tl0.elapsed().as_micros();
460 }
461
462 let pc = AbsCoord::new(packed.row0(), packed.col0());
463 let addr = CellRef::new(sid, Coord::new(pc.row(), pc.col(), true, true));
464 #[cfg(feature = "perf_instrumentation")]
465 let tg0 = PerfInstant::now();
466 if let Some(&existing) = self.cell_to_vertex.get(&addr) {
467 ordered[idx] = Some(existing);
468 if self.first_load_assume_new {
469 self.load_packed_to_vertex.insert(packed, existing);
470 }
471 #[cfg(feature = "perf_instrumentation")]
472 {
473 generic_hits += 1;
474 }
475 } else {
476 missing_items.push((idx, packed));
477 #[cfg(feature = "perf_instrumentation")]
478 {
479 missing += 1;
480 }
481 }
482 #[cfg(feature = "perf_instrumentation")]
483 {
484 t_generic_lookup_us += tg0.elapsed().as_micros();
485 }
486 }
487
488 if !missing_items.is_empty() {
489 self.ensure_touched_sheets.insert(sid);
490
491 let mut pcs: Vec<AbsCoord> = Vec::with_capacity(missing_items.len());
492 for (_, packed) in &missing_items {
493 pcs.push(AbsCoord::new(packed.row0(), packed.col0()));
494 }
495
496 #[cfg(feature = "perf_instrumentation")]
497 let ta0 = PerfInstant::now();
498 let vids = self.store.allocate_contiguous(sid, &pcs, 0x00);
499 #[cfg(feature = "perf_instrumentation")]
500 {
501 t_alloc_us += ta0.elapsed().as_micros();
502 }
503 add_batch.reserve(missing_items.len());
504
505 match self.config.sheet_index_mode {
506 crate::engine::SheetIndexMode::Eager
507 | crate::engine::SheetIndexMode::FastBatch => {
508 for ((input_idx, packed), vid) in
509 missing_items.into_iter().zip(vids.into_iter())
510 {
511 let pc = AbsCoord::new(packed.row0(), packed.col0());
512 ordered[input_idx] = Some(vid);
513 add_batch.push((pc, vid.0));
514
515 #[cfg(feature = "perf_instrumentation")]
516 let tm0 = PerfInstant::now();
517 if self.first_load_assume_new {
518 self.load_packed_to_vertex.insert(packed, vid);
519 } else {
520 let addr =
521 CellRef::new(sid, Coord::new(pc.row(), pc.col(), true, true));
522 self.cell_to_vertex.insert(addr, vid);
523 }
524 #[cfg(feature = "perf_instrumentation")]
525 {
526 t_map_insert_us += tm0.elapsed().as_micros();
527 }
528
529 #[cfg(feature = "perf_instrumentation")]
530 let ti0 = PerfInstant::now();
531 self.sheet_index_mut(sid).add_vertex(pc, vid);
532 #[cfg(feature = "perf_instrumentation")]
533 {
534 t_index_insert_us += ti0.elapsed().as_micros();
535 }
536 }
537 }
538 crate::engine::SheetIndexMode::Lazy => {
539 for ((input_idx, packed), vid) in
540 missing_items.into_iter().zip(vids.into_iter())
541 {
542 let pc = AbsCoord::new(packed.row0(), packed.col0());
543 ordered[input_idx] = Some(vid);
544 add_batch.push((pc, vid.0));
545
546 #[cfg(feature = "perf_instrumentation")]
547 let tm0 = PerfInstant::now();
548 if self.first_load_assume_new {
549 self.load_packed_to_vertex.insert(packed, vid);
550 } else {
551 let addr =
552 CellRef::new(sid, Coord::new(pc.row(), pc.col(), true, true));
553 self.cell_to_vertex.insert(addr, vid);
554 }
555 #[cfg(feature = "perf_instrumentation")]
556 {
557 t_map_insert_us += tm0.elapsed().as_micros();
558 }
559 }
560 }
561 }
562 }
563 } else {
564 let mut grouped: FxHashMap<SheetId, Vec<(usize, PackedSheetCell)>> =
565 FxHashMap::default();
566
567 for (idx, packed) in packed_cells.iter().copied().enumerate() {
568 #[cfg(feature = "perf_instrumentation")]
569 let tl0 = PerfInstant::now();
570 if self.first_load_assume_new
571 && let Some(&existing) = self.load_packed_to_vertex.get(&packed)
572 {
573 ordered[idx] = Some(existing);
574 #[cfg(feature = "perf_instrumentation")]
575 {
576 packed_hits += 1;
577 t_packed_lookup_us += tl0.elapsed().as_micros();
578 }
579 continue;
580 }
581 #[cfg(feature = "perf_instrumentation")]
582 {
583 t_packed_lookup_us += tl0.elapsed().as_micros();
584 }
585
586 let sid = packed.sheet_id();
587 let pc = AbsCoord::new(packed.row0(), packed.col0());
588 let addr = CellRef::new(sid, Coord::new(pc.row(), pc.col(), true, true));
589 #[cfg(feature = "perf_instrumentation")]
590 let tg0 = PerfInstant::now();
591 if let Some(&existing) = self.cell_to_vertex.get(&addr) {
592 ordered[idx] = Some(existing);
593 if self.first_load_assume_new {
594 self.load_packed_to_vertex.insert(packed, existing);
595 }
596 #[cfg(feature = "perf_instrumentation")]
597 {
598 generic_hits += 1;
599 }
600 } else {
601 grouped.entry(sid).or_default().push((idx, packed));
602 #[cfg(feature = "perf_instrumentation")]
603 {
604 missing += 1;
605 }
606 }
607 #[cfg(feature = "perf_instrumentation")]
608 {
609 t_generic_lookup_us += tg0.elapsed().as_micros();
610 }
611 }
612
613 for (sid, items) in grouped {
614 if items.is_empty() {
615 continue;
616 }
617 self.ensure_touched_sheets.insert(sid);
618
619 let mut pcs: Vec<AbsCoord> = Vec::with_capacity(items.len());
620 for (_, packed) in &items {
621 pcs.push(AbsCoord::new(packed.row0(), packed.col0()));
622 }
623
624 #[cfg(feature = "perf_instrumentation")]
625 let ta0 = PerfInstant::now();
626 let vids = self.store.allocate_contiguous(sid, &pcs, 0x00);
627 #[cfg(feature = "perf_instrumentation")]
628 {
629 t_alloc_us += ta0.elapsed().as_micros();
630 }
631
632 for ((input_idx, packed), vid) in items.into_iter().zip(vids.into_iter()) {
633 let pc = AbsCoord::new(packed.row0(), packed.col0());
634 ordered[input_idx] = Some(vid);
635 add_batch.push((pc, vid.0));
636
637 #[cfg(feature = "perf_instrumentation")]
638 let tm0 = PerfInstant::now();
639 if self.first_load_assume_new {
640 self.load_packed_to_vertex.insert(packed, vid);
641 } else {
642 let addr = CellRef::new(sid, Coord::new(pc.row(), pc.col(), true, true));
643 self.cell_to_vertex.insert(addr, vid);
644 }
645 #[cfg(feature = "perf_instrumentation")]
646 {
647 t_map_insert_us += tm0.elapsed().as_micros();
648 }
649
650 match self.config.sheet_index_mode {
651 crate::engine::SheetIndexMode::Eager
652 | crate::engine::SheetIndexMode::FastBatch => {
653 #[cfg(feature = "perf_instrumentation")]
654 let ti0 = PerfInstant::now();
655 self.sheet_index_mut(sid).add_vertex(pc, vid);
656 #[cfg(feature = "perf_instrumentation")]
657 {
658 t_index_insert_us += ti0.elapsed().as_micros();
659 }
660 }
661 crate::engine::SheetIndexMode::Lazy => {
662 }
664 }
665 }
666 }
667 }
668
669 if !add_batch.is_empty() {
670 #[cfg(feature = "perf_instrumentation")]
671 let te0 = PerfInstant::now();
672 self.edges.add_vertices_batch(&add_batch);
673 #[cfg(feature = "perf_instrumentation")]
674 {
675 t_edge_register_us += te0.elapsed().as_micros();
676 }
677 }
678
679 #[cfg(feature = "perf_instrumentation")]
680 if debug {
681 eprintln!(
682 "[fz][ensure] cells={} single_sheet={} packed_hits={} generic_hits={} missing={} packed_lookup={}us generic_lookup={}us alloc={}us map_insert={}us index_insert={}us edge_register={}us total={}ms",
683 packed_cells.len(),
684 single_sheet,
685 packed_hits,
686 generic_hits,
687 missing,
688 t_packed_lookup_us,
689 t_generic_lookup_us,
690 t_alloc_us,
691 t_map_insert_us,
692 t_index_insert_us,
693 t_edge_register_us,
694 t0.elapsed().as_millis(),
695 );
696 }
697
698 let ordered = ordered
699 .into_iter()
700 .map(|vid| vid.expect("ensure_vertices_batch_packed_ordered must resolve every coord"))
701 .collect();
702 (ordered, add_batch)
703 }
704
705 pub fn ensure_vertices_batch_ordered(
708 &mut self,
709 coords: &[(SheetId, AbsCoord)],
710 ) -> (Vec<VertexId>, Vec<(AbsCoord, u32)>) {
711 let mut packed: Vec<PackedSheetCell> = Vec::with_capacity(coords.len());
712 for &(sid, coord) in coords {
713 packed.push(Self::packed_cell_key(sid, coord));
714 }
715 self.ensure_vertices_batch_packed_ordered(&packed)
716 }
717
718 #[inline]
719 fn packed_cell_key(sheet_id: SheetId, coord: AbsCoord) -> PackedSheetCell {
720 PackedSheetCell::try_new(sheet_id, coord.row(), coord.col())
721 .expect("graph coordinate must fit PackedSheetCell")
722 }
723
724 fn flush_load_packed_mappings(&mut self) {
725 if self.load_packed_to_vertex.is_empty() {
726 return;
727 }
728 let debug = std::env::var("FZ_DEBUG_LOAD")
729 .ok()
730 .is_some_and(|v| v != "0");
731 let t0 = crate::instant::FzInstant::now();
732 let count = self.load_packed_to_vertex.len();
733 self.cell_to_vertex.reserve(count);
734 for (&packed, &vid) in &self.load_packed_to_vertex {
735 let coord = AbsCoord::new(packed.row0(), packed.col0());
736 let addr = CellRef::new(
737 packed.sheet_id(),
738 Coord::new(coord.row(), coord.col(), true, true),
739 );
740 self.cell_to_vertex.insert(addr, vid);
741 }
742 self.load_packed_to_vertex.clear();
743 if debug {
744 eprintln!(
745 "[fz][load] flush_load_packed_mappings: {} entries in {:.1} ms",
746 count,
747 t0.elapsed().as_secs_f64() * 1000.0,
748 );
749 }
750 }
751
752 pub fn set_first_load_assume_new(&mut self, enabled: bool) {
754 if self.first_load_assume_new && !enabled {
755 self.flush_load_packed_mappings();
756 } else if enabled {
757 self.load_packed_to_vertex.clear();
758 }
759 self.first_load_assume_new = enabled;
760 }
761
762 pub fn first_load_assume_new(&self) -> bool {
763 self.first_load_assume_new
764 }
765
766 pub fn reset_ensure_touched(&mut self) {
768 self.ensure_touched_sheets.clear();
769 }
770
771 pub fn store_ast(&mut self, ast: &formualizer_parse::parser::ASTNode) -> AstNodeId {
773 self.data_store.store_ast(ast, &self.sheet_reg)
774 }
775
776 pub fn store_asts_batch<'a, I>(&mut self, asts: I) -> Vec<AstNodeId>
778 where
779 I: IntoIterator<Item = &'a formualizer_parse::parser::ASTNode>,
780 {
781 self.data_store.store_asts_batch(asts, &self.sheet_reg)
782 }
783
784 pub fn reserve_formula_metadata(&mut self, additional: usize) {
786 self.vertex_formulas.reserve(additional);
787 self.dirty_vertices.reserve(additional);
788 self.volatile_vertices.reserve(additional);
789 }
790
791 pub fn vid_for_sid_pc(&self, sid: SheetId, pc: AbsCoord) -> Option<VertexId> {
793 let addr = CellRef::new(sid, Coord::new(pc.row(), pc.col(), true, true));
794 self.cell_to_vertex.get(&addr).copied()
795 }
796
797 pub fn vid_for_plan_idx(
799 &self,
800 plan: &crate::engine::plan::DependencyPlan,
801 idx: u32,
802 ) -> Option<VertexId> {
803 let (sid, pc) = plan.global_cells.get(idx as usize).copied()?;
804 self.vid_for_sid_pc(sid, pc)
805 }
806 pub fn assign_formula_vertex(
808 &mut self,
809 vid: VertexId,
810 ast_id: AstNodeId,
811 volatile: bool,
812 dynamic: bool,
813 ) {
814 if self.vertex_formulas.contains_key(&vid) {
815 self.remove_dependent_edges(vid);
816 }
817 self.store
818 .set_kind(vid, crate::engine::vertex::VertexKind::FormulaScalar);
819 self.vertex_values.remove(&vid);
820 self.vertex_formulas.insert(vid, ast_id);
821 self.mark_volatile(vid, volatile);
822 self.store.set_dynamic(vid, dynamic);
823
824 self.mark_vertex_dirty(vid);
826 }
827
828 pub fn assign_formula_vertex_load_fast(
831 &mut self,
832 vid: VertexId,
833 ast_id: AstNodeId,
834 volatile: bool,
835 dynamic: bool,
836 ) {
837 debug_assert!(
838 !self.vertex_formulas.contains_key(&vid),
839 "load-fast formula assignment expects fresh/non-formula vertices"
840 );
841 self.store
842 .set_kind(vid, crate::engine::vertex::VertexKind::FormulaScalar);
843 self.vertex_values.remove(&vid);
844 self.vertex_formulas.insert(vid, ast_id);
845 self.mark_volatile(vid, volatile);
846 self.store.set_dynamic(vid, dynamic);
847 }
848
849 pub fn add_edges_nobatch(&mut self, dependent: VertexId, dependencies: &[VertexId]) {
851 self.add_dependent_edges_nobatch(dependent, dependencies);
852 }
853
854 pub fn iter_vertex_ids(&self) -> impl Iterator<Item = VertexId> + '_ {
856 self.store.all_vertices()
857 }
858
859 pub fn vertex_coord(&self, vid: VertexId) -> AbsCoord {
861 self.store.coord(vid)
862 }
863
864 pub fn vertex_count(&self) -> usize {
866 self.store.len()
867 }
868
869 pub fn build_edges_from_adjacency(
871 &mut self,
872 adjacency: Vec<(u32, Vec<u32>)>,
873 coords: Vec<AbsCoord>,
874 vertex_ids: Vec<u32>,
875 ) {
876 let adjacency = self.edges.adjacency_with_carried_forward_edges(adjacency);
880 self.edges
881 .build_from_adjacency(adjacency, coords, vertex_ids);
882 }
883 pub fn used_row_bounds_for_columns(
885 &self,
886 sheet_id: SheetId,
887 start_col: u32,
888 end_col: u32,
889 ) -> Option<(u32, u32)> {
890 if let Some(index) = self.sheet_indexes.get(&sheet_id)
892 && !index.is_empty()
893 {
894 let mut min_r: Option<u32> = None;
895 let mut max_r: Option<u32> = None;
896 for vid in index.vertices_in_col_range(start_col, end_col) {
897 let r = self.store.coord(vid).row();
898 min_r = Some(min_r.map(|m| m.min(r)).unwrap_or(r));
899 max_r = Some(max_r.map(|m| m.max(r)).unwrap_or(r));
900 }
901 return match (min_r, max_r) {
902 (Some(a), Some(b)) => Some((a, b)),
903 _ => None,
904 };
905 }
906 let mut min_r: Option<u32> = None;
908 let mut max_r: Option<u32> = None;
909 for cref in self.cell_to_vertex.keys() {
910 if cref.sheet_id == sheet_id {
911 let c = cref.coord.col();
912 if c >= start_col && c <= end_col {
913 let r = cref.coord.row();
914 min_r = Some(min_r.map(|m| m.min(r)).unwrap_or(r));
915 max_r = Some(max_r.map(|m| m.max(r)).unwrap_or(r));
916 }
917 }
918 }
919 for packed in self.load_packed_to_vertex.keys() {
920 if packed.sheet_id() == sheet_id {
921 let c = packed.col0();
922 if c >= start_col && c <= end_col {
923 let r = packed.row0();
924 min_r = Some(min_r.map(|m| m.min(r)).unwrap_or(r));
925 max_r = Some(max_r.map(|m| m.max(r)).unwrap_or(r));
926 }
927 }
928 }
929 match (min_r, max_r) {
930 (Some(a), Some(b)) => Some((a, b)),
931 _ => None,
932 }
933 }
934
935 pub fn finalize_sheet_index(&mut self, sheet: &str) {
937 let Some(sheet_id) = self.sheet_reg.get_id(sheet) else {
938 return;
939 };
940 if let Some(idx) = self.sheet_indexes.get(&sheet_id)
942 && !idx.is_empty()
943 {
944 return;
945 }
946 let mut idx = SheetIndex::new();
947 let mut batch: Vec<(AbsCoord, VertexId)> =
949 Vec::with_capacity(self.cell_to_vertex.len() + self.load_packed_to_vertex.len());
950 for (cref, vid) in &self.cell_to_vertex {
951 if cref.sheet_id == sheet_id {
952 batch.push((AbsCoord::new(cref.coord.row(), cref.coord.col()), *vid));
953 }
954 }
955 for (&packed, &vid) in &self.load_packed_to_vertex {
956 if packed.sheet_id() != sheet_id {
957 continue;
958 }
959 let coord = AbsCoord::new(packed.row0(), packed.col0());
960 let addr = CellRef::new(sheet_id, Coord::new(coord.row(), coord.col(), true, true));
961 if self.cell_to_vertex.contains_key(&addr) {
962 continue;
963 }
964 batch.push((coord, vid));
965 }
966 idx.add_vertices_batch(&batch);
968 self.sheet_indexes.insert(sheet_id, idx);
969 }
970
971 pub fn set_sheet_index_mode(&mut self, mode: crate::engine::SheetIndexMode) {
972 self.config.sheet_index_mode = mode;
973 }
974
975 pub fn used_col_bounds_for_rows(
977 &self,
978 sheet_id: SheetId,
979 start_row: u32,
980 end_row: u32,
981 ) -> Option<(u32, u32)> {
982 if let Some(index) = self.sheet_indexes.get(&sheet_id)
983 && !index.is_empty()
984 {
985 let mut min_c: Option<u32> = None;
986 let mut max_c: Option<u32> = None;
987 for vid in index.vertices_in_row_range(start_row, end_row) {
988 let c = self.store.coord(vid).col();
989 min_c = Some(min_c.map(|m| m.min(c)).unwrap_or(c));
990 max_c = Some(max_c.map(|m| m.max(c)).unwrap_or(c));
991 }
992 return match (min_c, max_c) {
993 (Some(a), Some(b)) => Some((a, b)),
994 _ => None,
995 };
996 }
997 let mut min_c: Option<u32> = None;
999 let mut max_c: Option<u32> = None;
1000 for cref in self.cell_to_vertex.keys() {
1001 if cref.sheet_id == sheet_id {
1002 let r = cref.coord.row();
1003 if r >= start_row && r <= end_row {
1004 let c = cref.coord.col();
1005 min_c = Some(min_c.map(|m| m.min(c)).unwrap_or(c));
1006 max_c = Some(max_c.map(|m| m.max(c)).unwrap_or(c));
1007 }
1008 }
1009 }
1010 for packed in self.load_packed_to_vertex.keys() {
1011 if packed.sheet_id() == sheet_id {
1012 let r = packed.row0();
1013 if r >= start_row && r <= end_row {
1014 let c = packed.col0();
1015 min_c = Some(min_c.map(|m| m.min(c)).unwrap_or(c));
1016 max_c = Some(max_c.map(|m| m.max(c)).unwrap_or(c));
1017 }
1018 }
1019 }
1020 match (min_c, max_c) {
1021 (Some(a), Some(b)) => Some((a, b)),
1022 _ => None,
1023 }
1024 }
1025
1026 pub fn sheet_has_formulas(&self, sheet_id: SheetId) -> bool {
1028 for &vid in self.vertex_formulas.keys() {
1030 if self.store.sheet_id(vid) == sheet_id {
1031 return true;
1032 }
1033 }
1034 false
1035 }
1036 pub fn new() -> Self {
1037 Self::new_with_config(super::EvalConfig::default())
1038 }
1039
1040 pub fn new_with_config(config: super::EvalConfig) -> Self {
1041 let mut sheet_reg = SheetRegistry::new();
1042 let default_sheet_id = sheet_reg.id_for(&config.default_sheet_name);
1043
1044 let mut g = Self {
1045 store: VertexStore::new(),
1046 edges: CsrMutableEdges::new(),
1047 data_store: DataStore::new(),
1048 vertex_values: FxHashMap::default(),
1049 vertex_formulas: FxHashMap::default(),
1050 value_cache_enabled: false,
1053 #[cfg(debug_assertions)]
1054 graph_value_read_attempts: AtomicU64::new(0),
1055 cell_to_vertex: std::collections::HashMap::with_hasher(CoordBuildHasher),
1056 load_packed_to_vertex: std::collections::HashMap::with_hasher(CoordBuildHasher),
1057 dirty_vertices: FxHashSet::default(),
1058 volatile_vertices: FxHashSet::default(),
1059 ref_error_vertices: FxHashSet::default(),
1060 formula_to_range_deps: FxHashMap::default(),
1061 stripe_to_dependents: FxHashMap::default(),
1062 sheet_indexes: FxHashMap::default(),
1063 sheet_reg,
1064 default_sheet_id,
1065 named_ranges: FxHashMap::default(),
1066 named_ranges_lookup: FxHashMap::default(),
1067 sheet_named_ranges: FxHashMap::default(),
1068 sheet_named_ranges_lookup: FxHashMap::default(),
1069 vertex_to_names: FxHashMap::default(),
1070 name_vertex_lookup: FxHashMap::default(),
1071 pending_name_links: FxHashMap::default(),
1072 vertex_to_pending_names: FxHashMap::default(),
1073 tables: FxHashMap::default(),
1074 tables_lookup: FxHashMap::default(),
1075 table_vertex_lookup: FxHashMap::default(),
1076 source_scalars: FxHashMap::default(),
1077 source_tables: FxHashMap::default(),
1078 source_vertex_lookup: FxHashMap::default(),
1079 name_vertex_seq: 0,
1080 source_vertex_seq: 0,
1081 cell_to_name_dependents: FxHashMap::default(),
1082 name_to_cell_dependencies: FxHashMap::default(),
1083 config: config.clone(),
1084 formula_authority: FormulaAuthority::default(),
1085 pk_order: None,
1086 spill_anchor_to_cells: FxHashMap::default(),
1087 spill_cell_to_anchor: std::collections::HashMap::with_hasher(CoordBuildHasher),
1088 first_load_assume_new: false,
1089 ensure_touched_sheets: FxHashSet::default(),
1090 tombstone_registry: TombstoneRegistry::default(),
1091 #[cfg(test)]
1092 instr: std::sync::Mutex::new(GraphInstrumentation::default()),
1093 };
1094
1095 if config.use_dynamic_topo {
1096 let nodes = g
1098 .store
1099 .all_vertices()
1100 .filter(|&id| g.store.vertex_exists_active(id));
1101 let mut pk = DynamicTopo::new(
1102 nodes,
1103 PkConfig {
1104 visit_budget: config.pk_visit_budget,
1105 compaction_interval_ops: config.pk_compaction_interval_ops,
1106 },
1107 );
1108 let adapter = GraphAdapter { g: &g };
1110 pk.rebuild_full(&adapter);
1111 g.pk_order = Some(pk);
1112 }
1113
1114 g
1115 }
1116
1117 pub(crate) fn pk_layers_for(&self, subset: &[VertexId]) -> Option<Vec<crate::engine::Layer>> {
1119 let pk = self.pk_order.as_ref()?;
1120 let adapter = crate::engine::topo::GraphAdapter { g: self };
1121 let layers = pk.layers_for(&adapter, subset, self.config.max_layer_width);
1122 Some(
1123 layers
1124 .into_iter()
1125 .map(|vs| crate::engine::Layer { vertices: vs })
1126 .collect(),
1127 )
1128 }
1129
1130 #[inline]
1131 pub(crate) fn dynamic_topo_enabled(&self) -> bool {
1132 self.pk_order.is_some()
1133 }
1134
1135 #[cfg(test)]
1136 pub fn reset_instr(&mut self) {
1137 if let Ok(mut g) = self.instr.lock() {
1138 *g = GraphInstrumentation::default();
1139 }
1140 }
1141
1142 #[cfg(test)]
1143 pub fn instr(&self) -> GraphInstrumentation {
1144 self.instr.lock().map(|g| g.clone()).unwrap_or_default()
1145 }
1146
1147 pub fn begin_batch(&mut self) {
1149 self.edges.begin_batch();
1150 }
1151
1152 pub fn end_batch(&mut self) {
1154 self.edges.end_batch();
1155 }
1156
1157 pub fn default_sheet_id(&self) -> SheetId {
1158 self.default_sheet_id
1159 }
1160
1161 pub fn default_sheet_name(&self) -> &str {
1162 self.sheet_reg.name(self.default_sheet_id)
1163 }
1164
1165 pub fn set_default_sheet_by_name(&mut self, name: &str) {
1166 self.default_sheet_id = self.sheet_id_mut(name);
1167 }
1168
1169 pub fn set_default_sheet_by_id(&mut self, id: SheetId) {
1170 self.default_sheet_id = id;
1171 }
1172
1173 pub fn sheet_id_mut(&mut self, name: &str) -> SheetId {
1175 self.sheet_reg.id_for(name)
1176 }
1177
1178 pub fn sheet_id(&self, name: &str) -> Option<SheetId> {
1179 self.sheet_reg.get_id(name)
1180 }
1181
1182 fn resolve_existing_sheet_id(&self, name: &str) -> Result<SheetId, ExcelError> {
1184 self.sheet_id(name).ok_or_else(|| {
1185 ExcelError::new(ExcelErrorKind::Ref).with_message(format!("Sheet not found: {name}"))
1186 })
1187 }
1188
1189 pub fn sheet_name(&self, id: SheetId) -> &str {
1191 self.sheet_reg.name(id)
1192 }
1193
1194 pub fn sheet_reg(&self) -> &SheetRegistry {
1196 &self.sheet_reg
1197 }
1198
1199 pub(crate) fn data_store(&self) -> &DataStore {
1200 &self.data_store
1201 }
1202
1203 pub(crate) fn make_ingest_pipeline<'a>(
1204 &'a mut self,
1205 function_provider: &'a dyn crate::traits::FunctionProvider,
1206 policy: formualizer_parse::parser::CollectPolicy,
1207 ) -> crate::engine::ingest_pipeline::IngestPipeline<'a> {
1208 use crate::engine::ingest_pipeline::{
1209 NameRegistryView, NamedEntryRef, SourceEntryRef, SourceRegistryView,
1210 TableEntrySnapshot, TableRegistryView,
1211 };
1212
1213 let DependencyGraph {
1214 data_store,
1215 sheet_reg,
1216 named_ranges,
1217 named_ranges_lookup,
1218 sheet_named_ranges,
1219 sheet_named_ranges_lookup,
1220 tables,
1221 tables_lookup,
1222 source_scalars,
1223 source_tables,
1224 config,
1225 ..
1226 } = self;
1227
1228 let case_sensitive_names = config.case_sensitive_names;
1229 let names = NameRegistryView::new(move |name, current_sheet| {
1230 let found = if case_sensitive_names {
1231 sheet_named_ranges
1232 .get(&(current_sheet, name.to_string()))
1233 .or_else(|| named_ranges.get(name))
1234 } else {
1235 let key = name.to_lowercase();
1236 sheet_named_ranges_lookup
1237 .get(&(current_sheet, key.clone()))
1238 .and_then(|canon| sheet_named_ranges.get(&(current_sheet, canon.clone())))
1239 .or_else(|| {
1240 named_ranges_lookup
1241 .get(&key)
1242 .and_then(|canon| named_ranges.get(canon))
1243 })
1244 };
1245 found.map(|entry| NamedEntryRef {
1246 vertex: entry.vertex,
1247 })
1248 });
1249
1250 let case_sensitive_tables = config.case_sensitive_tables;
1251 let tables_ref = &*tables;
1252 let tables_lookup_ref = &*tables_lookup;
1253 let snapshot_table = |entry: &tables::TableEntry| TableEntrySnapshot {
1254 name: entry.name.clone(),
1255 range: entry.range,
1256 header_row: entry.header_row,
1257 headers: entry.headers.clone(),
1258 vertex: entry.vertex,
1259 };
1260 let tables_view = TableRegistryView::new(
1261 move |name| {
1262 if case_sensitive_tables {
1263 tables_ref.get(name).map(snapshot_table)
1264 } else {
1265 let key = name.to_lowercase();
1266 tables_lookup_ref
1267 .get(&key)
1268 .and_then(|canon| tables_ref.get(canon))
1269 .map(snapshot_table)
1270 }
1271 },
1272 move |cell| {
1273 let row0 = cell.coord.row();
1274 let col0 = cell.coord.col();
1275 let mut best: Option<&tables::TableEntry> = None;
1276 let mut best_area = u64::MAX;
1277 let mut best_name = "";
1278 for table in tables_ref.values() {
1279 if table.sheet_id() != cell.sheet_id {
1280 continue;
1281 }
1282 let sr0 = table.range.start.coord.row();
1283 let sc0 = table.range.start.coord.col();
1284 let er0 = table.range.end.coord.row();
1285 let ec0 = table.range.end.coord.col();
1286 if row0 < sr0 || row0 > er0 || col0 < sc0 || col0 > ec0 {
1287 continue;
1288 }
1289 let area = ((er0 - sr0 + 1) as u64).saturating_mul((ec0 - sc0 + 1) as u64);
1290 let name = table.name.as_str();
1291 if best.is_none() || area < best_area || (area == best_area && name < best_name)
1292 {
1293 best = Some(table);
1294 best_area = area;
1295 best_name = name;
1296 }
1297 }
1298 best.map(snapshot_table)
1299 },
1300 );
1301
1302 let sources = SourceRegistryView::new(
1303 move |name| {
1304 source_scalars.get(name).map(|entry| SourceEntryRef {
1305 vertex: entry.vertex,
1306 })
1307 },
1308 move |name| {
1309 source_tables.get(name).map(|entry| SourceEntryRef {
1310 vertex: entry.vertex,
1311 })
1312 },
1313 );
1314
1315 crate::engine::ingest_pipeline::IngestPipeline::new(
1316 data_store,
1317 sheet_reg,
1318 names,
1319 tables_view,
1320 sources,
1321 function_provider,
1322 policy,
1323 )
1324 }
1325
1326 pub fn to_a1(&self, cell_ref: CellRef) -> String {
1328 format!("{}!{}", self.sheet_name(cell_ref.sheet_id), cell_ref.coord)
1329 }
1330
1331 pub(crate) fn vertex_len(&self) -> usize {
1332 self.store.len()
1333 }
1334
1335 pub fn sheet_index_mut(&mut self, sheet_id: SheetId) -> &mut SheetIndex {
1338 self.sheet_indexes.entry(sheet_id).or_default()
1339 }
1340
1341 pub fn sheet_index(&self, sheet_id: SheetId) -> Option<&SheetIndex> {
1343 self.sheet_indexes.get(&sheet_id)
1344 }
1345
1346 pub fn set_cell_value(
1348 &mut self,
1349 sheet: &str,
1350 row: u32,
1351 col: u32,
1352 value: LiteralValue,
1353 ) -> Result<OperationSummary, ExcelError> {
1354 let value = normalize_stored_literal(value);
1355 let sheet_id = self.sheet_id_mut(sheet);
1356 let coord = Coord::from_excel(row, col, true, true);
1358 let addr = CellRef::new(sheet_id, coord);
1359 let mut created_placeholders = Vec::new();
1360
1361 let vertex_id = if let Some(&existing_id) = self.cell_to_vertex.get(&addr) {
1362 let is_formula = matches!(
1364 self.store.kind(existing_id),
1365 VertexKind::FormulaScalar | VertexKind::FormulaArray
1366 );
1367
1368 if is_formula {
1369 self.remove_dependent_edges(existing_id);
1370 self.detach_vertex_from_names(existing_id);
1371 self.clear_pending_name_references(existing_id);
1372 self.vertex_formulas.remove(&existing_id);
1373 }
1374
1375 self.store.set_kind(existing_id, VertexKind::Cell);
1377 if self.value_cache_enabled {
1378 let value_ref = self.data_store.store_value(value);
1379 self.vertex_values.insert(existing_id, value_ref);
1380 } else {
1381 self.vertex_values.remove(&existing_id);
1383 }
1384 existing_id
1385 } else {
1386 created_placeholders.push(addr);
1388 let packed_coord = AbsCoord::from_excel(row, col);
1389 let vertex_id = self.store.allocate(packed_coord, sheet_id, 0x01); self.edges.add_vertex(packed_coord, vertex_id.0);
1393
1394 self.sheet_index_mut(sheet_id)
1396 .add_vertex(packed_coord, vertex_id);
1397
1398 self.store.set_kind(vertex_id, VertexKind::Cell);
1399 if self.value_cache_enabled {
1400 let value_ref = self.data_store.store_value(value);
1401 self.vertex_values.insert(vertex_id, value_ref);
1402 }
1403 self.cell_to_vertex.insert(addr, vertex_id);
1404 vertex_id
1405 };
1406
1407 self.ref_error_vertices.remove(&vertex_id);
1409
1410 Ok(OperationSummary {
1411 affected_vertices: self.mark_dirty(vertex_id),
1412 created_placeholders,
1413 })
1414 }
1415
1416 pub fn reserve_cells(&mut self, additional: usize) {
1418 self.store.reserve(additional);
1419 if self.value_cache_enabled {
1420 self.vertex_values.reserve(additional);
1421 }
1422 self.cell_to_vertex.reserve(additional);
1423 }
1425
1426 pub fn set_cell_value_bulk_untracked(
1428 &mut self,
1429 sheet: &str,
1430 row: u32,
1431 col: u32,
1432 value: LiteralValue,
1433 ) {
1434 let value = normalize_stored_literal(value);
1435 let sheet_id = self.sheet_id_mut(sheet);
1436 let coord = Coord::from_excel(row, col, true, true);
1437 let addr = CellRef::new(sheet_id, coord);
1438 if let Some(&existing_id) = self.cell_to_vertex.get(&addr) {
1439 if matches!(
1441 self.store.kind(existing_id),
1442 VertexKind::FormulaScalar | VertexKind::FormulaArray
1443 ) {
1444 self.remove_dependent_edges(existing_id);
1445 self.detach_vertex_from_names(existing_id);
1446 self.clear_pending_name_references(existing_id);
1447 self.vertex_formulas.remove(&existing_id);
1448 }
1449 if self.value_cache_enabled {
1450 let value_ref = self.data_store.store_value(value);
1451 self.vertex_values.insert(existing_id, value_ref);
1452 } else {
1453 self.vertex_values.remove(&existing_id);
1454 }
1455 self.store.set_kind(existing_id, VertexKind::Cell);
1456 self.ref_error_vertices.remove(&existing_id);
1457 return;
1458 }
1459 let packed_coord = AbsCoord::from_excel(row, col);
1460 let vertex_id = self.store.allocate(packed_coord, sheet_id, 0x00); self.edges.add_vertex(packed_coord, vertex_id.0);
1462 self.sheet_index_mut(sheet_id)
1463 .add_vertex(packed_coord, vertex_id);
1464 self.store.set_kind(vertex_id, VertexKind::Cell);
1465 self.ref_error_vertices.remove(&vertex_id);
1466 if self.value_cache_enabled {
1467 let value_ref = self.data_store.store_value(value);
1468 self.vertex_values.insert(vertex_id, value_ref);
1469 }
1470 self.cell_to_vertex.insert(addr, vertex_id);
1471 }
1472
1473 pub fn bulk_insert_values<I>(&mut self, sheet: &str, cells: I)
1475 where
1476 I: IntoIterator<Item = (u32, u32, LiteralValue)>,
1477 {
1478 use crate::instant::FzInstant as Instant;
1479 let t0 = Instant::now();
1480 let collected: Vec<(u32, u32, LiteralValue)> = cells.into_iter().collect();
1482 if collected.is_empty() {
1483 return;
1484 }
1485 let sheet_id = self.sheet_id_mut(sheet);
1486 self.reserve_cells(collected.len());
1487 let t_reserve = Instant::now();
1488 let mut new_vertices: Vec<(AbsCoord, u32)> = Vec::with_capacity(collected.len());
1489 let mut index_items: Vec<(AbsCoord, VertexId)> = Vec::with_capacity(collected.len());
1490 let mut new_value_coords: Vec<(AbsCoord, VertexId)> = Vec::with_capacity(collected.len());
1492 let mut new_value_literals: Vec<LiteralValue> = Vec::with_capacity(collected.len());
1493 let assume_new = self.first_load_assume_new
1495 && self
1496 .sheet_id(sheet)
1497 .map(|sid| !self.ensure_touched_sheets.contains(&sid))
1498 .unwrap_or(false);
1499
1500 for (row, col, value) in collected {
1501 let value = normalize_stored_literal(value);
1502 let coord = Coord::from_excel(row, col, true, true);
1503 let addr = CellRef::new(sheet_id, coord);
1504 if !assume_new && let Some(&existing_id) = self.cell_to_vertex.get(&addr) {
1505 if matches!(
1506 self.store.kind(existing_id),
1507 VertexKind::FormulaScalar | VertexKind::FormulaArray
1508 ) {
1509 self.remove_dependent_edges(existing_id);
1510 self.detach_vertex_from_names(existing_id);
1511 self.clear_pending_name_references(existing_id);
1512 self.vertex_formulas.remove(&existing_id);
1513 }
1514 if self.value_cache_enabled {
1515 let value_ref = self.data_store.store_value(value);
1516 self.vertex_values.insert(existing_id, value_ref);
1517 } else {
1518 self.vertex_values.remove(&existing_id);
1519 }
1520 self.store.set_kind(existing_id, VertexKind::Cell);
1521 continue;
1522 }
1523 let packed = AbsCoord::from_excel(row, col);
1524 let vertex_id = self.store.allocate(packed, sheet_id, 0x00);
1525 self.store.set_kind(vertex_id, VertexKind::Cell);
1526 new_value_coords.push((packed, vertex_id));
1528 new_value_literals.push(value);
1529 self.cell_to_vertex.insert(addr, vertex_id);
1530 new_vertices.push((packed, vertex_id.0));
1531 index_items.push((packed, vertex_id));
1532 }
1533 if self.value_cache_enabled && !new_value_literals.is_empty() {
1535 let vrefs = self.data_store.store_values_batch(new_value_literals);
1536 debug_assert_eq!(vrefs.len(), new_value_coords.len());
1537 for (i, (_pc, vid)) in new_value_coords.iter().enumerate() {
1538 self.vertex_values.insert(*vid, vrefs[i]);
1539 }
1540 }
1541 let t_after_alloc = Instant::now();
1542 if !new_vertices.is_empty() {
1543 let t_edges_start = Instant::now();
1544 self.edges.add_vertices_batch(&new_vertices);
1545 let t_edges_done = Instant::now();
1546
1547 match self.config.sheet_index_mode {
1548 crate::engine::SheetIndexMode::Eager => {
1549 self.sheet_index_mut(sheet_id)
1550 .add_vertices_batch(&index_items);
1551 }
1552 crate::engine::SheetIndexMode::Lazy => {
1553 }
1555 crate::engine::SheetIndexMode::FastBatch => {
1556 self.sheet_index_mut(sheet_id)
1558 .add_vertices_batch(&index_items);
1559 }
1560 }
1561 let t_index_done = Instant::now();
1562 }
1563 }
1564
1565 pub fn set_cell_formula(
1567 &mut self,
1568 sheet: &str,
1569 row: u32,
1570 col: u32,
1571 ast: ASTNode,
1572 ) -> Result<OperationSummary, ExcelError> {
1573 self.set_cell_formula_with_volatility(sheet, row, col, ast, false)
1574 }
1575
1576 pub fn set_cell_formula_with_volatility(
1579 &mut self,
1580 sheet: &str,
1581 row: u32,
1582 col: u32,
1583 ast: ASTNode,
1584 _volatile: bool,
1585 ) -> Result<OperationSummary, ExcelError> {
1586 let sheet_id = self.sheet_id_mut(sheet);
1587 let placement = CellRef::new(sheet_id, Coord::from_excel(row, col, true, true));
1588 let provider = RegistryFunctionProvider;
1589 let ingested = {
1590 let mut pipeline = self.ingest_pipeline(&provider);
1591 pipeline.ingest_formula(FormulaAstInput::Tree(ast), placement, None)?
1592 };
1593 self.set_cell_formula_with_plan(
1594 sheet,
1595 row,
1596 col,
1597 ingested.ast_id,
1598 &ingested.dep_plan,
1599 ingested.dep_plan.volatile,
1600 ingested.dep_plan.dynamic,
1601 )
1602 }
1603
1604 pub(crate) fn set_cell_formula_with_plan(
1605 &mut self,
1606 sheet: &str,
1607 row: u32,
1608 col: u32,
1609 ast_id: AstNodeId,
1610 plan: &DependencyPlanRow,
1611 volatile: bool,
1612 dynamic: bool,
1613 ) -> Result<OperationSummary, ExcelError> {
1614 let dbg = std::env::var("FZ_DEBUG_LOAD")
1615 .ok()
1616 .is_some_and(|v| v != "0");
1617 let dep_ms_thresh: u128 = std::env::var("FZ_DEBUG_DEP_MS")
1618 .ok()
1619 .and_then(|s| s.parse().ok())
1620 .unwrap_or(0);
1621 let sample_n: usize = std::env::var("FZ_DEBUG_SAMPLE_N")
1622 .ok()
1623 .and_then(|s| s.parse().ok())
1624 .unwrap_or(0);
1625 let t0 = if dbg {
1626 Some(crate::instant::FzInstant::now())
1627 } else {
1628 None
1629 };
1630 let sheet_id = self.sheet_id_mut(sheet);
1631 let coord = Coord::from_excel(row, col, true, true);
1632 let addr = CellRef::new(sheet_id, coord);
1633
1634 let t_dep0 = if dbg {
1635 Some(crate::instant::FzInstant::now())
1636 } else {
1637 None
1638 };
1639 let mut created_placeholders = Vec::new();
1640 let mut new_dependencies = Vec::with_capacity(plan.direct_cell_deps.len());
1641 for dep in &plan.direct_cell_deps {
1642 let dep_vid = self.get_or_create_vertex(dep, &mut created_placeholders);
1643 if !new_dependencies.contains(&dep_vid) {
1644 new_dependencies.push(dep_vid);
1645 }
1646 }
1647 let mut named_dependencies = Vec::new();
1648 let mut unresolved_names = Vec::new();
1649 for name in plan
1650 .resolved_named_refs
1651 .iter()
1652 .chain(plan.named_refs.iter())
1653 {
1654 if let Some(named) = self.resolve_name_entry(name, sheet_id) {
1655 if !new_dependencies.contains(&named.vertex) {
1656 new_dependencies.push(named.vertex);
1657 }
1658 if !named_dependencies.contains(&named.vertex) {
1659 named_dependencies.push(named.vertex);
1660 }
1661 } else if let Some(source) = self.resolve_source_scalar_entry(name) {
1662 if !new_dependencies.contains(&source.vertex) {
1663 new_dependencies.push(source.vertex);
1664 }
1665 } else {
1666 unresolved_names.push(name.clone());
1667 }
1668 }
1669 for source_name in &plan.source_refs {
1670 if let Some(source) = self.resolve_source_scalar_entry(source_name) {
1671 if !new_dependencies.contains(&source.vertex) {
1672 new_dependencies.push(source.vertex);
1673 }
1674 } else if let Some(source) = self.resolve_source_table_entry(source_name)
1675 && !new_dependencies.contains(&source.vertex)
1676 {
1677 new_dependencies.push(source.vertex);
1678 }
1679 }
1680 for table_name in &plan.table_refs {
1681 if let Some(table) = self.resolve_table_entry(table_name) {
1682 if !new_dependencies.contains(&table.vertex) {
1683 new_dependencies.push(table.vertex);
1684 }
1685 } else if let Some(source) = self.resolve_source_table_entry(table_name)
1686 && !new_dependencies.contains(&source.vertex)
1687 {
1688 new_dependencies.push(source.vertex);
1689 }
1690 }
1691 if let (true, Some(t)) = (dbg, t_dep0) {
1692 let elapsed = t.elapsed().as_millis();
1693 let do_log = (dep_ms_thresh > 0 && elapsed >= dep_ms_thresh)
1694 || (sample_n > 0 && (row as usize).is_multiple_of(sample_n));
1695 if (dep_ms_thresh == 0 && sample_n == 0 && row.is_multiple_of(1000)) || do_log {
1696 eprintln!(
1697 "[fz][dep] {}!{} planned: deps={}, ranges={}, placeholders={}, names={} in {} ms",
1698 self.sheet_name(sheet_id),
1699 crate::reference::Coord::from_excel(row, col, true, true),
1700 new_dependencies.len(),
1701 plan.range_deps.len(),
1702 created_placeholders.len(),
1703 named_dependencies.len(),
1704 elapsed
1705 );
1706 }
1707 }
1708
1709 let addr_vertex_id = self.get_or_create_vertex(&addr, &mut created_placeholders);
1711
1712 self.ref_error_vertices.remove(&addr_vertex_id);
1714
1715 if new_dependencies.contains(&addr_vertex_id) {
1716 return Err(ExcelError::new(ExcelErrorKind::Circ)
1717 .with_message("Self-reference detected".to_string()));
1718 }
1719
1720 for &name_vertex in &named_dependencies {
1721 let mut visited = FxHashSet::default();
1722 if self.name_depends_on_vertex(name_vertex, addr_vertex_id, &mut visited) {
1723 return Err(ExcelError::new(ExcelErrorKind::Circ)
1724 .with_message("Circular reference through named range".to_string()));
1725 }
1726 }
1727
1728 self.remove_dependent_edges(addr_vertex_id);
1730 self.detach_vertex_from_names(addr_vertex_id);
1731 self.clear_pending_name_references(addr_vertex_id);
1732
1733 self.store
1735 .set_kind(addr_vertex_id, VertexKind::FormulaScalar);
1736 self.vertex_formulas.insert(addr_vertex_id, ast_id);
1737 self.store.set_dirty(addr_vertex_id, true);
1738
1739 self.vertex_values.remove(&addr_vertex_id);
1741
1742 self.mark_volatile(addr_vertex_id, volatile);
1743 self.store.set_dynamic(addr_vertex_id, dynamic);
1744
1745 if !named_dependencies.is_empty() {
1746 self.attach_vertex_to_names(addr_vertex_id, &named_dependencies);
1747 }
1748 for unresolved_name in &unresolved_names {
1749 self.record_pending_name_reference(sheet_id, unresolved_name, addr_vertex_id);
1750 }
1751
1752 if let (true, Some(t)) = (dbg, t0) {
1753 let elapsed = t.elapsed().as_millis();
1754 let log_set = dep_ms_thresh > 0 && elapsed >= dep_ms_thresh;
1755 if log_set {
1756 eprintln!(
1757 "[fz][set] {}!{} total {} ms",
1758 self.sheet_name(sheet_id),
1759 crate::reference::Coord::from_excel(row, col, true, true),
1760 elapsed
1761 );
1762 }
1763 }
1764
1765 self.add_dependent_edges(addr_vertex_id, &new_dependencies);
1767 self.add_range_dependent_edges(addr_vertex_id, &plan.range_deps, sheet_id);
1768
1769 Ok(OperationSummary {
1770 affected_vertices: self.mark_dirty(addr_vertex_id),
1771 created_placeholders,
1772 })
1773 }
1774
1775 pub(crate) fn rewrite_structured_references_for_cell(
1776 &self,
1777 ast: &mut ASTNode,
1778 cell: CellRef,
1779 ) -> Result<bool, ExcelError> {
1780 self.rewrite_structured_references_node(ast, cell)
1781 }
1782
1783 fn rewrite_structured_references_node(
1784 &self,
1785 node: &mut ASTNode,
1786 cell: CellRef,
1787 ) -> Result<bool, ExcelError> {
1788 match &mut node.node_type {
1789 ASTNodeType::Reference { reference, .. } => {
1790 self.rewrite_structured_reference(reference, cell)
1791 }
1792 ASTNodeType::UnaryOp { expr, .. } => {
1793 self.rewrite_structured_references_node(expr, cell)
1794 }
1795 ASTNodeType::BinaryOp { left, right, .. } => {
1796 let left_rewritten = self.rewrite_structured_references_node(left, cell)?;
1797 let right_rewritten = self.rewrite_structured_references_node(right, cell)?;
1798 Ok(left_rewritten || right_rewritten)
1799 }
1800 ASTNodeType::Function { args, .. } => {
1801 let mut rewritten = false;
1802 for a in args.iter_mut() {
1803 rewritten |= self.rewrite_structured_references_node(a, cell)?;
1804 }
1805 Ok(rewritten)
1806 }
1807 ASTNodeType::Call { callee, args } => {
1808 let mut rewritten = self.rewrite_structured_references_node(callee, cell)?;
1809 for a in args.iter_mut() {
1810 rewritten |= self.rewrite_structured_references_node(a, cell)?;
1811 }
1812 Ok(rewritten)
1813 }
1814 ASTNodeType::Array(rows) => {
1815 let mut rewritten = false;
1816 for r in rows.iter_mut() {
1817 for item in r.iter_mut() {
1818 rewritten |= self.rewrite_structured_references_node(item, cell)?;
1819 }
1820 }
1821 Ok(rewritten)
1822 }
1823 ASTNodeType::Literal(_) => Ok(false),
1824 }
1825 }
1826
1827 fn rewrite_structured_reference(
1828 &self,
1829 reference: &mut ReferenceType,
1830 cell: CellRef,
1831 ) -> Result<bool, ExcelError> {
1832 use formualizer_parse::parser::{SpecialItem, TableSpecifier};
1833
1834 let ReferenceType::Table(tref) = reference else {
1835 return Ok(false);
1836 };
1837
1838 if !tref.name.is_empty() {
1840 return Ok(false);
1841 }
1842
1843 let col_name = match &tref.specifier {
1844 Some(TableSpecifier::Combination(parts)) => {
1845 let mut saw_this_row = false;
1846 let mut col: Option<&str> = None;
1847 for p in parts {
1848 match p.as_ref() {
1849 TableSpecifier::SpecialItem(SpecialItem::ThisRow) => {
1850 saw_this_row = true;
1851 }
1852 TableSpecifier::Column(c) => {
1853 if col.is_some() {
1854 return Err(ExcelError::new(ExcelErrorKind::NImpl).with_message(
1855 "This-row structured reference with multiple columns is not supported"
1856 .to_string(),
1857 ));
1858 }
1859 col = Some(c.as_str());
1860 }
1861 other => {
1862 return Err(ExcelError::new(ExcelErrorKind::NImpl).with_message(
1863 format!(
1864 "Unsupported this-row structured reference component: {other}"
1865 ),
1866 ));
1867 }
1868 }
1869 }
1870 if !saw_this_row {
1871 return Err(ExcelError::new(ExcelErrorKind::NImpl).with_message(
1872 "Unnamed structured reference requires a this-row selector".to_string(),
1873 ));
1874 }
1875 col.ok_or_else(|| {
1876 ExcelError::new(ExcelErrorKind::NImpl).with_message(
1877 "This-row structured reference missing column selector".to_string(),
1878 )
1879 })?
1880 }
1881 _ => {
1882 return Err(ExcelError::new(ExcelErrorKind::NImpl).with_message(
1883 "Unnamed structured reference form is not supported".to_string(),
1884 ));
1885 }
1886 };
1887
1888 let Some(table) = self.find_table_containing_cell(cell) else {
1889 return Err(ExcelError::new(ExcelErrorKind::Name)
1890 .with_message("This-row structured reference used outside a table".to_string()));
1891 };
1892
1893 let row0 = cell.coord.row();
1894 let col0 = cell.coord.col();
1895 let sr0 = table.range.start.coord.row();
1896 let sc0 = table.range.start.coord.col();
1897 let er0 = table.range.end.coord.row();
1898 let ec0 = table.range.end.coord.col();
1899
1900 if row0 < sr0 || row0 > er0 || col0 < sc0 || col0 > ec0 {
1901 return Err(ExcelError::new(ExcelErrorKind::Name)
1902 .with_message("This-row structured reference used outside a table".to_string()));
1903 }
1904
1905 if table.header_row && row0 == sr0 {
1906 return Err(ExcelError::new(ExcelErrorKind::Ref).with_message(
1907 "This-row structured references are not valid in the table header row".to_string(),
1908 ));
1909 }
1910
1911 let data_start = if table.header_row { sr0 + 1 } else { sr0 };
1912 if row0 < data_start {
1913 return Err(ExcelError::new(ExcelErrorKind::Ref).with_message(
1914 "This-row structured references require a data/totals row context".to_string(),
1915 ));
1916 }
1917
1918 let Some(idx) = table.col_index(col_name) else {
1919 return Err(ExcelError::new(ExcelErrorKind::Ref).with_message(format!(
1920 "Unknown table column in this-row reference: {col_name}"
1921 )));
1922 };
1923 let target_col0 = sc0 + (idx as u32);
1924 let target_row = row0 + 1;
1925 let target_col = target_col0 + 1;
1926
1927 *reference = ReferenceType::Cell {
1928 sheet: None,
1929 row: target_row,
1930 col: target_col,
1931 row_abs: true,
1932 col_abs: true,
1933 };
1934
1935 Ok(true)
1936 }
1937
1938 fn find_table_containing_cell(&self, cell: CellRef) -> Option<&tables::TableEntry> {
1939 let row0 = cell.coord.row();
1940 let col0 = cell.coord.col();
1941
1942 let mut best: Option<&tables::TableEntry> = None;
1943 let mut best_area: u64 = u64::MAX;
1944 let mut best_name: &str = "";
1945
1946 for t in self.tables.values() {
1947 if t.sheet_id() != cell.sheet_id {
1948 continue;
1949 }
1950 let sr0 = t.range.start.coord.row();
1951 let sc0 = t.range.start.coord.col();
1952 let er0 = t.range.end.coord.row();
1953 let ec0 = t.range.end.coord.col();
1954 if row0 < sr0 || row0 > er0 || col0 < sc0 || col0 > ec0 {
1955 continue;
1956 }
1957
1958 let h = (er0 - sr0 + 1) as u64;
1959 let w = (ec0 - sc0 + 1) as u64;
1960 let area = h.saturating_mul(w);
1961 let name = t.name.as_str();
1962 let better = match best {
1963 None => true,
1964 Some(_) => area < best_area || (area == best_area && name < best_name),
1965 };
1966 if better {
1967 best = Some(t);
1968 best_area = area;
1969 best_name = name;
1970 }
1971 }
1972
1973 best
1974 }
1975
1976 #[allow(clippy::type_complexity)]
1977 pub(crate) fn fp8_parity_extract_dependencies_with_pending_names(
1978 &mut self,
1979 ast: &ASTNode,
1980 current_sheet_id: SheetId,
1981 ) -> Result<
1982 (
1983 Vec<VertexId>,
1984 Vec<SharedRangeRef<'static>>,
1985 Vec<CellRef>,
1986 Vec<VertexId>,
1987 Vec<String>,
1988 ),
1989 ExcelError,
1990 > {
1991 self.extract_dependencies_with_pending_names(ast, current_sheet_id)
1992 }
1993
1994 pub(crate) fn fp8_parity_is_ast_volatile(&self, ast: &ASTNode) -> bool {
1995 self.is_ast_volatile(ast)
1996 }
1997
1998 pub fn set_cell_value_ref(
1999 &mut self,
2000 cell: formualizer_common::SheetCellRef<'_>,
2001 value: LiteralValue,
2002 ) -> Result<OperationSummary, ExcelError> {
2003 let owned = cell.into_owned();
2004 let sheet_id = match owned.sheet {
2005 formualizer_common::SheetLocator::Id(id) => id,
2006 formualizer_common::SheetLocator::Name(name) => self.sheet_id_mut(name.as_ref()),
2007 formualizer_common::SheetLocator::Current => self.default_sheet_id,
2008 };
2009 let sheet_name = self.sheet_name(sheet_id).to_string();
2010 self.set_cell_value(
2011 &sheet_name,
2012 owned.coord.row() + 1,
2013 owned.coord.col() + 1,
2014 value,
2015 )
2016 }
2017
2018 pub fn set_cell_formula_ref(
2019 &mut self,
2020 cell: formualizer_common::SheetCellRef<'_>,
2021 ast: ASTNode,
2022 ) -> Result<OperationSummary, ExcelError> {
2023 let owned = cell.into_owned();
2024 let sheet_id = match owned.sheet {
2025 formualizer_common::SheetLocator::Id(id) => id,
2026 formualizer_common::SheetLocator::Name(name) => self.sheet_id_mut(name.as_ref()),
2027 formualizer_common::SheetLocator::Current => self.default_sheet_id,
2028 };
2029 let sheet_name = self.sheet_name(sheet_id).to_string();
2030 self.set_cell_formula(
2031 &sheet_name,
2032 owned.coord.row() + 1,
2033 owned.coord.col() + 1,
2034 ast,
2035 )
2036 }
2037
2038 pub fn get_cell_value_ref(
2039 &self,
2040 cell: formualizer_common::SheetCellRef<'_>,
2041 ) -> Option<LiteralValue> {
2042 let owned = cell.into_owned();
2043 let sheet_id = match owned.sheet {
2044 formualizer_common::SheetLocator::Id(id) => id,
2045 formualizer_common::SheetLocator::Name(name) => self.sheet_id(name.as_ref())?,
2046 formualizer_common::SheetLocator::Current => self.default_sheet_id,
2047 };
2048 let sheet_name = self.sheet_name(sheet_id);
2049 self.get_cell_value(sheet_name, owned.coord.row() + 1, owned.coord.col() + 1)
2050 }
2051
2052 pub fn get_cell_value(&self, sheet: &str, row: u32, col: u32) -> Option<LiteralValue> {
2054 if !self.value_cache_enabled {
2055 #[cfg(debug_assertions)]
2056 {
2057 self.graph_value_read_attempts
2058 .fetch_add(1, Ordering::Relaxed);
2059 }
2060 return None;
2061 }
2062 let sheet_id = self.sheet_reg.get_id(sheet)?;
2063 let coord = Coord::from_excel(row, col, true, true);
2064 let addr = CellRef::new(sheet_id, coord);
2065
2066 self.get_vertex_id_for_address(&addr)
2067 .and_then(|&vertex_id| {
2068 self.vertex_values
2070 .get(&vertex_id)
2071 .map(|&value_ref| self.data_store.retrieve_value(value_ref))
2072 })
2073 }
2074
2075 fn mark_dirty(&mut self, vertex_id: VertexId) -> Vec<VertexId> {
2077 let mut affected = FxHashSet::default();
2078 let mut to_visit = Vec::new();
2079 let mut visited_for_propagation = FxHashSet::default();
2080
2081 let is_formula = matches!(
2084 self.store.kind(vertex_id),
2085 VertexKind::FormulaScalar
2086 | VertexKind::FormulaArray
2087 | VertexKind::NamedScalar
2088 | VertexKind::NamedArray
2089 );
2090
2091 if is_formula {
2092 to_visit.push(vertex_id);
2093 } else {
2094 affected.insert(vertex_id);
2096 }
2097
2098 {
2100 if let Some(dependents) = self.dependents_slice(vertex_id) {
2102 to_visit.extend(dependents.iter().copied());
2103 } else {
2104 let dependents = self.get_dependents(vertex_id);
2105 to_visit.extend(dependents);
2106 }
2107
2108 if let Some(name_set) = self.cell_to_name_dependents.get(&vertex_id) {
2109 for &name_vertex in name_set {
2110 to_visit.push(name_vertex);
2111 }
2112 }
2113
2114 to_visit.extend(self.collect_range_dependents_for_vertex(vertex_id));
2115 }
2116
2117 while let Some(id) = to_visit.pop() {
2118 if !visited_for_propagation.insert(id) {
2119 continue; }
2121 affected.insert(id);
2122
2123 self.store.set_dirty(id, true);
2125
2126 if let Some(dependents) = self.dependents_slice(id) {
2128 to_visit.extend(dependents.iter().copied());
2129 } else {
2130 let dependents = self.get_dependents(id);
2131 to_visit.extend(dependents);
2132 }
2133 to_visit.extend(self.collect_range_dependents_for_vertex(id));
2134 }
2135
2136 self.dirty_vertices.extend(&affected);
2138
2139 affected.into_iter().collect()
2141 }
2142
2143 pub fn get_evaluation_vertices(&self) -> Vec<VertexId> {
2145 let mut combined = FxHashSet::default();
2146 combined.extend(&self.dirty_vertices);
2147 combined.extend(&self.volatile_vertices);
2148
2149 let mut result: Vec<VertexId> = combined
2150 .into_iter()
2151 .filter(|&id| {
2152 self.store.vertex_exists_active(id)
2155 && matches!(
2156 self.store.kind(id),
2157 VertexKind::FormulaScalar
2158 | VertexKind::FormulaArray
2159 | VertexKind::NamedScalar
2160 | VertexKind::NamedArray
2161 )
2162 })
2163 .collect();
2164 result.sort_unstable();
2165 result
2166 }
2167
2168 pub fn clear_dirty_flags(&mut self, vertices: &[VertexId]) {
2170 for &vertex_id in vertices {
2171 self.store.set_dirty(vertex_id, false);
2172 self.dirty_vertices.remove(&vertex_id);
2173 }
2174 }
2175
2176 pub fn clear_volatile_flags(&mut self) {
2178 self.volatile_vertices.clear();
2179 }
2180
2181 pub(crate) fn redirty_volatiles(&mut self) {
2183 let volatile_ids: Vec<VertexId> = self.volatile_vertices.iter().copied().collect();
2184 for id in volatile_ids {
2185 self.mark_dirty(id);
2186 }
2187 }
2188
2189 fn get_or_create_vertex(
2190 &mut self,
2191 addr: &CellRef,
2192 created_placeholders: &mut Vec<CellRef>,
2193 ) -> VertexId {
2194 if let Some(&vertex_id) = self.cell_to_vertex.get(addr) {
2195 return vertex_id;
2196 }
2197
2198 if self.first_load_assume_new {
2203 let packed = Self::packed_cell_key(
2204 addr.sheet_id,
2205 AbsCoord::new(addr.coord.row(), addr.coord.col()),
2206 );
2207 if let Some(&existing) = self.load_packed_to_vertex.get(&packed) {
2208 self.cell_to_vertex.insert(*addr, existing);
2209 return existing;
2210 }
2211 }
2212
2213 created_placeholders.push(*addr);
2214 let packed_coord = AbsCoord::new(addr.coord.row(), addr.coord.col());
2215 let vertex_id = self.store.allocate(packed_coord, addr.sheet_id, 0x00);
2216
2217 self.edges.add_vertex(packed_coord, vertex_id.0);
2219
2220 self.sheet_index_mut(addr.sheet_id)
2222 .add_vertex(packed_coord, vertex_id);
2223
2224 self.store.set_kind(vertex_id, VertexKind::Empty);
2225 self.cell_to_vertex.insert(*addr, vertex_id);
2226 vertex_id
2227 }
2228
2229 fn add_dependent_edges(&mut self, dependent: VertexId, dependencies: &[VertexId]) {
2230 self.edges.begin_batch();
2232
2233 let mut skip_deps: rustc_hash::FxHashSet<VertexId> = rustc_hash::FxHashSet::default();
2236 if self.pk_order.is_some()
2237 && let Some(mut pk) = self.pk_order.take()
2238 {
2239 pk.ensure_nodes(std::iter::once(dependent));
2240 pk.ensure_nodes(dependencies.iter().copied());
2241 {
2242 let adapter = GraphAdapter { g: self };
2243 for &dep_id in dependencies {
2244 match pk.try_add_edge(&adapter, dep_id, dependent) {
2245 Ok(_) => {}
2246 Err(_cycle) => {
2247 if self.config.pk_reject_cycle_edges {
2248 skip_deps.insert(dep_id);
2249 } else {
2250 pk.rebuild_full(&adapter);
2251 }
2252 }
2253 }
2254 }
2255 } self.pk_order = Some(pk);
2257 }
2258
2259 for &dep_id in dependencies {
2261 if self.config.pk_reject_cycle_edges && skip_deps.contains(&dep_id) {
2262 continue;
2263 }
2264 self.edges.add_edge(dependent, dep_id);
2265 #[cfg(test)]
2266 {
2267 if let Ok(mut g) = self.instr.lock() {
2268 g.edges_added += 1;
2269 }
2270 }
2271 }
2272
2273 self.edges.end_batch();
2274 }
2275
2276 fn add_dependent_edges_nobatch(&mut self, dependent: VertexId, dependencies: &[VertexId]) {
2278 let mut skip_deps: rustc_hash::FxHashSet<VertexId> = rustc_hash::FxHashSet::default();
2280 if self.pk_order.is_some()
2281 && let Some(mut pk) = self.pk_order.take()
2282 {
2283 pk.ensure_nodes(std::iter::once(dependent));
2284 pk.ensure_nodes(dependencies.iter().copied());
2285 {
2286 let adapter = GraphAdapter { g: self };
2287 for &dep_id in dependencies {
2288 match pk.try_add_edge(&adapter, dep_id, dependent) {
2289 Ok(_) => {}
2290 Err(_cycle) => {
2291 if self.config.pk_reject_cycle_edges {
2292 skip_deps.insert(dep_id);
2293 } else {
2294 pk.rebuild_full(&adapter);
2295 }
2296 }
2297 }
2298 }
2299 }
2300 self.pk_order = Some(pk);
2301 }
2302
2303 for &dep_id in dependencies {
2304 if self.config.pk_reject_cycle_edges && skip_deps.contains(&dep_id) {
2305 continue;
2306 }
2307 self.edges.add_edge(dependent, dep_id);
2308 #[cfg(test)]
2309 {
2310 if let Ok(mut g) = self.instr.lock() {
2311 g.edges_added += 1;
2312 }
2313 }
2314 }
2315 }
2316
2317 pub fn bulk_set_formulas<I>(&mut self, sheet: &str, items: I) -> Result<usize, ExcelError>
2319 where
2320 I: IntoIterator<Item = (u32, u32, ASTNode)>,
2321 {
2322 let collected: Vec<(u32, u32, ASTNode)> = items.into_iter().collect();
2323 if collected.is_empty() {
2324 return Ok(0);
2325 }
2326 let vol_flags: Vec<bool> = collected
2327 .iter()
2328 .map(|(_, _, ast)| self.is_ast_volatile(ast))
2329 .collect();
2330 self.bulk_set_formulas_with_volatility(sheet, collected, vol_flags)
2331 }
2332
2333 pub fn bulk_set_formulas_with_volatility(
2334 &mut self,
2335 sheet: &str,
2336 collected: Vec<(u32, u32, ASTNode)>,
2337 _vol_flags: Vec<bool>,
2338 ) -> Result<usize, ExcelError> {
2339 let sheet_id = self.sheet_id_mut(sheet);
2340 if collected.is_empty() {
2341 return Ok(0);
2342 }
2343 let provider = RegistryFunctionProvider;
2344 let ingested = {
2345 let mut pipeline = self.ingest_pipeline(&provider);
2346 let inputs = collected.into_iter().map(|(row, col, ast)| {
2347 let placement = CellRef::new(sheet_id, Coord::from_excel(row, col, true, true));
2348 (FormulaAstInput::Tree(ast), placement, None)
2349 });
2350 pipeline.ingest_batch(inputs)?
2351 };
2352 let planned = ingested
2353 .into_iter()
2354 .map(|formula| {
2355 (
2356 formula.placement.coord.row() + 1,
2357 formula.placement.coord.col() + 1,
2358 formula.ast_id,
2359 formula.dep_plan,
2360 )
2361 })
2362 .collect();
2363 self.bulk_set_formulas_with_plans(sheet, planned)
2364 }
2365
2366 pub(crate) fn bulk_set_formulas_with_plans(
2367 &mut self,
2368 sheet: &str,
2369 planned: Vec<(u32, u32, AstNodeId, DependencyPlanRow)>,
2370 ) -> Result<usize, ExcelError> {
2371 let sheet_id = self.sheet_id_mut(sheet);
2372 if planned.is_empty() {
2373 return Ok(0);
2374 }
2375 let mut created_placeholders: Vec<CellRef> = Vec::new();
2376 let mut target_vids: Vec<VertexId> = Vec::with_capacity(planned.len());
2377 for (row, col, _, _) in &planned {
2378 let addr = CellRef::new(sheet_id, Coord::from_excel(*row, *col, true, true));
2379 target_vids.push(self.get_or_create_vertex(&addr, &mut created_placeholders));
2380 }
2381 for (_, _, _, plan) in &planned {
2386 for cell in &plan.direct_cell_deps {
2387 self.get_or_create_vertex(cell, &mut created_placeholders);
2388 }
2389 }
2390
2391 for (i, &tvid) in target_vids.iter().enumerate() {
2392 if self.vertex_formulas.contains_key(&tvid) {
2393 self.remove_dependent_edges(tvid);
2394 }
2395 self.detach_vertex_from_names(tvid);
2396 self.clear_pending_name_references(tvid);
2397 self.store.set_kind(tvid, VertexKind::FormulaScalar);
2398 self.store.set_dirty(tvid, true);
2399 self.vertex_values.remove(&tvid);
2400 self.vertex_formulas.insert(tvid, planned[i].2);
2401 self.mark_volatile(tvid, planned[i].3.volatile);
2402 self.store.set_dynamic(tvid, planned[i].3.dynamic);
2403 }
2404 self.dirty_vertices.extend(target_vids.iter().copied());
2405
2406 self.edges.begin_batch();
2407 for (i, tvid) in target_vids.iter().copied().enumerate() {
2408 let plan = &planned[i].3;
2409 let mut deps: Vec<VertexId> = Vec::new();
2410 for cell in &plan.direct_cell_deps {
2411 let dep_vid = self.get_or_create_vertex(cell, &mut created_placeholders);
2412 if !deps.contains(&dep_vid) {
2413 deps.push(dep_vid);
2414 }
2415 }
2416
2417 let mut name_vertices = Vec::new();
2418 for name in plan
2419 .resolved_named_refs
2420 .iter()
2421 .chain(plan.named_refs.iter())
2422 {
2423 if let Some(named) = self.resolve_name_entry(name, sheet_id) {
2424 if !deps.contains(&named.vertex) {
2425 deps.push(named.vertex);
2426 }
2427 if !name_vertices.contains(&named.vertex) {
2428 name_vertices.push(named.vertex);
2429 }
2430 } else if let Some(source) = self.resolve_source_scalar_entry(name) {
2431 if !deps.contains(&source.vertex) {
2432 deps.push(source.vertex);
2433 }
2434 } else {
2435 self.record_pending_name_reference(sheet_id, name, tvid);
2436 }
2437 }
2438 for source_name in &plan.source_refs {
2439 if let Some(source) = self.resolve_source_scalar_entry(source_name) {
2440 if !deps.contains(&source.vertex) {
2441 deps.push(source.vertex);
2442 }
2443 } else if let Some(source) = self.resolve_source_table_entry(source_name)
2444 && !deps.contains(&source.vertex)
2445 {
2446 deps.push(source.vertex);
2447 }
2448 }
2449 for table_name in &plan.table_refs {
2450 if let Some(table) = self.resolve_table_entry(table_name) {
2451 if !deps.contains(&table.vertex) {
2452 deps.push(table.vertex);
2453 }
2454 } else if let Some(source) = self.resolve_source_table_entry(table_name)
2455 && !deps.contains(&source.vertex)
2456 {
2457 deps.push(source.vertex);
2458 }
2459 }
2460 if !name_vertices.is_empty() {
2461 self.attach_vertex_to_names(tvid, &name_vertices);
2462 }
2463 if !deps.is_empty() {
2464 self.add_dependent_edges_nobatch(tvid, &deps);
2465 }
2466 self.add_range_dependent_edges(tvid, &plan.range_deps, sheet_id);
2467 }
2468 self.edges.end_batch();
2469
2470 Ok(planned.len())
2471 }
2472
2473 pub fn add_dependency_edge(&mut self, dependent: VertexId, dependency: VertexId) {
2475 if dependent == dependency {
2476 return;
2477 }
2478 if self.pk_order.is_some()
2480 && let Some(mut pk) = self.pk_order.take()
2481 {
2482 pk.ensure_nodes(std::iter::once(dependent));
2483 pk.ensure_nodes(std::iter::once(dependency));
2484 let adapter = GraphAdapter { g: self };
2485 if pk.try_add_edge(&adapter, dependency, dependent).is_err() {
2486 pk.rebuild_full(&adapter);
2488 }
2489 self.pk_order = Some(pk);
2490 }
2491 self.edges.add_edge(dependent, dependency);
2492 self.store.set_dirty(dependent, true);
2493 self.dirty_vertices.insert(dependent);
2494 }
2495
2496 fn remove_dependent_edges(&mut self, vertex: VertexId) {
2497 let dependencies = self.edges.out_edges(vertex);
2499
2500 self.edges.begin_batch();
2501 if self.pk_order.is_some()
2502 && let Some(mut pk) = self.pk_order.take()
2503 {
2504 for dep in &dependencies {
2505 pk.remove_edge(*dep, vertex);
2506 }
2507 self.pk_order = Some(pk);
2508 }
2509 for dep in dependencies {
2510 self.edges.remove_edge(vertex, dep);
2511 }
2512 self.edges.end_batch();
2513
2514 if let Some(old_ranges) = self.formula_to_range_deps.remove(&vertex) {
2516 let old_sheet_id = self.store.sheet_id(vertex);
2517
2518 for range in &old_ranges {
2519 let sheet_id = match range.sheet {
2520 SharedSheetLocator::Id(id) => id,
2521 _ => old_sheet_id,
2522 };
2523 let s_row = range.start_row.map(|b| b.index);
2524 let e_row = range.end_row.map(|b| b.index);
2525 let s_col = range.start_col.map(|b| b.index);
2526 let e_col = range.end_col.map(|b| b.index);
2527
2528 let mut keys_to_clean = FxHashSet::default();
2529
2530 let col_stripes = (s_row.is_none() && e_row.is_none())
2531 || (s_col.is_some() && e_col.is_some() && (s_row.is_none() || e_row.is_none()));
2532 let row_stripes = (s_col.is_none() && e_col.is_none())
2533 || (s_row.is_some() && e_row.is_some() && (s_col.is_none() || e_col.is_none()));
2534
2535 if col_stripes && !row_stripes {
2536 let sc = s_col.unwrap_or(0);
2537 let ec = e_col.unwrap_or(sc);
2538 for col in sc..=ec {
2539 keys_to_clean.insert(StripeKey {
2540 sheet_id,
2541 stripe_type: StripeType::Column,
2542 index: col,
2543 });
2544 }
2545 } else if row_stripes && !col_stripes {
2546 let sr = s_row.unwrap_or(0);
2547 let er = e_row.unwrap_or(sr);
2548 for row in sr..=er {
2549 keys_to_clean.insert(StripeKey {
2550 sheet_id,
2551 stripe_type: StripeType::Row,
2552 index: row,
2553 });
2554 }
2555 } else {
2556 let start_row = s_row.unwrap_or(0);
2557 let start_col = s_col.unwrap_or(0);
2558 let end_row = e_row.unwrap_or(start_row);
2559 let end_col = e_col.unwrap_or(start_col);
2560
2561 let height = end_row.saturating_sub(start_row) + 1;
2562 let width = end_col.saturating_sub(start_col) + 1;
2563
2564 if self.config.enable_block_stripes && height > 1 && width > 1 {
2565 let start_block_row = start_row / BLOCK_H;
2566 let end_block_row = end_row / BLOCK_H;
2567 let start_block_col = start_col / BLOCK_W;
2568 let end_block_col = end_col / BLOCK_W;
2569
2570 for block_row in start_block_row..=end_block_row {
2571 for block_col in start_block_col..=end_block_col {
2572 keys_to_clean.insert(StripeKey {
2573 sheet_id,
2574 stripe_type: StripeType::Block,
2575 index: block_index(block_row * BLOCK_H, block_col * BLOCK_W),
2576 });
2577 }
2578 }
2579 } else if height > width {
2580 for col in start_col..=end_col {
2581 keys_to_clean.insert(StripeKey {
2582 sheet_id,
2583 stripe_type: StripeType::Column,
2584 index: col,
2585 });
2586 }
2587 } else {
2588 for row in start_row..=end_row {
2589 keys_to_clean.insert(StripeKey {
2590 sheet_id,
2591 stripe_type: StripeType::Row,
2592 index: row,
2593 });
2594 }
2595 }
2596 }
2597
2598 for key in keys_to_clean {
2599 if let Some(dependents) = self.stripe_to_dependents.get_mut(&key) {
2600 dependents.remove(&vertex);
2601 if dependents.is_empty() {
2602 self.stripe_to_dependents.remove(&key);
2603 #[cfg(test)]
2604 {
2605 if let Ok(mut g) = self.instr.lock() {
2606 g.stripe_removes += 1;
2607 }
2608 }
2609 }
2610 }
2611 }
2612 }
2613 }
2614 }
2615
2616 pub(crate) fn update_vertex_value(&mut self, vertex_id: VertexId, value: LiteralValue) {
2622 if !self.value_cache_enabled {
2623 match self.store.kind(vertex_id) {
2625 VertexKind::Cell
2626 | VertexKind::FormulaScalar
2627 | VertexKind::FormulaArray
2628 | VertexKind::Empty => {
2629 self.vertex_values.remove(&vertex_id);
2630 return;
2631 }
2632 _ => {
2633 }
2635 }
2636 }
2637 let value_ref = self.data_store.store_value(normalize_stored_literal(value));
2638 self.vertex_values.insert(vertex_id, value_ref);
2639 }
2640
2641 pub fn plan_spill_region(
2643 &self,
2644 anchor: VertexId,
2645 target_cells: &[CellRef],
2646 ) -> Result<(), ExcelError> {
2647 self.plan_spill_region_allowing_formula_overwrite(anchor, target_cells, None)
2648 }
2649
2650 pub(crate) fn plan_spill_region_allowing_formula_overwrite(
2655 &self,
2656 anchor: VertexId,
2657 target_cells: &[CellRef],
2658 overwritable_formulas: Option<&rustc_hash::FxHashSet<VertexId>>,
2659 ) -> Result<(), ExcelError> {
2660 use formualizer_common::{ExcelErrorExtra, ExcelErrorKind};
2661 let (expected_rows, expected_cols) = if target_cells.is_empty() {
2663 (0u32, 0u32)
2664 } else {
2665 let mut min_r = u32::MAX;
2666 let mut max_r = 0u32;
2667 let mut min_c = u32::MAX;
2668 let mut max_c = 0u32;
2669 for cell in target_cells {
2670 let r = cell.coord.row();
2671 let c = cell.coord.col();
2672 if r < min_r {
2673 min_r = r;
2674 }
2675 if r > max_r {
2676 max_r = r;
2677 }
2678 if c < min_c {
2679 min_c = c;
2680 }
2681 if c > max_c {
2682 max_c = c;
2683 }
2684 }
2685 (
2686 max_r.saturating_sub(min_r).saturating_add(1),
2687 max_c.saturating_sub(min_c).saturating_add(1),
2688 )
2689 };
2690 for cell in target_cells {
2692 let owned_by_anchor = match self.spill_cell_to_anchor.get(cell) {
2694 Some(&existing_anchor) if existing_anchor == anchor => true,
2695 Some(_other) => {
2696 return Err(ExcelError::new(ExcelErrorKind::Spill)
2697 .with_message("BlockedBySpill")
2698 .with_extra(ExcelErrorExtra::Spill {
2699 expected_rows,
2700 expected_cols,
2701 }));
2702 }
2703 None => false,
2704 };
2705
2706 if owned_by_anchor {
2707 continue;
2708 }
2709
2710 if let Some(&vid) = self.cell_to_vertex.get(cell)
2712 && vid != anchor
2713 {
2714 match self.store.kind(vid) {
2716 VertexKind::FormulaScalar | VertexKind::FormulaArray => {
2717 if let Some(allow) = overwritable_formulas
2718 && allow.contains(&vid)
2719 {
2720 continue;
2721 }
2722 return Err(ExcelError::new(ExcelErrorKind::Spill)
2723 .with_message("BlockedByFormula")
2724 .with_extra(ExcelErrorExtra::Spill {
2725 expected_rows,
2726 expected_cols,
2727 }));
2728 }
2729 _ => {
2730 if let Some(vref) = self.vertex_values.get(&vid) {
2732 let v = self.data_store.retrieve_value(*vref);
2733 if !matches!(v, LiteralValue::Empty) {
2734 return Err(ExcelError::new(ExcelErrorKind::Spill)
2735 .with_message("BlockedByValue")
2736 .with_extra(ExcelErrorExtra::Spill {
2737 expected_rows,
2738 expected_cols,
2739 }));
2740 }
2741 }
2742 }
2743 }
2744 }
2745 }
2746 Ok(())
2747 }
2748
2749 pub fn commit_spill_region_atomic_with_fault(
2756 &mut self,
2757 anchor: VertexId,
2758 target_cells: Vec<CellRef>,
2759 values: Vec<Vec<LiteralValue>>,
2760 fault_after_ops: Option<usize>,
2761 ) -> Result<(), ExcelError> {
2762 let anchor_cell = self
2766 .get_cell_ref(anchor)
2767 .expect("anchor cell ref for spill commit");
2768 let anchor_sheet_name = self.sheet_name(anchor_cell.sheet_id).to_string();
2769 let anchor_row = anchor_cell.coord.row();
2770 let anchor_col = anchor_cell.coord.col();
2771
2772 let prev_cells = self
2774 .spill_anchor_to_cells
2775 .get(&anchor)
2776 .cloned()
2777 .unwrap_or_default();
2778 let new_set: std::collections::HashSet<CellRef, CoordBuildHasher> =
2781 target_cells.iter().copied().collect();
2782 let prev_set: std::collections::HashSet<CellRef, CoordBuildHasher> =
2783 prev_cells.iter().copied().collect();
2784
2785 #[derive(Clone)]
2787 struct Op {
2788 sheet: String,
2789 row: u32,
2790 col: u32,
2791 new_value: LiteralValue,
2792 }
2793 let mut ops: Vec<Op> = Vec::new();
2794
2795 for cell in prev_cells.iter() {
2797 if !new_set.contains(cell) {
2798 let sheet = self.sheet_name(cell.sheet_id).to_string();
2799 ops.push(Op {
2800 sheet,
2801 row: cell.coord.row(),
2802 col: cell.coord.col(),
2803 new_value: LiteralValue::Empty,
2804 });
2805 }
2806 }
2807
2808 if !target_cells.is_empty() {
2810 let first = target_cells.first().copied().unwrap();
2811 let row0 = first.coord.row();
2812 let col0 = first.coord.col();
2813 let sheet = self.sheet_name(first.sheet_id).to_string();
2814 for (r_off, row_vals) in values.iter().enumerate() {
2815 for (c_off, v) in row_vals.iter().enumerate() {
2816 ops.push(Op {
2817 sheet: sheet.clone(),
2818 row: row0 + r_off as u32,
2819 col: col0 + c_off as u32,
2820 new_value: v.clone(),
2821 });
2822 }
2823 }
2824 }
2825
2826 #[derive(Clone)]
2828 struct OldVal {
2829 present: bool,
2830 value: LiteralValue,
2831 }
2832 let mut old_values: Vec<((String, u32, u32), OldVal)> = Vec::with_capacity(ops.len());
2833
2834 for op in &ops {
2836 let old = self
2838 .get_cell_value(&op.sheet, op.row + 1, op.col + 1)
2839 .unwrap_or(LiteralValue::Empty);
2840 let present = true; old_values.push((
2842 (op.sheet.clone(), op.row, op.col),
2843 OldVal {
2844 present,
2845 value: old,
2846 },
2847 ));
2848 }
2849
2850 for (applied, op) in ops.iter().enumerate() {
2852 if let Some(n) = fault_after_ops
2853 && applied == n
2854 {
2855 for idx in (0..applied).rev() {
2856 let ((ref sheet, row, col), ref old) = old_values[idx];
2857 if sheet == &anchor_sheet_name && row == anchor_row && col == anchor_col {
2858 self.update_vertex_value(anchor, old.value.clone());
2859 } else {
2860 let _ = self.set_cell_value(sheet, row + 1, col + 1, old.value.clone());
2861 }
2862 }
2863 return Err(ExcelError::new(ExcelErrorKind::Error)
2864 .with_message("Injected persistence fault during spill commit"));
2865 }
2866 if op.sheet == anchor_sheet_name && op.row == anchor_row && op.col == anchor_col {
2867 self.update_vertex_value(anchor, op.new_value.clone());
2868 } else {
2869 let _ =
2870 self.set_cell_value(&op.sheet, op.row + 1, op.col + 1, op.new_value.clone());
2871 }
2872 }
2873
2874 for cell in prev_cells.iter() {
2877 if !new_set.contains(cell) {
2878 self.spill_cell_to_anchor.remove(cell);
2879 }
2880 }
2881 for cell in &target_cells {
2883 self.spill_cell_to_anchor.insert(*cell, anchor);
2884 }
2885 self.spill_anchor_to_cells.insert(anchor, target_cells);
2886 Ok(())
2887 }
2888
2889 pub(crate) fn spill_cells_for_anchor(&self, anchor: VertexId) -> Option<&[CellRef]> {
2890 self.spill_anchor_to_cells
2891 .get(&anchor)
2892 .map(|v| v.as_slice())
2893 }
2894
2895 pub(crate) fn spill_registry_has_anchor(&self, anchor: VertexId) -> bool {
2896 self.spill_anchor_to_cells.contains_key(&anchor)
2897 }
2898
2899 pub(crate) fn spill_registry_anchor_for_cell(&self, cell: CellRef) -> Option<VertexId> {
2900 self.spill_cell_to_anchor.get(&cell).copied()
2901 }
2902
2903 pub(crate) fn spill_registry_counts(&self) -> (usize, usize) {
2904 (
2905 self.spill_anchor_to_cells.len(),
2906 self.spill_cell_to_anchor.len(),
2907 )
2908 }
2909
2910 pub fn clear_spill_region(&mut self, anchor: VertexId) {
2912 let _ = self.clear_spill_region_bulk(anchor);
2913 }
2914
2915 pub fn clear_spill_region_bulk(&mut self, anchor: VertexId) -> Vec<CellRef> {
2924 let anchor_cell = self.get_cell_ref(anchor);
2925 let Some(cells) = self.spill_anchor_to_cells.remove(&anchor) else {
2926 return Vec::new();
2927 };
2928
2929 for cell in cells.iter() {
2931 self.spill_cell_to_anchor.remove(cell);
2932 }
2933
2934 let empty_ref = if self.value_cache_enabled {
2936 Some(self.data_store.store_value(LiteralValue::Empty))
2937 } else {
2938 None
2939 };
2940
2941 let mut changed_vertices: Vec<VertexId> = Vec::new();
2943 for cell in cells.iter().copied() {
2944 let is_anchor = anchor_cell.map(|a| a == cell).unwrap_or(false);
2945 if is_anchor {
2946 continue;
2947 }
2948 let Some(&vid) = self.cell_to_vertex.get(&cell) else {
2949 continue;
2950 };
2951 if self.vertex_formulas.remove(&vid).is_some() {
2953 self.remove_dependent_edges(vid);
2956 }
2957 self.store.set_kind(vid, VertexKind::Cell);
2958 if let Some(er) = empty_ref {
2959 self.vertex_values.insert(vid, er);
2960 } else {
2961 self.vertex_values.remove(&vid);
2962 }
2963 self.store.set_dirty(vid, false);
2964 self.dirty_vertices.remove(&vid);
2965 changed_vertices.push(vid);
2966 }
2967
2968 if !changed_vertices.is_empty() {
2970 self.mark_dirty_many_value_cells(&changed_vertices);
2971 }
2972
2973 cells
2974 }
2975
2976 fn mark_dirty_many_value_cells(&mut self, vertex_ids: &[VertexId]) -> Vec<VertexId> {
2977 if vertex_ids.is_empty() {
2978 return Vec::new();
2979 }
2980
2981 if self.edges.delta_size() > 0 {
2983 self.edges.rebuild();
2984 }
2985
2986 let mut affected: FxHashSet<VertexId> = FxHashSet::default();
2987 let mut to_visit: Vec<VertexId> = Vec::new();
2988 let mut visited_for_propagation: FxHashSet<VertexId> = FxHashSet::default();
2989
2990 for &src in vertex_ids {
2992 affected.insert(src);
2993 }
2994
2995 for &src in vertex_ids {
2997 to_visit.extend(self.edges.in_edges(src));
2998 if let Some(name_set) = self.cell_to_name_dependents.get(&src) {
2999 for &name_vertex in name_set {
3000 to_visit.push(name_vertex);
3001 }
3002 }
3003 }
3004
3005 let mut bounds_by_sheet: FxHashMap<SheetId, (u32, u32, u32, u32)> = FxHashMap::default();
3007 for &src in vertex_ids {
3008 let view = self.store.view(src);
3009 let sid = view.sheet_id();
3010 let r = view.row();
3011 let c = view.col();
3012 bounds_by_sheet
3013 .entry(sid)
3014 .and_modify(|b| {
3015 b.0 = b.0.min(r);
3016 b.1 = b.1.max(r);
3017 b.2 = b.2.min(c);
3018 b.3 = b.3.max(c);
3019 })
3020 .or_insert((r, r, c, c));
3021 }
3022
3023 for (sid, (sr, er, sc, ec)) in bounds_by_sheet {
3024 to_visit.extend(self.collect_range_dependents_for_rect(sid, sr, sc, er, ec));
3025 }
3026
3027 while let Some(id) = to_visit.pop() {
3028 if !visited_for_propagation.insert(id) {
3029 continue;
3030 }
3031 affected.insert(id);
3032 self.store.set_dirty(id, true);
3033 to_visit.extend(self.edges.in_edges(id));
3034 to_visit.extend(self.collect_range_dependents_for_vertex(id));
3035 }
3036
3037 self.dirty_vertices.extend(&affected);
3038 affected.into_iter().collect()
3039 }
3040
3041 fn collect_range_dependents_for_vertex(&self, vertex_id: VertexId) -> Vec<VertexId> {
3042 match self.store.kind(vertex_id) {
3043 VertexKind::Cell
3044 | VertexKind::Empty
3045 | VertexKind::FormulaScalar
3046 | VertexKind::FormulaArray => {
3047 let view = self.store.view(vertex_id);
3048 self.collect_range_dependents_for_rect(
3049 view.sheet_id(),
3050 view.row(),
3051 view.col(),
3052 view.row(),
3053 view.col(),
3054 )
3055 }
3056 _ => Vec::new(),
3057 }
3058 }
3059
3060 fn collect_range_dependents_for_rect(
3061 &self,
3062 sheet_id: SheetId,
3063 start_row: u32,
3064 start_col: u32,
3065 end_row: u32,
3066 end_col: u32,
3067 ) -> Vec<VertexId> {
3068 if self.stripe_to_dependents.is_empty() {
3069 return Vec::new();
3070 }
3071 let mut candidates: FxHashSet<VertexId> = FxHashSet::default();
3072
3073 for col in start_col..=end_col {
3074 let key = StripeKey {
3075 sheet_id,
3076 stripe_type: StripeType::Column,
3077 index: col,
3078 };
3079 if let Some(deps) = self.stripe_to_dependents.get(&key) {
3080 candidates.extend(deps);
3081 }
3082 }
3083 for row in start_row..=end_row {
3084 let key = StripeKey {
3085 sheet_id,
3086 stripe_type: StripeType::Row,
3087 index: row,
3088 };
3089 if let Some(deps) = self.stripe_to_dependents.get(&key) {
3090 candidates.extend(deps);
3091 }
3092 }
3093 if self.config.enable_block_stripes {
3094 let br0 = start_row / BLOCK_H;
3095 let br1 = end_row / BLOCK_H;
3096 let bc0 = start_col / BLOCK_W;
3097 let bc1 = end_col / BLOCK_W;
3098 for br in br0..=br1 {
3099 for bc in bc0..=bc1 {
3100 let key = StripeKey {
3101 sheet_id,
3102 stripe_type: StripeType::Block,
3103 index: block_index(br * BLOCK_H, bc * BLOCK_W),
3104 };
3105 if let Some(deps) = self.stripe_to_dependents.get(&key) {
3106 candidates.extend(deps);
3107 }
3108 }
3109 }
3110 }
3111
3112 let mut out: Vec<VertexId> = Vec::new();
3114 for dep_id in candidates {
3115 let Some(ranges) = self.formula_to_range_deps.get(&dep_id) else {
3116 continue;
3117 };
3118 let mut hit = false;
3119 for range in ranges {
3120 let range_sheet_id = match range.sheet {
3121 SharedSheetLocator::Id(id) => id,
3122 _ => sheet_id,
3123 };
3124 if range_sheet_id != sheet_id {
3125 continue;
3126 }
3127 let sr0 = range.start_row.map(|b| b.index).unwrap_or(0);
3128 let er0 = range.end_row.map(|b| b.index).unwrap_or(u32::MAX);
3129 let sc0 = range.start_col.map(|b| b.index).unwrap_or(0);
3130 let ec0 = range.end_col.map(|b| b.index).unwrap_or(u32::MAX);
3131 let overlap =
3132 sr0 <= end_row && er0 >= start_row && sc0 <= end_col && ec0 >= start_col;
3133 if overlap {
3134 hit = true;
3135 break;
3136 }
3137 }
3138 if hit {
3139 out.push(dep_id);
3140 }
3141 }
3142 out
3143 }
3144
3145 pub(crate) fn vertex_exists(&self, vertex_id: VertexId) -> bool {
3147 if vertex_id.0 < FIRST_NORMAL_VERTEX {
3148 return false;
3149 }
3150 let index = (vertex_id.0 - FIRST_NORMAL_VERTEX) as usize;
3151 index < self.store.len()
3152 }
3153
3154 pub(crate) fn get_vertex_kind(&self, vertex_id: VertexId) -> VertexKind {
3156 self.store.kind(vertex_id)
3157 }
3158
3159 pub(crate) fn get_vertex_sheet_id(&self, vertex_id: VertexId) -> SheetId {
3161 self.store.sheet_id(vertex_id)
3162 }
3163
3164 pub fn get_formula_id(&self, vertex_id: VertexId) -> Option<AstNodeId> {
3165 self.vertex_formulas.get(&vertex_id).copied()
3166 }
3167
3168 pub(crate) fn formula_vertices(&self) -> Vec<VertexId> {
3169 let mut vertices = self.vertex_formulas.keys().copied().collect::<Vec<_>>();
3170 vertices.sort_unstable();
3171 vertices
3172 }
3173
3174 pub fn get_formula_id_and_volatile(&self, vertex_id: VertexId) -> Option<(AstNodeId, bool)> {
3175 let ast_id = self.get_formula_id(vertex_id)?;
3176 Some((ast_id, self.is_volatile(vertex_id)))
3177 }
3178
3179 pub fn get_formula_node(&self, vertex_id: VertexId) -> Option<&super::arena::AstNodeData> {
3180 let ast_id = self.get_formula_id(vertex_id)?;
3181 self.data_store.get_node(ast_id)
3182 }
3183
3184 pub fn get_formula_node_and_volatile(
3185 &self,
3186 vertex_id: VertexId,
3187 ) -> Option<(&super::arena::AstNodeData, bool)> {
3188 let (ast_id, vol) = self.get_formula_id_and_volatile(vertex_id)?;
3189 let node = self.data_store.get_node(ast_id)?;
3190 Some((node, vol))
3191 }
3192
3193 pub fn get_formula(&self, vertex_id: VertexId) -> Option<ASTNode> {
3197 let ast_id = self.get_formula_id(vertex_id)?;
3198 self.data_store.retrieve_ast(ast_id, &self.sheet_reg)
3199 }
3200
3201 pub fn get_value(&self, vertex_id: VertexId) -> Option<LiteralValue> {
3203 if !self.value_cache_enabled {
3204 match self.store.kind(vertex_id) {
3207 VertexKind::Cell
3208 | VertexKind::FormulaScalar
3209 | VertexKind::FormulaArray
3210 | VertexKind::Empty => {
3211 #[cfg(debug_assertions)]
3212 {
3213 self.graph_value_read_attempts
3214 .fetch_add(1, Ordering::Relaxed);
3215 }
3216 return None;
3217 }
3218 _ => {
3219 }
3221 }
3222 }
3223 self.vertex_values
3224 .get(&vertex_id)
3225 .map(|&value_ref| self.data_store.retrieve_value(value_ref))
3226 }
3227
3228 pub(crate) fn get_cell_ref(&self, vertex_id: VertexId) -> Option<CellRef> {
3230 let packed_coord = self.store.coord(vertex_id);
3231 let sheet_id = self.store.sheet_id(vertex_id);
3232 let coord = Coord::new(packed_coord.row(), packed_coord.col(), true, true);
3233 Some(CellRef::new(sheet_id, coord))
3234 }
3235
3236 pub(crate) fn make_cell_ref_internal(&self, sheet_id: SheetId, row: u32, col: u32) -> CellRef {
3238 let coord = Coord::new(row, col, true, true);
3239 CellRef::new(sheet_id, coord)
3240 }
3241
3242 pub fn make_cell_ref(&self, sheet_name: &str, row: u32, col: u32) -> CellRef {
3244 let sheet_id = self.sheet_reg.get_id(sheet_name).unwrap_or(0);
3245 let coord = Coord::from_excel(row, col, true, true);
3246 CellRef::new(sheet_id, coord)
3247 }
3248
3249 pub(crate) fn is_dirty(&self, vertex_id: VertexId) -> bool {
3251 self.store.is_dirty(vertex_id)
3252 }
3253
3254 pub(crate) fn is_volatile(&self, vertex_id: VertexId) -> bool {
3256 self.store.is_volatile(vertex_id)
3257 }
3258
3259 pub(crate) fn is_dynamic(&self, vertex_id: VertexId) -> bool {
3260 self.store.is_dynamic(vertex_id)
3261 }
3262
3263 pub fn get_vertex_id_for_address(&self, addr: &CellRef) -> Option<&VertexId> {
3265 self.cell_to_vertex.get(addr)
3266 }
3267
3268 #[cfg(test)]
3269 pub fn cell_to_vertex(
3270 &self,
3271 ) -> &std::collections::HashMap<CellRef, VertexId, CoordBuildHasher> {
3272 &self.cell_to_vertex
3273 }
3274
3275 #[inline]
3279 pub(crate) fn dependencies_slice(&self, vertex_id: VertexId) -> Option<&[VertexId]> {
3280 self.edges.out_edges_ref(vertex_id)
3281 }
3282
3283 pub(crate) fn get_dependencies(&self, vertex_id: VertexId) -> Vec<VertexId> {
3285 self.edges.out_edges(vertex_id)
3286 }
3287
3288 pub(crate) fn has_self_loop(&self, vertex_id: VertexId) -> bool {
3290 if let Some(deps) = self.dependencies_slice(vertex_id) {
3291 deps.contains(&vertex_id)
3292 } else {
3293 self.edges.out_edges(vertex_id).contains(&vertex_id)
3294 }
3295 }
3296
3297 #[inline]
3301 pub(crate) fn dependents_slice(&self, vertex_id: VertexId) -> Option<&[VertexId]> {
3302 self.edges.in_edges_ref(vertex_id)
3303 }
3304
3305 pub(crate) fn get_dependents(&self, vertex_id: VertexId) -> Vec<VertexId> {
3308 if self.edges.delta_size() > 0 {
3311 #[cfg(test)]
3312 {
3313 if let Ok(mut g) = self.instr.lock() {
3316 g.dependents_scan_fallback_calls += 1;
3317 g.dependents_scan_vertices_scanned += self.cell_to_vertex.len() as u64;
3318 }
3319 }
3320 let mut dependents = Vec::new();
3322 for (&_addr, &vid) in &self.cell_to_vertex {
3323 let out_edges = self.edges.out_edges(vid);
3324 if out_edges.contains(&vertex_id) {
3325 dependents.push(vid);
3326 }
3327 }
3328 for named in self.named_ranges.values() {
3329 let vid = named.vertex;
3330 let out_edges = self.edges.out_edges(vid);
3331 if out_edges.contains(&vertex_id) {
3332 dependents.push(vid);
3333 }
3334 }
3335 for named in self.sheet_named_ranges.values() {
3336 let vid = named.vertex;
3337 let out_edges = self.edges.out_edges(vid);
3338 if out_edges.contains(&vertex_id) {
3339 dependents.push(vid);
3340 }
3341 }
3342 dependents
3343 } else {
3344 self.edges.in_edges(vertex_id).to_vec()
3346 }
3347 }
3348
3349 #[doc(hidden)]
3353 pub fn snapshot_vertex(&self, id: VertexId) -> crate::engine::VertexSnapshot {
3354 let coord = self.store.coord(id);
3355 let sheet_id = self.store.sheet_id(id);
3356 let kind = self.store.kind(id);
3357 let flags = self.store.flags(id);
3358
3359 let value_ref = self.vertex_values.get(&id).copied();
3361 let formula_ref = self.vertex_formulas.get(&id).copied();
3362
3363 let out_edges = self.get_dependencies(id);
3365
3366 crate::engine::VertexSnapshot {
3367 coord,
3368 sheet_id,
3369 kind,
3370 flags,
3371 value_ref,
3372 formula_ref,
3373 out_edges,
3374 }
3375 }
3376
3377 #[doc(hidden)]
3379 pub fn remove_all_edges(&mut self, id: VertexId) {
3380 self.edges.begin_batch();
3382
3383 self.remove_dependent_edges(id);
3385
3386 self.edges.rebuild();
3389
3390 let dependents = self.get_dependents(id);
3392 if self.pk_order.is_some()
3393 && let Some(mut pk) = self.pk_order.take()
3394 {
3395 for dependent in &dependents {
3396 pk.remove_edge(id, *dependent);
3397 }
3398 self.pk_order = Some(pk);
3399 }
3400 for dependent in dependents {
3401 self.edges.remove_edge(dependent, id);
3402 }
3403
3404 self.edges.end_batch();
3406 }
3407
3408 #[doc(hidden)]
3410 pub fn mark_as_ref_error(&mut self, id: VertexId) {
3411 if !self.value_cache_enabled {
3412 match self.store.kind(id) {
3413 VertexKind::Cell
3414 | VertexKind::FormulaScalar
3415 | VertexKind::FormulaArray
3416 | VertexKind::Empty => {
3417 self.ref_error_vertices.insert(id);
3418 self.vertex_values.remove(&id);
3421 let _ = self.mark_dirty(id);
3422 return;
3423 }
3424 _ => {
3425 }
3427 }
3428 }
3429 let error = LiteralValue::Error(ExcelError::new(ExcelErrorKind::Ref));
3430 let value_ref = self.data_store.store_value(error);
3431 self.vertex_values.insert(id, value_ref);
3432 let _ = self.mark_dirty(id);
3433 }
3434
3435 pub fn is_ref_error(&self, id: VertexId) -> bool {
3437 if !self.value_cache_enabled {
3438 match self.store.kind(id) {
3439 VertexKind::Cell
3440 | VertexKind::FormulaScalar
3441 | VertexKind::FormulaArray
3442 | VertexKind::Empty => {
3443 return self.ref_error_vertices.contains(&id);
3444 }
3445 _ => {
3446 }
3448 }
3449 }
3450 if let Some(value_ref) = self.vertex_values.get(&id) {
3451 let value = self.data_store.retrieve_value(*value_ref);
3452 if let LiteralValue::Error(err) = value {
3453 return err.kind == ExcelErrorKind::Ref;
3454 }
3455 }
3456 false
3457 }
3458
3459 #[doc(hidden)]
3461 pub fn mark_dependents_dirty(&mut self, id: VertexId) {
3462 let dependents = self.get_dependents(id);
3463 for dep_id in dependents {
3464 self.store.set_dirty(dep_id, true);
3465 self.dirty_vertices.insert(dep_id);
3466 }
3467 }
3468
3469 #[doc(hidden)]
3471 pub fn mark_volatile(&mut self, id: VertexId, volatile: bool) {
3472 self.store.set_volatile(id, volatile);
3473 if volatile {
3474 self.volatile_vertices.insert(id);
3475 } else {
3476 self.volatile_vertices.remove(&id);
3477 }
3478 }
3479
3480 #[doc(hidden)]
3482 pub fn set_coord(&mut self, id: VertexId, coord: AbsCoord) {
3483 self.store.set_coord(id, coord);
3484 }
3485
3486 #[doc(hidden)]
3488 pub fn update_edge_coord(&mut self, id: VertexId, coord: AbsCoord) {
3489 self.edges.update_coord(id, coord);
3490 }
3491
3492 #[doc(hidden)]
3494 pub fn mark_deleted(&mut self, id: VertexId, deleted: bool) {
3495 self.store.mark_deleted(id, deleted);
3496 }
3497
3498 #[doc(hidden)]
3500 pub fn set_kind(&mut self, id: VertexId, kind: VertexKind) {
3501 self.store.set_kind(id, kind);
3502 }
3503
3504 #[doc(hidden)]
3506 pub fn set_dirty(&mut self, id: VertexId, dirty: bool) {
3507 self.store.set_dirty(id, dirty);
3508 if dirty {
3509 self.dirty_vertices.insert(id);
3510 } else {
3511 self.dirty_vertices.remove(&id);
3512 }
3513 }
3514
3515 #[cfg(test)]
3517 pub(crate) fn get_kind(&self, id: VertexId) -> VertexKind {
3518 self.store.kind(id)
3519 }
3520
3521 #[cfg(test)]
3523 pub(crate) fn get_flags(&self, id: VertexId) -> u8 {
3524 self.store.flags(id)
3525 }
3526
3527 #[cfg(test)]
3529 pub(crate) fn is_deleted(&self, id: VertexId) -> bool {
3530 self.store.is_deleted(id)
3531 }
3532
3533 #[doc(hidden)]
3535 pub fn rebuild_edges(&mut self) {
3536 self.edges.rebuild();
3537 }
3538
3539 #[doc(hidden)]
3541 pub fn edges_delta_size(&self) -> usize {
3542 self.edges.delta_size()
3543 }
3544
3545 pub fn get_vertex_for_cell(&self, addr: &CellRef) -> Option<VertexId> {
3547 self.cell_to_vertex.get(addr).copied()
3548 }
3549
3550 pub fn get_coord(&self, id: VertexId) -> AbsCoord {
3552 self.store.coord(id)
3553 }
3554
3555 pub fn get_sheet_id(&self, id: VertexId) -> SheetId {
3557 self.store.sheet_id(id)
3558 }
3559
3560 pub fn vertices_in_sheet(&self, sheet_id: SheetId) -> impl Iterator<Item = VertexId> + '_ {
3562 self.store
3563 .all_vertices()
3564 .filter(move |&id| self.vertex_exists(id) && self.store.sheet_id(id) == sheet_id)
3565 }
3566
3567 pub fn vertex_has_formula(&self, id: VertexId) -> bool {
3569 self.vertex_formulas.contains_key(&id)
3570 }
3571
3572 pub fn vertices_with_formulas(&self) -> impl Iterator<Item = VertexId> + '_ {
3574 self.vertex_formulas.keys().copied()
3575 }
3576
3577 pub fn update_vertex_formula(&mut self, id: VertexId, ast: ASTNode) -> Result<(), ExcelError> {
3579 let sheet_id = self.store.sheet_id(id);
3581
3582 let has_ref_marker = ast.get_dependencies().into_iter().any(|r| {
3586 matches!(
3587 r,
3588 ReferenceType::Cell { sheet: Some(s), .. }
3589 | ReferenceType::Range { sheet: Some(s), .. } if s == "#REF"
3590 )
3591 });
3592 if has_ref_marker {
3593 let ast_id = self.data_store.store_ast(&ast, &self.sheet_reg);
3595 self.vertex_formulas.insert(id, ast_id);
3596 self.mark_as_ref_error(id);
3597 self.store.set_kind(id, VertexKind::FormulaScalar);
3598 return Ok(());
3599 }
3600
3601 let (new_dependencies, new_range_dependencies, _, named_dependencies) =
3603 self.extract_dependencies(&ast, sheet_id)?;
3604
3605 self.remove_dependent_edges(id);
3607 self.detach_vertex_from_names(id);
3608
3609 let ast_id = self.data_store.store_ast(&ast, &self.sheet_reg);
3611 self.vertex_formulas.insert(id, ast_id);
3612
3613 self.add_dependent_edges(id, &new_dependencies);
3615 self.add_range_dependent_edges(id, &new_range_dependencies, sheet_id);
3616
3617 if !named_dependencies.is_empty() {
3618 self.attach_vertex_to_names(id, &named_dependencies);
3619 }
3620
3621 self.store.set_kind(id, VertexKind::FormulaScalar);
3623
3624 Ok(())
3625 }
3626
3627 pub fn mark_vertex_dirty(&mut self, vertex_id: VertexId) {
3629 self.store.set_dirty(vertex_id, true);
3630 self.dirty_vertices.insert(vertex_id);
3631 }
3632
3633 pub fn mark_vertices_dirty_batch(&mut self, vertices: &[VertexId]) {
3635 self.dirty_vertices.reserve(vertices.len());
3636 for &vertex_id in vertices {
3637 self.store.set_dirty(vertex_id, true);
3638 }
3639 self.dirty_vertices.extend(vertices.iter().copied());
3640 }
3641
3642 pub fn update_cell_mapping(
3644 &mut self,
3645 id: VertexId,
3646 old_addr: Option<CellRef>,
3647 new_addr: CellRef,
3648 ) {
3649 if let Some(old) = old_addr {
3651 self.cell_to_vertex.remove(&old);
3652 }
3653 self.cell_to_vertex.insert(new_addr, id);
3655 }
3656
3657 pub fn remove_cell_mapping(&mut self, addr: &CellRef) {
3659 self.cell_to_vertex.remove(addr);
3660 }
3661
3662 pub fn get_cell_ref_for_vertex(&self, id: VertexId) -> Option<CellRef> {
3664 let coord = self.store.coord(id);
3665 let sheet_id = self.store.sheet_id(id);
3666 let cell_ref = CellRef::new(sheet_id, Coord::new(coord.row(), coord.col(), true, true));
3668 if self.cell_to_vertex.get(&cell_ref) == Some(&id) {
3670 Some(cell_ref)
3671 } else {
3672 None
3673 }
3674 }
3675
3676 pub(crate) fn rebuild_formula_dependencies(&mut self, vertex_id: VertexId, ast: &ASTNode) {
3682 let sheet_id = self.store.sheet_id(vertex_id);
3683
3684 self.remove_dependent_edges(vertex_id);
3686 self.detach_vertex_from_names(vertex_id);
3687 self.clear_pending_name_references(vertex_id);
3688
3689 let (
3690 new_dependencies,
3691 new_range_dependencies,
3692 _created_placeholders,
3693 named_dependencies,
3694 unresolved_names,
3695 ) = match self.extract_dependencies_with_pending_names(ast, sheet_id) {
3696 Ok(v) => v,
3697 Err(_) => {
3698 self.mark_as_ref_error(vertex_id);
3699 return;
3700 }
3701 };
3702
3703 if new_dependencies.contains(&vertex_id) {
3705 self.mark_as_ref_error(vertex_id);
3706 return;
3707 }
3708
3709 for &name_vertex in &named_dependencies {
3710 let mut visited = FxHashSet::default();
3711 if self.name_depends_on_vertex(name_vertex, vertex_id, &mut visited) {
3712 self.mark_as_ref_error(vertex_id);
3713 return;
3714 }
3715 }
3716
3717 self.ref_error_vertices.remove(&vertex_id);
3719 self.vertex_values.remove(&vertex_id);
3720
3721 if !named_dependencies.is_empty() {
3722 self.attach_vertex_to_names(vertex_id, &named_dependencies);
3723 }
3724 for unresolved_name in &unresolved_names {
3725 self.record_pending_name_reference(sheet_id, unresolved_name, vertex_id);
3726 }
3727
3728 self.add_dependent_edges(vertex_id, &new_dependencies);
3729 self.add_range_dependent_edges(vertex_id, &new_range_dependencies, sheet_id);
3730 let _ = self.mark_dirty(vertex_id);
3731 }
3732}
3733
3734