1use crate::SheetId;
2use crate::engine::TombstoneRegistry;
3use crate::engine::named_range::{NameScope, NamedDefinition, NamedRange};
4use crate::engine::sheet_registry::SheetRegistry;
5use formualizer_common::{
6 CoordBuildHasher, ExcelError, ExcelErrorKind, LiteralValue, PackedSheetCell,
7};
8use formualizer_parse::parser::{ASTNode, ASTNodeType, ReferenceType};
9use rustc_hash::{FxHashMap, FxHashSet};
10
11#[cfg(debug_assertions)]
12use std::sync::atomic::{AtomicU64, Ordering};
13
14#[cfg(test)]
15#[derive(Debug, Default, Clone)]
16pub struct GraphInstrumentation {
17 pub edges_added: u64,
18 pub stripe_inserts: u64,
19 pub stripe_removes: u64,
20 pub dependents_scan_fallback_calls: u64,
21 pub dependents_scan_vertices_scanned: u64,
22}
23
24mod ast_utils;
25pub mod editor;
26mod formula_analysis;
27mod names;
28mod range_deps;
29mod sheets;
30pub mod snapshot;
31mod sources;
32mod tables;
33
34use super::arena::{AstNodeId, DataStore, ValueRef};
35use super::delta_edges::CsrMutableEdges;
36use super::sheet_index::SheetIndex;
37use super::vertex::{VertexId, VertexKind};
38use super::vertex_store::{FIRST_NORMAL_VERTEX, VertexStore};
39use crate::engine::topo::{
40 GraphAdapter,
41 pk::{DynamicTopo, PkConfig},
42};
43use crate::reference::{CellRef, Coord, SharedRangeRef, SharedRef, SharedSheetLocator};
44use formualizer_common::Coord as AbsCoord;
45#[inline]
48fn normalize_stored_literal(value: LiteralValue) -> LiteralValue {
49 match value {
50 LiteralValue::Int(i) => LiteralValue::Number(i as f64),
52 other => other,
53 }
54}
55
56pub use editor::change_log::{ChangeEvent, ChangeLog};
57
58#[derive(Debug, Clone, PartialEq, Eq, Hash)]
62pub enum DependencyRef {
63 Cell(VertexId),
65 Range {
67 sheet: String,
68 start_row: u32,
69 start_col: u32,
70 end_row: u32, end_col: u32, },
73 WholeColumn { sheet: String, col: u32 },
75 WholeRow { sheet: String, row: u32 },
77}
78
79#[derive(Debug, Clone, Hash, PartialEq, Eq)]
81pub struct StripeKey {
82 pub sheet_id: SheetId,
83 pub stripe_type: StripeType,
84 pub index: u32, }
86
87#[derive(Debug, Clone, Hash, PartialEq, Eq)]
88pub enum StripeType {
89 Row,
90 Column,
91 Block, }
93
94const BLOCK_H: u32 = 256;
96const BLOCK_W: u32 = 256;
97
98pub fn block_index(row: u32, col: u32) -> u32 {
99 (row / BLOCK_H) << 16 | (col / BLOCK_W)
100}
101
102#[derive(Debug, Clone)]
105pub struct OperationSummary {
106 pub affected_vertices: Vec<VertexId>,
108 pub created_placeholders: Vec<CellRef>,
110}
111
112#[derive(Debug)]
114pub struct DependencyGraph {
115 store: VertexStore,
117
118 edges: CsrMutableEdges,
120
121 data_store: DataStore,
123 vertex_values: FxHashMap<VertexId, ValueRef>,
124 vertex_formulas: FxHashMap<VertexId, AstNodeId>,
125
126 value_cache_enabled: bool,
131
132 #[cfg(debug_assertions)]
135 graph_value_read_attempts: AtomicU64,
136
137 cell_to_vertex: std::collections::HashMap<CellRef, VertexId, CoordBuildHasher>,
141 load_packed_to_vertex: std::collections::HashMap<PackedSheetCell, VertexId, CoordBuildHasher>,
142
143 dirty_vertices: FxHashSet<VertexId>,
145 volatile_vertices: FxHashSet<VertexId>,
146
147 ref_error_vertices: FxHashSet<VertexId>,
153
154 formula_to_range_deps: FxHashMap<VertexId, Vec<SharedRangeRef<'static>>>,
157
158 stripe_to_dependents: FxHashMap<StripeKey, FxHashSet<VertexId>>,
161
162 sheet_indexes: FxHashMap<SheetId, SheetIndex>,
165
166 sheet_reg: SheetRegistry,
168 default_sheet_id: SheetId,
169
170 named_ranges: FxHashMap<String, NamedRange>,
173
174 named_ranges_lookup: FxHashMap<String, String>,
179
180 sheet_named_ranges: FxHashMap<(SheetId, String), NamedRange>,
182
183 sheet_named_ranges_lookup: FxHashMap<(SheetId, String), String>,
188
189 vertex_to_names: FxHashMap<VertexId, Vec<VertexId>>,
191
192 name_vertex_lookup: FxHashMap<VertexId, (NameScope, String)>,
194
195 pending_name_links: FxHashMap<String, FxHashSet<(SheetId, VertexId)>>,
200
201 vertex_to_pending_names: FxHashMap<VertexId, FxHashSet<String>>,
204
205 tables: FxHashMap<String, tables::TableEntry>,
207 tables_lookup: FxHashMap<String, String>,
209 table_vertex_lookup: FxHashMap<VertexId, String>,
210
211 source_scalars: FxHashMap<String, sources::SourceScalarEntry>,
213 source_tables: FxHashMap<String, sources::SourceTableEntry>,
214 source_vertex_lookup: FxHashMap<VertexId, String>,
215
216 name_vertex_seq: u32,
218
219 source_vertex_seq: u32,
221
222 cell_to_name_dependents: FxHashMap<VertexId, FxHashSet<VertexId>>,
224 name_to_cell_dependencies: FxHashMap<VertexId, Vec<VertexId>>,
226
227 config: super::EvalConfig,
229
230 pk_order: Option<DynamicTopo<VertexId>>,
232
233 spill_anchor_to_cells: FxHashMap<VertexId, Vec<CellRef>>,
237 spill_cell_to_anchor: std::collections::HashMap<CellRef, VertexId, CoordBuildHasher>,
238
239 first_load_assume_new: bool,
241 ensure_touched_sheets: FxHashSet<SheetId>,
242
243 pub tombstone_registry: TombstoneRegistry,
245
246 #[cfg(test)]
247 instr: std::sync::Mutex<GraphInstrumentation>,
248}
249
250impl Default for DependencyGraph {
251 fn default() -> Self {
252 Self::new()
253 }
254}
255
256impl DependencyGraph {
257 pub fn range_expansion_limit(&self) -> usize {
259 self.config.range_expansion_limit
260 }
261
262 pub fn get_config(&self) -> &super::EvalConfig {
263 &self.config
264 }
265
266 #[inline]
267 pub(crate) fn value_cache_enabled(&self) -> bool {
268 self.value_cache_enabled
269 }
270
271 #[cfg(test)]
275 pub fn debug_graph_value_read_attempts(&self) -> u64 {
276 #[cfg(debug_assertions)]
277 {
278 self.graph_value_read_attempts.load(Ordering::Relaxed)
279 }
280 #[cfg(not(debug_assertions))]
281 {
282 0
283 }
284 }
285
286 pub fn plan_dependencies<'a, I>(
288 &mut self,
289 items: I,
290 policy: &formualizer_parse::parser::CollectPolicy,
291 volatile: Option<&[bool]>,
292 ) -> Result<crate::engine::plan::DependencyPlan, formualizer_common::ExcelError>
293 where
294 I: IntoIterator<Item = (&'a str, u32, u32, &'a formualizer_parse::parser::ASTNode)>,
295 {
296 crate::engine::plan::build_dependency_plan(
297 &mut self.sheet_reg,
298 items.into_iter(),
299 policy,
300 volatile,
301 )
302 }
303
304 pub fn ensure_vertices_batch(
307 &mut self,
308 coords: &[(SheetId, AbsCoord)],
309 ) -> Vec<(AbsCoord, u32)> {
310 self.ensure_vertices_batch_ordered(coords).1
311 }
312
313 pub fn ensure_vertices_batch_packed_ordered(
317 &mut self,
318 packed_cells: &[PackedSheetCell],
319 ) -> (Vec<VertexId>, Vec<(AbsCoord, u32)>) {
320 #[cfg(feature = "perf_instrumentation")]
321 use crate::instant::FzInstant as PerfInstant;
322 use rustc_hash::FxHashMap;
323
324 #[cfg(feature = "perf_instrumentation")]
325 let debug = std::env::var("FZ_DEBUG_LOAD")
326 .ok()
327 .is_some_and(|v| v != "0");
328 #[cfg(feature = "perf_instrumentation")]
329 let t0 = PerfInstant::now();
330
331 let mut ordered: Vec<Option<VertexId>> = vec![None; packed_cells.len()];
332 if packed_cells.is_empty() {
333 return (Vec::new(), Vec::new());
334 }
335
336 let first_sid = packed_cells[0].sheet_id();
337 let single_sheet = packed_cells.iter().all(|cell| cell.sheet_id() == first_sid);
338 let mut add_batch: Vec<(AbsCoord, u32)> = Vec::new();
339
340 #[cfg(feature = "perf_instrumentation")]
341 let mut packed_hits = 0usize;
342 #[cfg(feature = "perf_instrumentation")]
343 let mut generic_hits = 0usize;
344 #[cfg(feature = "perf_instrumentation")]
345 let mut missing = 0usize;
346 #[cfg(feature = "perf_instrumentation")]
347 let mut t_packed_lookup_us = 0u128;
348 #[cfg(feature = "perf_instrumentation")]
349 let mut t_generic_lookup_us = 0u128;
350 #[cfg(feature = "perf_instrumentation")]
351 let mut t_alloc_us = 0u128;
352 #[cfg(feature = "perf_instrumentation")]
353 let mut t_map_insert_us = 0u128;
354 #[cfg(feature = "perf_instrumentation")]
355 let mut t_index_insert_us = 0u128;
356 #[cfg(feature = "perf_instrumentation")]
357 let mut t_edge_register_us = 0u128;
358
359 if single_sheet {
360 let sid = first_sid;
361 let mut missing_items: Vec<(usize, PackedSheetCell)> =
362 Vec::with_capacity(packed_cells.len());
363
364 for (idx, packed) in packed_cells.iter().copied().enumerate() {
365 #[cfg(feature = "perf_instrumentation")]
366 let tl0 = PerfInstant::now();
367 if self.first_load_assume_new
368 && let Some(&existing) = self.load_packed_to_vertex.get(&packed)
369 {
370 ordered[idx] = Some(existing);
371 #[cfg(feature = "perf_instrumentation")]
372 {
373 packed_hits += 1;
374 t_packed_lookup_us += tl0.elapsed().as_micros();
375 }
376 continue;
377 }
378 #[cfg(feature = "perf_instrumentation")]
379 {
380 t_packed_lookup_us += tl0.elapsed().as_micros();
381 }
382
383 let pc = AbsCoord::new(packed.row0(), packed.col0());
384 let addr = CellRef::new(sid, Coord::new(pc.row(), pc.col(), true, true));
385 #[cfg(feature = "perf_instrumentation")]
386 let tg0 = PerfInstant::now();
387 if let Some(&existing) = self.cell_to_vertex.get(&addr) {
388 ordered[idx] = Some(existing);
389 if self.first_load_assume_new {
390 self.load_packed_to_vertex.insert(packed, existing);
391 }
392 #[cfg(feature = "perf_instrumentation")]
393 {
394 generic_hits += 1;
395 }
396 } else {
397 missing_items.push((idx, packed));
398 #[cfg(feature = "perf_instrumentation")]
399 {
400 missing += 1;
401 }
402 }
403 #[cfg(feature = "perf_instrumentation")]
404 {
405 t_generic_lookup_us += tg0.elapsed().as_micros();
406 }
407 }
408
409 if !missing_items.is_empty() {
410 self.ensure_touched_sheets.insert(sid);
411
412 let mut pcs: Vec<AbsCoord> = Vec::with_capacity(missing_items.len());
413 for (_, packed) in &missing_items {
414 pcs.push(AbsCoord::new(packed.row0(), packed.col0()));
415 }
416
417 #[cfg(feature = "perf_instrumentation")]
418 let ta0 = PerfInstant::now();
419 let vids = self.store.allocate_contiguous(sid, &pcs, 0x00);
420 #[cfg(feature = "perf_instrumentation")]
421 {
422 t_alloc_us += ta0.elapsed().as_micros();
423 }
424 add_batch.reserve(missing_items.len());
425
426 match self.config.sheet_index_mode {
427 crate::engine::SheetIndexMode::Eager
428 | crate::engine::SheetIndexMode::FastBatch => {
429 for ((input_idx, packed), vid) in
430 missing_items.into_iter().zip(vids.into_iter())
431 {
432 let pc = AbsCoord::new(packed.row0(), packed.col0());
433 ordered[input_idx] = Some(vid);
434 add_batch.push((pc, vid.0));
435
436 #[cfg(feature = "perf_instrumentation")]
437 let tm0 = PerfInstant::now();
438 if self.first_load_assume_new {
439 self.load_packed_to_vertex.insert(packed, vid);
440 } else {
441 let addr =
442 CellRef::new(sid, Coord::new(pc.row(), pc.col(), true, true));
443 self.cell_to_vertex.insert(addr, vid);
444 }
445 #[cfg(feature = "perf_instrumentation")]
446 {
447 t_map_insert_us += tm0.elapsed().as_micros();
448 }
449
450 #[cfg(feature = "perf_instrumentation")]
451 let ti0 = PerfInstant::now();
452 self.sheet_index_mut(sid).add_vertex(pc, vid);
453 #[cfg(feature = "perf_instrumentation")]
454 {
455 t_index_insert_us += ti0.elapsed().as_micros();
456 }
457 }
458 }
459 crate::engine::SheetIndexMode::Lazy => {
460 for ((input_idx, packed), vid) in
461 missing_items.into_iter().zip(vids.into_iter())
462 {
463 let pc = AbsCoord::new(packed.row0(), packed.col0());
464 ordered[input_idx] = Some(vid);
465 add_batch.push((pc, vid.0));
466
467 #[cfg(feature = "perf_instrumentation")]
468 let tm0 = PerfInstant::now();
469 if self.first_load_assume_new {
470 self.load_packed_to_vertex.insert(packed, vid);
471 } else {
472 let addr =
473 CellRef::new(sid, Coord::new(pc.row(), pc.col(), true, true));
474 self.cell_to_vertex.insert(addr, vid);
475 }
476 #[cfg(feature = "perf_instrumentation")]
477 {
478 t_map_insert_us += tm0.elapsed().as_micros();
479 }
480 }
481 }
482 }
483 }
484 } else {
485 let mut grouped: FxHashMap<SheetId, Vec<(usize, PackedSheetCell)>> =
486 FxHashMap::default();
487
488 for (idx, packed) in packed_cells.iter().copied().enumerate() {
489 #[cfg(feature = "perf_instrumentation")]
490 let tl0 = PerfInstant::now();
491 if self.first_load_assume_new
492 && let Some(&existing) = self.load_packed_to_vertex.get(&packed)
493 {
494 ordered[idx] = Some(existing);
495 #[cfg(feature = "perf_instrumentation")]
496 {
497 packed_hits += 1;
498 t_packed_lookup_us += tl0.elapsed().as_micros();
499 }
500 continue;
501 }
502 #[cfg(feature = "perf_instrumentation")]
503 {
504 t_packed_lookup_us += tl0.elapsed().as_micros();
505 }
506
507 let sid = packed.sheet_id();
508 let pc = AbsCoord::new(packed.row0(), packed.col0());
509 let addr = CellRef::new(sid, Coord::new(pc.row(), pc.col(), true, true));
510 #[cfg(feature = "perf_instrumentation")]
511 let tg0 = PerfInstant::now();
512 if let Some(&existing) = self.cell_to_vertex.get(&addr) {
513 ordered[idx] = Some(existing);
514 if self.first_load_assume_new {
515 self.load_packed_to_vertex.insert(packed, existing);
516 }
517 #[cfg(feature = "perf_instrumentation")]
518 {
519 generic_hits += 1;
520 }
521 } else {
522 grouped.entry(sid).or_default().push((idx, packed));
523 #[cfg(feature = "perf_instrumentation")]
524 {
525 missing += 1;
526 }
527 }
528 #[cfg(feature = "perf_instrumentation")]
529 {
530 t_generic_lookup_us += tg0.elapsed().as_micros();
531 }
532 }
533
534 for (sid, items) in grouped {
535 if items.is_empty() {
536 continue;
537 }
538 self.ensure_touched_sheets.insert(sid);
539
540 let mut pcs: Vec<AbsCoord> = Vec::with_capacity(items.len());
541 for (_, packed) in &items {
542 pcs.push(AbsCoord::new(packed.row0(), packed.col0()));
543 }
544
545 #[cfg(feature = "perf_instrumentation")]
546 let ta0 = PerfInstant::now();
547 let vids = self.store.allocate_contiguous(sid, &pcs, 0x00);
548 #[cfg(feature = "perf_instrumentation")]
549 {
550 t_alloc_us += ta0.elapsed().as_micros();
551 }
552
553 for ((input_idx, packed), vid) in items.into_iter().zip(vids.into_iter()) {
554 let pc = AbsCoord::new(packed.row0(), packed.col0());
555 ordered[input_idx] = Some(vid);
556 add_batch.push((pc, vid.0));
557
558 #[cfg(feature = "perf_instrumentation")]
559 let tm0 = PerfInstant::now();
560 if self.first_load_assume_new {
561 self.load_packed_to_vertex.insert(packed, vid);
562 } else {
563 let addr = CellRef::new(sid, Coord::new(pc.row(), pc.col(), true, true));
564 self.cell_to_vertex.insert(addr, vid);
565 }
566 #[cfg(feature = "perf_instrumentation")]
567 {
568 t_map_insert_us += tm0.elapsed().as_micros();
569 }
570
571 match self.config.sheet_index_mode {
572 crate::engine::SheetIndexMode::Eager
573 | crate::engine::SheetIndexMode::FastBatch => {
574 #[cfg(feature = "perf_instrumentation")]
575 let ti0 = PerfInstant::now();
576 self.sheet_index_mut(sid).add_vertex(pc, vid);
577 #[cfg(feature = "perf_instrumentation")]
578 {
579 t_index_insert_us += ti0.elapsed().as_micros();
580 }
581 }
582 crate::engine::SheetIndexMode::Lazy => {
583 }
585 }
586 }
587 }
588 }
589
590 if !add_batch.is_empty() {
591 #[cfg(feature = "perf_instrumentation")]
592 let te0 = PerfInstant::now();
593 self.edges.add_vertices_batch(&add_batch);
594 #[cfg(feature = "perf_instrumentation")]
595 {
596 t_edge_register_us += te0.elapsed().as_micros();
597 }
598 }
599
600 #[cfg(feature = "perf_instrumentation")]
601 if debug {
602 eprintln!(
603 "[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",
604 packed_cells.len(),
605 single_sheet,
606 packed_hits,
607 generic_hits,
608 missing,
609 t_packed_lookup_us,
610 t_generic_lookup_us,
611 t_alloc_us,
612 t_map_insert_us,
613 t_index_insert_us,
614 t_edge_register_us,
615 t0.elapsed().as_millis(),
616 );
617 }
618
619 let ordered = ordered
620 .into_iter()
621 .map(|vid| vid.expect("ensure_vertices_batch_packed_ordered must resolve every coord"))
622 .collect();
623 (ordered, add_batch)
624 }
625
626 pub fn ensure_vertices_batch_ordered(
629 &mut self,
630 coords: &[(SheetId, AbsCoord)],
631 ) -> (Vec<VertexId>, Vec<(AbsCoord, u32)>) {
632 let mut packed: Vec<PackedSheetCell> = Vec::with_capacity(coords.len());
633 for &(sid, coord) in coords {
634 packed.push(Self::packed_cell_key(sid, coord));
635 }
636 self.ensure_vertices_batch_packed_ordered(&packed)
637 }
638
639 #[inline]
640 fn packed_cell_key(sheet_id: SheetId, coord: AbsCoord) -> PackedSheetCell {
641 PackedSheetCell::try_new(sheet_id, coord.row(), coord.col())
642 .expect("graph coordinate must fit PackedSheetCell")
643 }
644
645 fn flush_load_packed_mappings(&mut self) {
646 if self.load_packed_to_vertex.is_empty() {
647 return;
648 }
649 let debug = std::env::var("FZ_DEBUG_LOAD")
650 .ok()
651 .is_some_and(|v| v != "0");
652 let t0 = crate::instant::FzInstant::now();
653 let count = self.load_packed_to_vertex.len();
654 self.cell_to_vertex.reserve(count);
655 for (&packed, &vid) in &self.load_packed_to_vertex {
656 let coord = AbsCoord::new(packed.row0(), packed.col0());
657 let addr = CellRef::new(
658 packed.sheet_id(),
659 Coord::new(coord.row(), coord.col(), true, true),
660 );
661 self.cell_to_vertex.insert(addr, vid);
662 }
663 self.load_packed_to_vertex.clear();
664 if debug {
665 eprintln!(
666 "[fz][load] flush_load_packed_mappings: {} entries in {:.1} ms",
667 count,
668 t0.elapsed().as_secs_f64() * 1000.0,
669 );
670 }
671 }
672
673 pub fn set_first_load_assume_new(&mut self, enabled: bool) {
675 if self.first_load_assume_new && !enabled {
676 self.flush_load_packed_mappings();
677 } else if enabled {
678 self.load_packed_to_vertex.clear();
679 }
680 self.first_load_assume_new = enabled;
681 }
682
683 pub fn first_load_assume_new(&self) -> bool {
684 self.first_load_assume_new
685 }
686
687 pub fn reset_ensure_touched(&mut self) {
689 self.ensure_touched_sheets.clear();
690 }
691
692 pub fn store_asts_batch<'a, I>(&mut self, asts: I) -> Vec<AstNodeId>
694 where
695 I: IntoIterator<Item = &'a formualizer_parse::parser::ASTNode>,
696 {
697 self.data_store.store_asts_batch(asts, &self.sheet_reg)
698 }
699
700 pub fn reserve_formula_metadata(&mut self, additional: usize) {
702 self.vertex_formulas.reserve(additional);
703 self.dirty_vertices.reserve(additional);
704 self.volatile_vertices.reserve(additional);
705 }
706
707 pub fn vid_for_sid_pc(&self, sid: SheetId, pc: AbsCoord) -> Option<VertexId> {
709 let addr = CellRef::new(sid, Coord::new(pc.row(), pc.col(), true, true));
710 self.cell_to_vertex.get(&addr).copied()
711 }
712
713 pub fn vid_for_plan_idx(
715 &self,
716 plan: &crate::engine::plan::DependencyPlan,
717 idx: u32,
718 ) -> Option<VertexId> {
719 let (sid, pc) = plan.global_cells.get(idx as usize).copied()?;
720 self.vid_for_sid_pc(sid, pc)
721 }
722 pub fn assign_formula_vertex(
724 &mut self,
725 vid: VertexId,
726 ast_id: AstNodeId,
727 volatile: bool,
728 dynamic: bool,
729 ) {
730 if self.vertex_formulas.contains_key(&vid) {
731 self.remove_dependent_edges(vid);
732 }
733 self.store
734 .set_kind(vid, crate::engine::vertex::VertexKind::FormulaScalar);
735 self.vertex_values.remove(&vid);
736 self.vertex_formulas.insert(vid, ast_id);
737 self.mark_volatile(vid, volatile);
738 self.store.set_dynamic(vid, dynamic);
739
740 self.mark_vertex_dirty(vid);
742 }
743
744 pub fn assign_formula_vertex_load_fast(
747 &mut self,
748 vid: VertexId,
749 ast_id: AstNodeId,
750 volatile: bool,
751 dynamic: bool,
752 ) {
753 debug_assert!(
754 !self.vertex_formulas.contains_key(&vid),
755 "load-fast formula assignment expects fresh/non-formula vertices"
756 );
757 self.store
758 .set_kind(vid, crate::engine::vertex::VertexKind::FormulaScalar);
759 self.vertex_values.remove(&vid);
760 self.vertex_formulas.insert(vid, ast_id);
761 self.mark_volatile(vid, volatile);
762 self.store.set_dynamic(vid, dynamic);
763 }
764
765 pub fn add_edges_nobatch(&mut self, dependent: VertexId, dependencies: &[VertexId]) {
767 self.add_dependent_edges_nobatch(dependent, dependencies);
768 }
769
770 pub fn iter_vertex_ids(&self) -> impl Iterator<Item = VertexId> + '_ {
772 self.store.all_vertices()
773 }
774
775 pub fn vertex_coord(&self, vid: VertexId) -> AbsCoord {
777 self.store.coord(vid)
778 }
779
780 pub fn vertex_count(&self) -> usize {
782 self.store.len()
783 }
784
785 pub fn build_edges_from_adjacency(
787 &mut self,
788 adjacency: Vec<(u32, Vec<u32>)>,
789 coords: Vec<AbsCoord>,
790 vertex_ids: Vec<u32>,
791 ) {
792 self.edges
793 .build_from_adjacency(adjacency, coords, vertex_ids);
794 }
795 pub fn used_row_bounds_for_columns(
797 &self,
798 sheet_id: SheetId,
799 start_col: u32,
800 end_col: u32,
801 ) -> Option<(u32, u32)> {
802 if let Some(index) = self.sheet_indexes.get(&sheet_id)
804 && !index.is_empty()
805 {
806 let mut min_r: Option<u32> = None;
807 let mut max_r: Option<u32> = None;
808 for vid in index.vertices_in_col_range(start_col, end_col) {
809 let r = self.store.coord(vid).row();
810 min_r = Some(min_r.map(|m| m.min(r)).unwrap_or(r));
811 max_r = Some(max_r.map(|m| m.max(r)).unwrap_or(r));
812 }
813 return match (min_r, max_r) {
814 (Some(a), Some(b)) => Some((a, b)),
815 _ => None,
816 };
817 }
818 let mut min_r: Option<u32> = None;
820 let mut max_r: Option<u32> = None;
821 for cref in self.cell_to_vertex.keys() {
822 if cref.sheet_id == sheet_id {
823 let c = cref.coord.col();
824 if c >= start_col && c <= end_col {
825 let r = cref.coord.row();
826 min_r = Some(min_r.map(|m| m.min(r)).unwrap_or(r));
827 max_r = Some(max_r.map(|m| m.max(r)).unwrap_or(r));
828 }
829 }
830 }
831 for packed in self.load_packed_to_vertex.keys() {
832 if packed.sheet_id() == sheet_id {
833 let c = packed.col0();
834 if c >= start_col && c <= end_col {
835 let r = packed.row0();
836 min_r = Some(min_r.map(|m| m.min(r)).unwrap_or(r));
837 max_r = Some(max_r.map(|m| m.max(r)).unwrap_or(r));
838 }
839 }
840 }
841 match (min_r, max_r) {
842 (Some(a), Some(b)) => Some((a, b)),
843 _ => None,
844 }
845 }
846
847 pub fn finalize_sheet_index(&mut self, sheet: &str) {
849 let Some(sheet_id) = self.sheet_reg.get_id(sheet) else {
850 return;
851 };
852 if let Some(idx) = self.sheet_indexes.get(&sheet_id)
854 && !idx.is_empty()
855 {
856 return;
857 }
858 let mut idx = SheetIndex::new();
859 let mut batch: Vec<(AbsCoord, VertexId)> =
861 Vec::with_capacity(self.cell_to_vertex.len() + self.load_packed_to_vertex.len());
862 for (cref, vid) in &self.cell_to_vertex {
863 if cref.sheet_id == sheet_id {
864 batch.push((AbsCoord::new(cref.coord.row(), cref.coord.col()), *vid));
865 }
866 }
867 for (&packed, &vid) in &self.load_packed_to_vertex {
868 if packed.sheet_id() != sheet_id {
869 continue;
870 }
871 let coord = AbsCoord::new(packed.row0(), packed.col0());
872 let addr = CellRef::new(sheet_id, Coord::new(coord.row(), coord.col(), true, true));
873 if self.cell_to_vertex.contains_key(&addr) {
874 continue;
875 }
876 batch.push((coord, vid));
877 }
878 idx.add_vertices_batch(&batch);
880 self.sheet_indexes.insert(sheet_id, idx);
881 }
882
883 pub fn set_sheet_index_mode(&mut self, mode: crate::engine::SheetIndexMode) {
884 self.config.sheet_index_mode = mode;
885 }
886
887 pub fn used_col_bounds_for_rows(
889 &self,
890 sheet_id: SheetId,
891 start_row: u32,
892 end_row: u32,
893 ) -> Option<(u32, u32)> {
894 if let Some(index) = self.sheet_indexes.get(&sheet_id)
895 && !index.is_empty()
896 {
897 let mut min_c: Option<u32> = None;
898 let mut max_c: Option<u32> = None;
899 for vid in index.vertices_in_row_range(start_row, end_row) {
900 let c = self.store.coord(vid).col();
901 min_c = Some(min_c.map(|m| m.min(c)).unwrap_or(c));
902 max_c = Some(max_c.map(|m| m.max(c)).unwrap_or(c));
903 }
904 return match (min_c, max_c) {
905 (Some(a), Some(b)) => Some((a, b)),
906 _ => None,
907 };
908 }
909 let mut min_c: Option<u32> = None;
911 let mut max_c: Option<u32> = None;
912 for cref in self.cell_to_vertex.keys() {
913 if cref.sheet_id == sheet_id {
914 let r = cref.coord.row();
915 if r >= start_row && r <= end_row {
916 let c = cref.coord.col();
917 min_c = Some(min_c.map(|m| m.min(c)).unwrap_or(c));
918 max_c = Some(max_c.map(|m| m.max(c)).unwrap_or(c));
919 }
920 }
921 }
922 for packed in self.load_packed_to_vertex.keys() {
923 if packed.sheet_id() == sheet_id {
924 let r = packed.row0();
925 if r >= start_row && r <= end_row {
926 let c = packed.col0();
927 min_c = Some(min_c.map(|m| m.min(c)).unwrap_or(c));
928 max_c = Some(max_c.map(|m| m.max(c)).unwrap_or(c));
929 }
930 }
931 }
932 match (min_c, max_c) {
933 (Some(a), Some(b)) => Some((a, b)),
934 _ => None,
935 }
936 }
937
938 pub fn sheet_has_formulas(&self, sheet_id: SheetId) -> bool {
940 for &vid in self.vertex_formulas.keys() {
942 if self.store.sheet_id(vid) == sheet_id {
943 return true;
944 }
945 }
946 false
947 }
948 pub fn new() -> Self {
949 Self::new_with_config(super::EvalConfig::default())
950 }
951
952 pub fn new_with_config(config: super::EvalConfig) -> Self {
953 let mut sheet_reg = SheetRegistry::new();
954 let default_sheet_id = sheet_reg.id_for(&config.default_sheet_name);
955
956 let mut g = Self {
957 store: VertexStore::new(),
958 edges: CsrMutableEdges::new(),
959 data_store: DataStore::new(),
960 vertex_values: FxHashMap::default(),
961 vertex_formulas: FxHashMap::default(),
962 value_cache_enabled: false,
965 #[cfg(debug_assertions)]
966 graph_value_read_attempts: AtomicU64::new(0),
967 cell_to_vertex: std::collections::HashMap::with_hasher(CoordBuildHasher),
968 load_packed_to_vertex: std::collections::HashMap::with_hasher(CoordBuildHasher),
969 dirty_vertices: FxHashSet::default(),
970 volatile_vertices: FxHashSet::default(),
971 ref_error_vertices: FxHashSet::default(),
972 formula_to_range_deps: FxHashMap::default(),
973 stripe_to_dependents: FxHashMap::default(),
974 sheet_indexes: FxHashMap::default(),
975 sheet_reg,
976 default_sheet_id,
977 named_ranges: FxHashMap::default(),
978 named_ranges_lookup: FxHashMap::default(),
979 sheet_named_ranges: FxHashMap::default(),
980 sheet_named_ranges_lookup: FxHashMap::default(),
981 vertex_to_names: FxHashMap::default(),
982 name_vertex_lookup: FxHashMap::default(),
983 pending_name_links: FxHashMap::default(),
984 vertex_to_pending_names: FxHashMap::default(),
985 tables: FxHashMap::default(),
986 tables_lookup: FxHashMap::default(),
987 table_vertex_lookup: FxHashMap::default(),
988 source_scalars: FxHashMap::default(),
989 source_tables: FxHashMap::default(),
990 source_vertex_lookup: FxHashMap::default(),
991 name_vertex_seq: 0,
992 source_vertex_seq: 0,
993 cell_to_name_dependents: FxHashMap::default(),
994 name_to_cell_dependencies: FxHashMap::default(),
995 config: config.clone(),
996 pk_order: None,
997 spill_anchor_to_cells: FxHashMap::default(),
998 spill_cell_to_anchor: std::collections::HashMap::with_hasher(CoordBuildHasher),
999 first_load_assume_new: false,
1000 ensure_touched_sheets: FxHashSet::default(),
1001 tombstone_registry: TombstoneRegistry::default(),
1002 #[cfg(test)]
1003 instr: std::sync::Mutex::new(GraphInstrumentation::default()),
1004 };
1005
1006 if config.use_dynamic_topo {
1007 let nodes = g
1009 .store
1010 .all_vertices()
1011 .filter(|&id| g.store.vertex_exists_active(id));
1012 let mut pk = DynamicTopo::new(
1013 nodes,
1014 PkConfig {
1015 visit_budget: config.pk_visit_budget,
1016 compaction_interval_ops: config.pk_compaction_interval_ops,
1017 },
1018 );
1019 let adapter = GraphAdapter { g: &g };
1021 pk.rebuild_full(&adapter);
1022 g.pk_order = Some(pk);
1023 }
1024
1025 g
1026 }
1027
1028 pub(crate) fn pk_layers_for(&self, subset: &[VertexId]) -> Option<Vec<crate::engine::Layer>> {
1030 let pk = self.pk_order.as_ref()?;
1031 let adapter = crate::engine::topo::GraphAdapter { g: self };
1032 let layers = pk.layers_for(&adapter, subset, self.config.max_layer_width);
1033 Some(
1034 layers
1035 .into_iter()
1036 .map(|vs| crate::engine::Layer { vertices: vs })
1037 .collect(),
1038 )
1039 }
1040
1041 #[inline]
1042 pub(crate) fn dynamic_topo_enabled(&self) -> bool {
1043 self.pk_order.is_some()
1044 }
1045
1046 #[cfg(test)]
1047 pub fn reset_instr(&mut self) {
1048 if let Ok(mut g) = self.instr.lock() {
1049 *g = GraphInstrumentation::default();
1050 }
1051 }
1052
1053 #[cfg(test)]
1054 pub fn instr(&self) -> GraphInstrumentation {
1055 self.instr.lock().map(|g| g.clone()).unwrap_or_default()
1056 }
1057
1058 pub fn begin_batch(&mut self) {
1060 self.edges.begin_batch();
1061 }
1062
1063 pub fn end_batch(&mut self) {
1065 self.edges.end_batch();
1066 }
1067
1068 pub fn default_sheet_id(&self) -> SheetId {
1069 self.default_sheet_id
1070 }
1071
1072 pub fn default_sheet_name(&self) -> &str {
1073 self.sheet_reg.name(self.default_sheet_id)
1074 }
1075
1076 pub fn set_default_sheet_by_name(&mut self, name: &str) {
1077 self.default_sheet_id = self.sheet_id_mut(name);
1078 }
1079
1080 pub fn set_default_sheet_by_id(&mut self, id: SheetId) {
1081 self.default_sheet_id = id;
1082 }
1083
1084 pub fn sheet_id_mut(&mut self, name: &str) -> SheetId {
1086 self.sheet_reg.id_for(name)
1087 }
1088
1089 pub fn sheet_id(&self, name: &str) -> Option<SheetId> {
1090 self.sheet_reg.get_id(name)
1091 }
1092
1093 fn resolve_existing_sheet_id(&self, name: &str) -> Result<SheetId, ExcelError> {
1095 self.sheet_id(name).ok_or_else(|| {
1096 ExcelError::new(ExcelErrorKind::Ref).with_message(format!("Sheet not found: {name}"))
1097 })
1098 }
1099
1100 pub fn sheet_name(&self, id: SheetId) -> &str {
1102 self.sheet_reg.name(id)
1103 }
1104
1105 pub fn sheet_reg(&self) -> &SheetRegistry {
1107 &self.sheet_reg
1108 }
1109
1110 pub(crate) fn data_store(&self) -> &DataStore {
1111 &self.data_store
1112 }
1113
1114 pub fn to_a1(&self, cell_ref: CellRef) -> String {
1116 format!("{}!{}", self.sheet_name(cell_ref.sheet_id), cell_ref.coord)
1117 }
1118
1119 pub(crate) fn vertex_len(&self) -> usize {
1120 self.store.len()
1121 }
1122
1123 pub fn sheet_index_mut(&mut self, sheet_id: SheetId) -> &mut SheetIndex {
1126 self.sheet_indexes.entry(sheet_id).or_default()
1127 }
1128
1129 pub fn sheet_index(&self, sheet_id: SheetId) -> Option<&SheetIndex> {
1131 self.sheet_indexes.get(&sheet_id)
1132 }
1133
1134 pub fn set_cell_value(
1136 &mut self,
1137 sheet: &str,
1138 row: u32,
1139 col: u32,
1140 value: LiteralValue,
1141 ) -> Result<OperationSummary, ExcelError> {
1142 let value = normalize_stored_literal(value);
1143 let sheet_id = self.sheet_id_mut(sheet);
1144 let coord = Coord::from_excel(row, col, true, true);
1146 let addr = CellRef::new(sheet_id, coord);
1147 let mut created_placeholders = Vec::new();
1148
1149 let vertex_id = if let Some(&existing_id) = self.cell_to_vertex.get(&addr) {
1150 let is_formula = matches!(
1152 self.store.kind(existing_id),
1153 VertexKind::FormulaScalar | VertexKind::FormulaArray
1154 );
1155
1156 if is_formula {
1157 self.remove_dependent_edges(existing_id);
1158 self.detach_vertex_from_names(existing_id);
1159 self.clear_pending_name_references(existing_id);
1160 self.vertex_formulas.remove(&existing_id);
1161 }
1162
1163 self.store.set_kind(existing_id, VertexKind::Cell);
1165 if self.value_cache_enabled {
1166 let value_ref = self.data_store.store_value(value);
1167 self.vertex_values.insert(existing_id, value_ref);
1168 } else {
1169 self.vertex_values.remove(&existing_id);
1171 }
1172 existing_id
1173 } else {
1174 created_placeholders.push(addr);
1176 let packed_coord = AbsCoord::from_excel(row, col);
1177 let vertex_id = self.store.allocate(packed_coord, sheet_id, 0x01); self.edges.add_vertex(packed_coord, vertex_id.0);
1181
1182 self.sheet_index_mut(sheet_id)
1184 .add_vertex(packed_coord, vertex_id);
1185
1186 self.store.set_kind(vertex_id, VertexKind::Cell);
1187 if self.value_cache_enabled {
1188 let value_ref = self.data_store.store_value(value);
1189 self.vertex_values.insert(vertex_id, value_ref);
1190 }
1191 self.cell_to_vertex.insert(addr, vertex_id);
1192 vertex_id
1193 };
1194
1195 self.ref_error_vertices.remove(&vertex_id);
1197
1198 Ok(OperationSummary {
1199 affected_vertices: self.mark_dirty(vertex_id),
1200 created_placeholders,
1201 })
1202 }
1203
1204 pub fn reserve_cells(&mut self, additional: usize) {
1206 self.store.reserve(additional);
1207 if self.value_cache_enabled {
1208 self.vertex_values.reserve(additional);
1209 }
1210 self.cell_to_vertex.reserve(additional);
1211 }
1213
1214 pub fn set_cell_value_bulk_untracked(
1216 &mut self,
1217 sheet: &str,
1218 row: u32,
1219 col: u32,
1220 value: LiteralValue,
1221 ) {
1222 let value = normalize_stored_literal(value);
1223 let sheet_id = self.sheet_id_mut(sheet);
1224 let coord = Coord::from_excel(row, col, true, true);
1225 let addr = CellRef::new(sheet_id, coord);
1226 if let Some(&existing_id) = self.cell_to_vertex.get(&addr) {
1227 if matches!(
1229 self.store.kind(existing_id),
1230 VertexKind::FormulaScalar | VertexKind::FormulaArray
1231 ) {
1232 self.remove_dependent_edges(existing_id);
1233 self.detach_vertex_from_names(existing_id);
1234 self.clear_pending_name_references(existing_id);
1235 self.vertex_formulas.remove(&existing_id);
1236 }
1237 if self.value_cache_enabled {
1238 let value_ref = self.data_store.store_value(value);
1239 self.vertex_values.insert(existing_id, value_ref);
1240 } else {
1241 self.vertex_values.remove(&existing_id);
1242 }
1243 self.store.set_kind(existing_id, VertexKind::Cell);
1244 self.ref_error_vertices.remove(&existing_id);
1245 return;
1246 }
1247 let packed_coord = AbsCoord::from_excel(row, col);
1248 let vertex_id = self.store.allocate(packed_coord, sheet_id, 0x00); self.edges.add_vertex(packed_coord, vertex_id.0);
1250 self.sheet_index_mut(sheet_id)
1251 .add_vertex(packed_coord, vertex_id);
1252 self.store.set_kind(vertex_id, VertexKind::Cell);
1253 self.ref_error_vertices.remove(&vertex_id);
1254 if self.value_cache_enabled {
1255 let value_ref = self.data_store.store_value(value);
1256 self.vertex_values.insert(vertex_id, value_ref);
1257 }
1258 self.cell_to_vertex.insert(addr, vertex_id);
1259 }
1260
1261 pub fn bulk_insert_values<I>(&mut self, sheet: &str, cells: I)
1263 where
1264 I: IntoIterator<Item = (u32, u32, LiteralValue)>,
1265 {
1266 use crate::instant::FzInstant as Instant;
1267 let t0 = Instant::now();
1268 let collected: Vec<(u32, u32, LiteralValue)> = cells.into_iter().collect();
1270 if collected.is_empty() {
1271 return;
1272 }
1273 let sheet_id = self.sheet_id_mut(sheet);
1274 self.reserve_cells(collected.len());
1275 let t_reserve = Instant::now();
1276 let mut new_vertices: Vec<(AbsCoord, u32)> = Vec::with_capacity(collected.len());
1277 let mut index_items: Vec<(AbsCoord, VertexId)> = Vec::with_capacity(collected.len());
1278 let mut new_value_coords: Vec<(AbsCoord, VertexId)> = Vec::with_capacity(collected.len());
1280 let mut new_value_literals: Vec<LiteralValue> = Vec::with_capacity(collected.len());
1281 let assume_new = self.first_load_assume_new
1283 && self
1284 .sheet_id(sheet)
1285 .map(|sid| !self.ensure_touched_sheets.contains(&sid))
1286 .unwrap_or(false);
1287
1288 for (row, col, value) in collected {
1289 let value = normalize_stored_literal(value);
1290 let coord = Coord::from_excel(row, col, true, true);
1291 let addr = CellRef::new(sheet_id, coord);
1292 if !assume_new && let Some(&existing_id) = self.cell_to_vertex.get(&addr) {
1293 if matches!(
1294 self.store.kind(existing_id),
1295 VertexKind::FormulaScalar | VertexKind::FormulaArray
1296 ) {
1297 self.remove_dependent_edges(existing_id);
1298 self.detach_vertex_from_names(existing_id);
1299 self.clear_pending_name_references(existing_id);
1300 self.vertex_formulas.remove(&existing_id);
1301 }
1302 if self.value_cache_enabled {
1303 let value_ref = self.data_store.store_value(value);
1304 self.vertex_values.insert(existing_id, value_ref);
1305 } else {
1306 self.vertex_values.remove(&existing_id);
1307 }
1308 self.store.set_kind(existing_id, VertexKind::Cell);
1309 continue;
1310 }
1311 let packed = AbsCoord::from_excel(row, col);
1312 let vertex_id = self.store.allocate(packed, sheet_id, 0x00);
1313 self.store.set_kind(vertex_id, VertexKind::Cell);
1314 new_value_coords.push((packed, vertex_id));
1316 new_value_literals.push(value);
1317 self.cell_to_vertex.insert(addr, vertex_id);
1318 new_vertices.push((packed, vertex_id.0));
1319 index_items.push((packed, vertex_id));
1320 }
1321 if self.value_cache_enabled && !new_value_literals.is_empty() {
1323 let vrefs = self.data_store.store_values_batch(new_value_literals);
1324 debug_assert_eq!(vrefs.len(), new_value_coords.len());
1325 for (i, (_pc, vid)) in new_value_coords.iter().enumerate() {
1326 self.vertex_values.insert(*vid, vrefs[i]);
1327 }
1328 }
1329 let t_after_alloc = Instant::now();
1330 if !new_vertices.is_empty() {
1331 let t_edges_start = Instant::now();
1332 self.edges.add_vertices_batch(&new_vertices);
1333 let t_edges_done = Instant::now();
1334
1335 match self.config.sheet_index_mode {
1336 crate::engine::SheetIndexMode::Eager => {
1337 self.sheet_index_mut(sheet_id)
1338 .add_vertices_batch(&index_items);
1339 }
1340 crate::engine::SheetIndexMode::Lazy => {
1341 }
1343 crate::engine::SheetIndexMode::FastBatch => {
1344 self.sheet_index_mut(sheet_id)
1346 .add_vertices_batch(&index_items);
1347 }
1348 }
1349 let t_index_done = Instant::now();
1350 }
1351 }
1352
1353 pub fn set_cell_formula(
1355 &mut self,
1356 sheet: &str,
1357 row: u32,
1358 col: u32,
1359 ast: ASTNode,
1360 ) -> Result<OperationSummary, ExcelError> {
1361 let volatile = self.is_ast_volatile(&ast);
1362 self.set_cell_formula_with_volatility(sheet, row, col, ast, volatile)
1363 }
1364
1365 pub fn set_cell_formula_with_volatility(
1367 &mut self,
1368 sheet: &str,
1369 row: u32,
1370 col: u32,
1371 ast: ASTNode,
1372 volatile: bool,
1373 ) -> Result<OperationSummary, ExcelError> {
1374 let dbg = std::env::var("FZ_DEBUG_LOAD")
1375 .ok()
1376 .is_some_and(|v| v != "0");
1377 let dep_ms_thresh: u128 = std::env::var("FZ_DEBUG_DEP_MS")
1378 .ok()
1379 .and_then(|s| s.parse().ok())
1380 .unwrap_or(0);
1381 let sample_n: usize = std::env::var("FZ_DEBUG_SAMPLE_N")
1382 .ok()
1383 .and_then(|s| s.parse().ok())
1384 .unwrap_or(0);
1385 let t0 = if dbg {
1386 Some(crate::instant::FzInstant::now())
1387 } else {
1388 None
1389 };
1390 let sheet_id = self.sheet_id_mut(sheet);
1391 let coord = Coord::from_excel(row, col, true, true);
1392 let addr = CellRef::new(sheet_id, coord);
1393
1394 let mut ast = ast;
1397 self.rewrite_structured_references_for_cell(&mut ast, addr)?;
1398
1399 let t_dep0 = if dbg {
1401 Some(crate::instant::FzInstant::now())
1402 } else {
1403 None
1404 };
1405 let (
1406 new_dependencies,
1407 new_range_dependencies,
1408 mut created_placeholders,
1409 named_dependencies,
1410 unresolved_names,
1411 ) = self.extract_dependencies_with_pending_names(&ast, sheet_id)?;
1412 if let (true, Some(t)) = (dbg, t_dep0) {
1413 let elapsed = t.elapsed().as_millis();
1414 let do_log = (dep_ms_thresh > 0 && elapsed >= dep_ms_thresh)
1416 || (sample_n > 0 && (row as usize).is_multiple_of(sample_n));
1417 if dep_ms_thresh == 0 && sample_n == 0 {
1418 if row.is_multiple_of(1000) {
1420 eprintln!(
1421 "[fz][dep] {}!{} extracted: deps={}, ranges={}, placeholders={}, names={} in {} ms",
1422 self.sheet_name(sheet_id),
1423 crate::reference::Coord::from_excel(row, col, true, true),
1424 new_dependencies.len(),
1425 new_range_dependencies.len(),
1426 created_placeholders.len(),
1427 named_dependencies.len(),
1428 elapsed
1429 );
1430 }
1431 } else if do_log {
1432 eprintln!(
1433 "[fz][dep] {}!{} extracted: deps={}, ranges={}, placeholders={}, names={} in {} ms",
1434 self.sheet_name(sheet_id),
1435 crate::reference::Coord::from_excel(row, col, true, true),
1436 new_dependencies.len(),
1437 new_range_dependencies.len(),
1438 created_placeholders.len(),
1439 named_dependencies.len(),
1440 elapsed
1441 );
1442 }
1443 }
1444
1445 let addr_vertex_id = self.get_or_create_vertex(&addr, &mut created_placeholders);
1447
1448 self.ref_error_vertices.remove(&addr_vertex_id);
1450
1451 if new_dependencies.contains(&addr_vertex_id) {
1452 return Err(ExcelError::new(ExcelErrorKind::Circ)
1453 .with_message("Self-reference detected".to_string()));
1454 }
1455
1456 for &name_vertex in &named_dependencies {
1457 let mut visited = FxHashSet::default();
1458 if self.name_depends_on_vertex(name_vertex, addr_vertex_id, &mut visited) {
1459 return Err(ExcelError::new(ExcelErrorKind::Circ)
1460 .with_message("Circular reference through named range".to_string()));
1461 }
1462 }
1463
1464 self.remove_dependent_edges(addr_vertex_id);
1466 self.detach_vertex_from_names(addr_vertex_id);
1467 self.clear_pending_name_references(addr_vertex_id);
1468
1469 self.store
1471 .set_kind(addr_vertex_id, VertexKind::FormulaScalar);
1472 let ast_id = self.data_store.store_ast(&ast, &self.sheet_reg);
1473 self.vertex_formulas.insert(addr_vertex_id, ast_id);
1474 self.store.set_dirty(addr_vertex_id, true);
1475
1476 self.vertex_values.remove(&addr_vertex_id);
1478
1479 self.mark_volatile(addr_vertex_id, volatile);
1480 let dynamic = self.is_ast_dynamic(&ast);
1481 self.store.set_dynamic(addr_vertex_id, dynamic);
1482
1483 if !named_dependencies.is_empty() {
1484 self.attach_vertex_to_names(addr_vertex_id, &named_dependencies);
1485 }
1486 for unresolved_name in &unresolved_names {
1487 self.record_pending_name_reference(sheet_id, unresolved_name, addr_vertex_id);
1488 }
1489
1490 if let (true, Some(t)) = (dbg, t0) {
1491 let elapsed = t.elapsed().as_millis();
1492 let log_set = dep_ms_thresh > 0 && elapsed >= dep_ms_thresh;
1493 if log_set {
1494 eprintln!(
1495 "[fz][set] {}!{} total {} ms",
1496 self.sheet_name(sheet_id),
1497 crate::reference::Coord::from_excel(row, col, true, true),
1498 elapsed
1499 );
1500 }
1501 }
1502
1503 self.add_dependent_edges(addr_vertex_id, &new_dependencies);
1505 self.add_range_dependent_edges(addr_vertex_id, &new_range_dependencies, sheet_id);
1506
1507 Ok(OperationSummary {
1508 affected_vertices: self.mark_dirty(addr_vertex_id),
1509 created_placeholders,
1510 })
1511 }
1512
1513 pub(crate) fn rewrite_structured_references_for_cell(
1514 &self,
1515 ast: &mut ASTNode,
1516 cell: CellRef,
1517 ) -> Result<(), ExcelError> {
1518 self.rewrite_structured_references_node(ast, cell)
1519 }
1520
1521 fn rewrite_structured_references_node(
1522 &self,
1523 node: &mut ASTNode,
1524 cell: CellRef,
1525 ) -> Result<(), ExcelError> {
1526 match &mut node.node_type {
1527 ASTNodeType::Reference { reference, .. } => {
1528 self.rewrite_structured_reference(reference, cell)
1529 }
1530 ASTNodeType::UnaryOp { expr, .. } => {
1531 self.rewrite_structured_references_node(expr, cell)
1532 }
1533 ASTNodeType::BinaryOp { left, right, .. } => {
1534 self.rewrite_structured_references_node(left, cell)?;
1535 self.rewrite_structured_references_node(right, cell)
1536 }
1537 ASTNodeType::Function { args, .. } => {
1538 for a in args.iter_mut() {
1539 self.rewrite_structured_references_node(a, cell)?;
1540 }
1541 Ok(())
1542 }
1543 ASTNodeType::Call { callee, args } => {
1544 self.rewrite_structured_references_node(callee, cell)?;
1545 for a in args.iter_mut() {
1546 self.rewrite_structured_references_node(a, cell)?;
1547 }
1548 Ok(())
1549 }
1550 ASTNodeType::Array(rows) => {
1551 for r in rows.iter_mut() {
1552 for item in r.iter_mut() {
1553 self.rewrite_structured_references_node(item, cell)?;
1554 }
1555 }
1556 Ok(())
1557 }
1558 ASTNodeType::Literal(_) => Ok(()),
1559 }
1560 }
1561
1562 fn rewrite_structured_reference(
1563 &self,
1564 reference: &mut ReferenceType,
1565 cell: CellRef,
1566 ) -> Result<(), ExcelError> {
1567 use formualizer_parse::parser::{SpecialItem, TableSpecifier};
1568
1569 let ReferenceType::Table(tref) = reference else {
1570 return Ok(());
1571 };
1572
1573 if !tref.name.is_empty() {
1575 return Ok(());
1576 }
1577
1578 let col_name = match &tref.specifier {
1579 Some(TableSpecifier::Combination(parts)) => {
1580 let mut saw_this_row = false;
1581 let mut col: Option<&str> = None;
1582 for p in parts {
1583 match p.as_ref() {
1584 TableSpecifier::SpecialItem(SpecialItem::ThisRow) => {
1585 saw_this_row = true;
1586 }
1587 TableSpecifier::Column(c) => {
1588 if col.is_some() {
1589 return Err(ExcelError::new(ExcelErrorKind::NImpl).with_message(
1590 "This-row structured reference with multiple columns is not supported"
1591 .to_string(),
1592 ));
1593 }
1594 col = Some(c.as_str());
1595 }
1596 other => {
1597 return Err(ExcelError::new(ExcelErrorKind::NImpl).with_message(
1598 format!(
1599 "Unsupported this-row structured reference component: {other}"
1600 ),
1601 ));
1602 }
1603 }
1604 }
1605 if !saw_this_row {
1606 return Err(ExcelError::new(ExcelErrorKind::NImpl).with_message(
1607 "Unnamed structured reference requires a this-row selector".to_string(),
1608 ));
1609 }
1610 col.ok_or_else(|| {
1611 ExcelError::new(ExcelErrorKind::NImpl).with_message(
1612 "This-row structured reference missing column selector".to_string(),
1613 )
1614 })?
1615 }
1616 _ => {
1617 return Err(ExcelError::new(ExcelErrorKind::NImpl).with_message(
1618 "Unnamed structured reference form is not supported".to_string(),
1619 ));
1620 }
1621 };
1622
1623 let Some(table) = self.find_table_containing_cell(cell) else {
1624 return Err(ExcelError::new(ExcelErrorKind::Name)
1625 .with_message("This-row structured reference used outside a table".to_string()));
1626 };
1627
1628 let row0 = cell.coord.row();
1629 let col0 = cell.coord.col();
1630 let sr0 = table.range.start.coord.row();
1631 let sc0 = table.range.start.coord.col();
1632 let er0 = table.range.end.coord.row();
1633 let ec0 = table.range.end.coord.col();
1634
1635 if row0 < sr0 || row0 > er0 || col0 < sc0 || col0 > ec0 {
1636 return Err(ExcelError::new(ExcelErrorKind::Name)
1637 .with_message("This-row structured reference used outside a table".to_string()));
1638 }
1639
1640 if table.header_row && row0 == sr0 {
1641 return Err(ExcelError::new(ExcelErrorKind::Ref).with_message(
1642 "This-row structured references are not valid in the table header row".to_string(),
1643 ));
1644 }
1645
1646 let data_start = if table.header_row { sr0 + 1 } else { sr0 };
1647 if row0 < data_start {
1648 return Err(ExcelError::new(ExcelErrorKind::Ref).with_message(
1649 "This-row structured references require a data/totals row context".to_string(),
1650 ));
1651 }
1652
1653 let Some(idx) = table.col_index(col_name) else {
1654 return Err(ExcelError::new(ExcelErrorKind::Ref).with_message(format!(
1655 "Unknown table column in this-row reference: {col_name}"
1656 )));
1657 };
1658 let target_col0 = sc0 + (idx as u32);
1659 let target_row = row0 + 1;
1660 let target_col = target_col0 + 1;
1661
1662 *reference = ReferenceType::Cell {
1663 sheet: None,
1664 row: target_row,
1665 col: target_col,
1666 row_abs: true,
1667 col_abs: true,
1668 };
1669
1670 Ok(())
1671 }
1672
1673 fn find_table_containing_cell(&self, cell: CellRef) -> Option<&tables::TableEntry> {
1674 let row0 = cell.coord.row();
1675 let col0 = cell.coord.col();
1676
1677 let mut best: Option<&tables::TableEntry> = None;
1678 let mut best_area: u64 = u64::MAX;
1679 let mut best_name: &str = "";
1680
1681 for t in self.tables.values() {
1682 if t.sheet_id() != cell.sheet_id {
1683 continue;
1684 }
1685 let sr0 = t.range.start.coord.row();
1686 let sc0 = t.range.start.coord.col();
1687 let er0 = t.range.end.coord.row();
1688 let ec0 = t.range.end.coord.col();
1689 if row0 < sr0 || row0 > er0 || col0 < sc0 || col0 > ec0 {
1690 continue;
1691 }
1692
1693 let h = (er0 - sr0 + 1) as u64;
1694 let w = (ec0 - sc0 + 1) as u64;
1695 let area = h.saturating_mul(w);
1696 let name = t.name.as_str();
1697 let better = match best {
1698 None => true,
1699 Some(_) => area < best_area || (area == best_area && name < best_name),
1700 };
1701 if better {
1702 best = Some(t);
1703 best_area = area;
1704 best_name = name;
1705 }
1706 }
1707
1708 best
1709 }
1710
1711 pub fn set_cell_value_ref(
1712 &mut self,
1713 cell: formualizer_common::SheetCellRef<'_>,
1714 value: LiteralValue,
1715 ) -> Result<OperationSummary, ExcelError> {
1716 let owned = cell.into_owned();
1717 let sheet_id = match owned.sheet {
1718 formualizer_common::SheetLocator::Id(id) => id,
1719 formualizer_common::SheetLocator::Name(name) => self.sheet_id_mut(name.as_ref()),
1720 formualizer_common::SheetLocator::Current => self.default_sheet_id,
1721 };
1722 let sheet_name = self.sheet_name(sheet_id).to_string();
1723 self.set_cell_value(
1724 &sheet_name,
1725 owned.coord.row() + 1,
1726 owned.coord.col() + 1,
1727 value,
1728 )
1729 }
1730
1731 pub fn set_cell_formula_ref(
1732 &mut self,
1733 cell: formualizer_common::SheetCellRef<'_>,
1734 ast: ASTNode,
1735 ) -> Result<OperationSummary, ExcelError> {
1736 let owned = cell.into_owned();
1737 let sheet_id = match owned.sheet {
1738 formualizer_common::SheetLocator::Id(id) => id,
1739 formualizer_common::SheetLocator::Name(name) => self.sheet_id_mut(name.as_ref()),
1740 formualizer_common::SheetLocator::Current => self.default_sheet_id,
1741 };
1742 let sheet_name = self.sheet_name(sheet_id).to_string();
1743 self.set_cell_formula(
1744 &sheet_name,
1745 owned.coord.row() + 1,
1746 owned.coord.col() + 1,
1747 ast,
1748 )
1749 }
1750
1751 pub fn get_cell_value_ref(
1752 &self,
1753 cell: formualizer_common::SheetCellRef<'_>,
1754 ) -> Option<LiteralValue> {
1755 let owned = cell.into_owned();
1756 let sheet_id = match owned.sheet {
1757 formualizer_common::SheetLocator::Id(id) => id,
1758 formualizer_common::SheetLocator::Name(name) => self.sheet_id(name.as_ref())?,
1759 formualizer_common::SheetLocator::Current => self.default_sheet_id,
1760 };
1761 let sheet_name = self.sheet_name(sheet_id);
1762 self.get_cell_value(sheet_name, owned.coord.row() + 1, owned.coord.col() + 1)
1763 }
1764
1765 pub fn get_cell_value(&self, sheet: &str, row: u32, col: u32) -> Option<LiteralValue> {
1767 if !self.value_cache_enabled {
1768 #[cfg(debug_assertions)]
1769 {
1770 self.graph_value_read_attempts
1771 .fetch_add(1, Ordering::Relaxed);
1772 }
1773 return None;
1774 }
1775 let sheet_id = self.sheet_reg.get_id(sheet)?;
1776 let coord = Coord::from_excel(row, col, true, true);
1777 let addr = CellRef::new(sheet_id, coord);
1778
1779 self.get_vertex_id_for_address(&addr)
1780 .and_then(|&vertex_id| {
1781 self.vertex_values
1783 .get(&vertex_id)
1784 .map(|&value_ref| self.data_store.retrieve_value(value_ref))
1785 })
1786 }
1787
1788 fn mark_dirty(&mut self, vertex_id: VertexId) -> Vec<VertexId> {
1790 let mut affected = FxHashSet::default();
1791 let mut to_visit = Vec::new();
1792 let mut visited_for_propagation = FxHashSet::default();
1793
1794 let is_formula = matches!(
1797 self.store.kind(vertex_id),
1798 VertexKind::FormulaScalar
1799 | VertexKind::FormulaArray
1800 | VertexKind::NamedScalar
1801 | VertexKind::NamedArray
1802 );
1803
1804 if is_formula {
1805 to_visit.push(vertex_id);
1806 } else {
1807 affected.insert(vertex_id);
1809 }
1810
1811 {
1813 if let Some(dependents) = self.dependents_slice(vertex_id) {
1815 to_visit.extend(dependents.iter().copied());
1816 } else {
1817 let dependents = self.get_dependents(vertex_id);
1818 to_visit.extend(dependents);
1819 }
1820
1821 if let Some(name_set) = self.cell_to_name_dependents.get(&vertex_id) {
1822 for &name_vertex in name_set {
1823 to_visit.push(name_vertex);
1824 }
1825 }
1826
1827 to_visit.extend(self.collect_range_dependents_for_vertex(vertex_id));
1828 }
1829
1830 while let Some(id) = to_visit.pop() {
1831 if !visited_for_propagation.insert(id) {
1832 continue; }
1834 affected.insert(id);
1835
1836 self.store.set_dirty(id, true);
1838
1839 if let Some(dependents) = self.dependents_slice(id) {
1841 to_visit.extend(dependents.iter().copied());
1842 } else {
1843 let dependents = self.get_dependents(id);
1844 to_visit.extend(dependents);
1845 }
1846 to_visit.extend(self.collect_range_dependents_for_vertex(id));
1847 }
1848
1849 self.dirty_vertices.extend(&affected);
1851
1852 affected.into_iter().collect()
1854 }
1855
1856 pub fn get_evaluation_vertices(&self) -> Vec<VertexId> {
1858 let mut combined = FxHashSet::default();
1859 combined.extend(&self.dirty_vertices);
1860 combined.extend(&self.volatile_vertices);
1861
1862 let mut result: Vec<VertexId> = combined
1863 .into_iter()
1864 .filter(|&id| {
1865 matches!(
1867 self.store.kind(id),
1868 VertexKind::FormulaScalar
1869 | VertexKind::FormulaArray
1870 | VertexKind::NamedScalar
1871 | VertexKind::NamedArray
1872 )
1873 })
1874 .collect();
1875 result.sort_unstable();
1876 result
1877 }
1878
1879 pub fn clear_dirty_flags(&mut self, vertices: &[VertexId]) {
1881 for &vertex_id in vertices {
1882 self.store.set_dirty(vertex_id, false);
1883 self.dirty_vertices.remove(&vertex_id);
1884 }
1885 }
1886
1887 pub fn clear_volatile_flags(&mut self) {
1889 self.volatile_vertices.clear();
1890 }
1891
1892 pub(crate) fn redirty_volatiles(&mut self) {
1894 let volatile_ids: Vec<VertexId> = self.volatile_vertices.iter().copied().collect();
1895 for id in volatile_ids {
1896 self.mark_dirty(id);
1897 }
1898 }
1899
1900 fn get_or_create_vertex(
1901 &mut self,
1902 addr: &CellRef,
1903 created_placeholders: &mut Vec<CellRef>,
1904 ) -> VertexId {
1905 if let Some(&vertex_id) = self.cell_to_vertex.get(addr) {
1906 return vertex_id;
1907 }
1908
1909 created_placeholders.push(*addr);
1910 let packed_coord = AbsCoord::new(addr.coord.row(), addr.coord.col());
1911 let vertex_id = self.store.allocate(packed_coord, addr.sheet_id, 0x00);
1912
1913 self.edges.add_vertex(packed_coord, vertex_id.0);
1915
1916 self.sheet_index_mut(addr.sheet_id)
1918 .add_vertex(packed_coord, vertex_id);
1919
1920 self.store.set_kind(vertex_id, VertexKind::Empty);
1921 self.cell_to_vertex.insert(*addr, vertex_id);
1922 vertex_id
1923 }
1924
1925 fn add_dependent_edges(&mut self, dependent: VertexId, dependencies: &[VertexId]) {
1926 self.edges.begin_batch();
1928
1929 let mut skip_deps: rustc_hash::FxHashSet<VertexId> = rustc_hash::FxHashSet::default();
1932 if self.pk_order.is_some()
1933 && let Some(mut pk) = self.pk_order.take()
1934 {
1935 pk.ensure_nodes(std::iter::once(dependent));
1936 pk.ensure_nodes(dependencies.iter().copied());
1937 {
1938 let adapter = GraphAdapter { g: self };
1939 for &dep_id in dependencies {
1940 match pk.try_add_edge(&adapter, dep_id, dependent) {
1941 Ok(_) => {}
1942 Err(_cycle) => {
1943 if self.config.pk_reject_cycle_edges {
1944 skip_deps.insert(dep_id);
1945 } else {
1946 pk.rebuild_full(&adapter);
1947 }
1948 }
1949 }
1950 }
1951 } self.pk_order = Some(pk);
1953 }
1954
1955 for &dep_id in dependencies {
1957 if self.config.pk_reject_cycle_edges && skip_deps.contains(&dep_id) {
1958 continue;
1959 }
1960 self.edges.add_edge(dependent, dep_id);
1961 #[cfg(test)]
1962 {
1963 if let Ok(mut g) = self.instr.lock() {
1964 g.edges_added += 1;
1965 }
1966 }
1967 }
1968
1969 self.edges.end_batch();
1970 }
1971
1972 fn add_dependent_edges_nobatch(&mut self, dependent: VertexId, dependencies: &[VertexId]) {
1974 let mut skip_deps: rustc_hash::FxHashSet<VertexId> = rustc_hash::FxHashSet::default();
1976 if self.pk_order.is_some()
1977 && let Some(mut pk) = self.pk_order.take()
1978 {
1979 pk.ensure_nodes(std::iter::once(dependent));
1980 pk.ensure_nodes(dependencies.iter().copied());
1981 {
1982 let adapter = GraphAdapter { g: self };
1983 for &dep_id in dependencies {
1984 match pk.try_add_edge(&adapter, dep_id, dependent) {
1985 Ok(_) => {}
1986 Err(_cycle) => {
1987 if self.config.pk_reject_cycle_edges {
1988 skip_deps.insert(dep_id);
1989 } else {
1990 pk.rebuild_full(&adapter);
1991 }
1992 }
1993 }
1994 }
1995 }
1996 self.pk_order = Some(pk);
1997 }
1998
1999 for &dep_id in dependencies {
2000 if self.config.pk_reject_cycle_edges && skip_deps.contains(&dep_id) {
2001 continue;
2002 }
2003 self.edges.add_edge(dependent, dep_id);
2004 #[cfg(test)]
2005 {
2006 if let Ok(mut g) = self.instr.lock() {
2007 g.edges_added += 1;
2008 }
2009 }
2010 }
2011 }
2012
2013 pub fn bulk_set_formulas<I>(&mut self, sheet: &str, items: I) -> Result<usize, ExcelError>
2015 where
2016 I: IntoIterator<Item = (u32, u32, ASTNode)>,
2017 {
2018 let collected: Vec<(u32, u32, ASTNode)> = items.into_iter().collect();
2019 if collected.is_empty() {
2020 return Ok(0);
2021 }
2022 let vol_flags: Vec<bool> = collected
2023 .iter()
2024 .map(|(_, _, ast)| self.is_ast_volatile(ast))
2025 .collect();
2026 self.bulk_set_formulas_with_volatility(sheet, collected, vol_flags)
2027 }
2028
2029 pub fn bulk_set_formulas_with_volatility(
2030 &mut self,
2031 sheet: &str,
2032 collected: Vec<(u32, u32, ASTNode)>,
2033 vol_flags: Vec<bool>,
2034 ) -> Result<usize, ExcelError> {
2035 use formualizer_parse::parser::CollectPolicy;
2036 let sheet_id = self.sheet_id_mut(sheet);
2037
2038 if collected.is_empty() {
2039 return Ok(0);
2040 }
2041
2042 let tiny_refs = collected.iter().map(|(r, c, ast)| (sheet, *r, *c, ast));
2044 let policy = CollectPolicy {
2045 expand_small_ranges: true,
2046 range_expansion_limit: self.config.range_expansion_limit,
2047 include_names: true,
2048 };
2049 let plan = crate::engine::plan::build_dependency_plan(
2050 &mut self.sheet_reg,
2051 tiny_refs,
2052 &policy,
2053 Some(&vol_flags),
2054 )?;
2055
2056 let mut created_placeholders: Vec<CellRef> = Vec::new();
2058
2059 let mut target_vids: Vec<VertexId> = Vec::with_capacity(plan.formula_targets.len());
2061 for (sid, pc) in &plan.formula_targets {
2062 let addr = CellRef::new(*sid, Coord::new(pc.row(), pc.col(), true, true));
2063 let vid = if let Some(&existing) = self.cell_to_vertex.get(&addr) {
2064 existing
2065 } else {
2066 self.get_or_create_vertex(&addr, &mut created_placeholders)
2067 };
2068 target_vids.push(vid);
2069 }
2070
2071 let mut dep_vids: Vec<VertexId> = Vec::with_capacity(plan.global_cells.len());
2073 for (sid, pc) in &plan.global_cells {
2074 let addr = CellRef::new(*sid, Coord::new(pc.row(), pc.col(), true, true));
2075 let vid = if let Some(&existing) = self.cell_to_vertex.get(&addr) {
2076 existing
2077 } else {
2078 self.get_or_create_vertex(&addr, &mut created_placeholders)
2079 };
2080 dep_vids.push(vid);
2081 }
2082
2083 let ast_ids = self
2085 .data_store
2086 .store_asts_batch(collected.iter().map(|(_, _, ast)| ast), &self.sheet_reg);
2087 for (i, &tvid) in target_vids.iter().enumerate() {
2088 if self.vertex_formulas.contains_key(&tvid) {
2090 self.remove_dependent_edges(tvid);
2091 }
2092 self.store.set_kind(tvid, VertexKind::FormulaScalar);
2093 self.store.set_dirty(tvid, true);
2094 self.vertex_values.remove(&tvid);
2095 self.vertex_formulas.insert(tvid, ast_ids[i]);
2096 self.mark_volatile(tvid, vol_flags.get(i).copied().unwrap_or(false));
2097
2098 let dynamic = self.is_ast_dynamic(&collected[i].2);
2099 self.store.set_dynamic(tvid, dynamic);
2100 }
2101
2102 self.edges.begin_batch();
2104 for (i, tvid) in target_vids.iter().copied().enumerate() {
2105 let mut deps: Vec<VertexId> = Vec::new();
2106
2107 if let Some(indices) = plan.per_formula_cells.get(i) {
2109 deps.reserve(indices.len());
2110 for &idx in indices {
2111 if let Some(vid) = dep_vids.get(idx as usize) {
2112 deps.push(*vid);
2113 }
2114 }
2115 }
2116
2117 if let Some(names) = plan.per_formula_names.get(i)
2118 && !names.is_empty()
2119 {
2120 let mut name_vertices = Vec::new();
2121 let formula_sheet = plan
2122 .formula_targets
2123 .get(i)
2124 .map(|(sid, _)| *sid)
2125 .unwrap_or(sheet_id);
2126 for name in names {
2127 if let Some(named) = self.resolve_name_entry(name, formula_sheet) {
2128 deps.push(named.vertex);
2129 name_vertices.push(named.vertex);
2130 } else if let Some(source) = self.resolve_source_scalar_entry(name) {
2131 deps.push(source.vertex);
2132 } else {
2133 self.record_pending_name_reference(formula_sheet, name, tvid);
2134 }
2135 }
2136 if !name_vertices.is_empty() {
2137 self.attach_vertex_to_names(tvid, &name_vertices);
2138 }
2139 }
2140
2141 if let Some(tables) = plan.per_formula_tables.get(i)
2142 && !tables.is_empty()
2143 {
2144 for table_name in tables {
2145 if let Some(table) = self.resolve_table_entry(table_name) {
2146 deps.push(table.vertex);
2147 } else if let Some(source) = self.resolve_source_table_entry(table_name) {
2148 deps.push(source.vertex);
2149 }
2150 }
2151 }
2152
2153 if !deps.is_empty() {
2154 self.add_dependent_edges_nobatch(tvid, &deps);
2155 }
2156
2157 if let Some(rks) = plan.per_formula_ranges.get(i) {
2159 self.add_range_deps_from_keys(tvid, rks, sheet_id);
2160 }
2161 }
2162 self.edges.end_batch();
2163
2164 Ok(collected.len())
2165 }
2166
2167 pub fn add_dependency_edge(&mut self, dependent: VertexId, dependency: VertexId) {
2169 if dependent == dependency {
2170 return;
2171 }
2172 if self.pk_order.is_some()
2174 && let Some(mut pk) = self.pk_order.take()
2175 {
2176 pk.ensure_nodes(std::iter::once(dependent));
2177 pk.ensure_nodes(std::iter::once(dependency));
2178 let adapter = GraphAdapter { g: self };
2179 if pk.try_add_edge(&adapter, dependency, dependent).is_err() {
2180 pk.rebuild_full(&adapter);
2182 }
2183 self.pk_order = Some(pk);
2184 }
2185 self.edges.add_edge(dependent, dependency);
2186 self.store.set_dirty(dependent, true);
2187 self.dirty_vertices.insert(dependent);
2188 }
2189
2190 fn remove_dependent_edges(&mut self, vertex: VertexId) {
2191 let dependencies = self.edges.out_edges(vertex);
2193
2194 self.edges.begin_batch();
2195 if self.pk_order.is_some()
2196 && let Some(mut pk) = self.pk_order.take()
2197 {
2198 for dep in &dependencies {
2199 pk.remove_edge(*dep, vertex);
2200 }
2201 self.pk_order = Some(pk);
2202 }
2203 for dep in dependencies {
2204 self.edges.remove_edge(vertex, dep);
2205 }
2206 self.edges.end_batch();
2207
2208 if let Some(old_ranges) = self.formula_to_range_deps.remove(&vertex) {
2210 let old_sheet_id = self.store.sheet_id(vertex);
2211
2212 for range in &old_ranges {
2213 let sheet_id = match range.sheet {
2214 SharedSheetLocator::Id(id) => id,
2215 _ => old_sheet_id,
2216 };
2217 let s_row = range.start_row.map(|b| b.index);
2218 let e_row = range.end_row.map(|b| b.index);
2219 let s_col = range.start_col.map(|b| b.index);
2220 let e_col = range.end_col.map(|b| b.index);
2221
2222 let mut keys_to_clean = FxHashSet::default();
2223
2224 let col_stripes = (s_row.is_none() && e_row.is_none())
2225 || (s_col.is_some() && e_col.is_some() && (s_row.is_none() || e_row.is_none()));
2226 let row_stripes = (s_col.is_none() && e_col.is_none())
2227 || (s_row.is_some() && e_row.is_some() && (s_col.is_none() || e_col.is_none()));
2228
2229 if col_stripes && !row_stripes {
2230 let sc = s_col.unwrap_or(0);
2231 let ec = e_col.unwrap_or(sc);
2232 for col in sc..=ec {
2233 keys_to_clean.insert(StripeKey {
2234 sheet_id,
2235 stripe_type: StripeType::Column,
2236 index: col,
2237 });
2238 }
2239 } else if row_stripes && !col_stripes {
2240 let sr = s_row.unwrap_or(0);
2241 let er = e_row.unwrap_or(sr);
2242 for row in sr..=er {
2243 keys_to_clean.insert(StripeKey {
2244 sheet_id,
2245 stripe_type: StripeType::Row,
2246 index: row,
2247 });
2248 }
2249 } else {
2250 let start_row = s_row.unwrap_or(0);
2251 let start_col = s_col.unwrap_or(0);
2252 let end_row = e_row.unwrap_or(start_row);
2253 let end_col = e_col.unwrap_or(start_col);
2254
2255 let height = end_row.saturating_sub(start_row) + 1;
2256 let width = end_col.saturating_sub(start_col) + 1;
2257
2258 if self.config.enable_block_stripes && height > 1 && width > 1 {
2259 let start_block_row = start_row / BLOCK_H;
2260 let end_block_row = end_row / BLOCK_H;
2261 let start_block_col = start_col / BLOCK_W;
2262 let end_block_col = end_col / BLOCK_W;
2263
2264 for block_row in start_block_row..=end_block_row {
2265 for block_col in start_block_col..=end_block_col {
2266 keys_to_clean.insert(StripeKey {
2267 sheet_id,
2268 stripe_type: StripeType::Block,
2269 index: block_index(block_row * BLOCK_H, block_col * BLOCK_W),
2270 });
2271 }
2272 }
2273 } else if height > width {
2274 for col in start_col..=end_col {
2275 keys_to_clean.insert(StripeKey {
2276 sheet_id,
2277 stripe_type: StripeType::Column,
2278 index: col,
2279 });
2280 }
2281 } else {
2282 for row in start_row..=end_row {
2283 keys_to_clean.insert(StripeKey {
2284 sheet_id,
2285 stripe_type: StripeType::Row,
2286 index: row,
2287 });
2288 }
2289 }
2290 }
2291
2292 for key in keys_to_clean {
2293 if let Some(dependents) = self.stripe_to_dependents.get_mut(&key) {
2294 dependents.remove(&vertex);
2295 if dependents.is_empty() {
2296 self.stripe_to_dependents.remove(&key);
2297 #[cfg(test)]
2298 {
2299 if let Ok(mut g) = self.instr.lock() {
2300 g.stripe_removes += 1;
2301 }
2302 }
2303 }
2304 }
2305 }
2306 }
2307 }
2308 }
2309
2310 pub(crate) fn update_vertex_value(&mut self, vertex_id: VertexId, value: LiteralValue) {
2316 if !self.value_cache_enabled {
2317 match self.store.kind(vertex_id) {
2319 VertexKind::Cell
2320 | VertexKind::FormulaScalar
2321 | VertexKind::FormulaArray
2322 | VertexKind::Empty => {
2323 self.vertex_values.remove(&vertex_id);
2324 return;
2325 }
2326 _ => {
2327 }
2329 }
2330 }
2331 let value_ref = self.data_store.store_value(normalize_stored_literal(value));
2332 self.vertex_values.insert(vertex_id, value_ref);
2333 }
2334
2335 pub fn plan_spill_region(
2337 &self,
2338 anchor: VertexId,
2339 target_cells: &[CellRef],
2340 ) -> Result<(), ExcelError> {
2341 self.plan_spill_region_allowing_formula_overwrite(anchor, target_cells, None)
2342 }
2343
2344 pub(crate) fn plan_spill_region_allowing_formula_overwrite(
2349 &self,
2350 anchor: VertexId,
2351 target_cells: &[CellRef],
2352 overwritable_formulas: Option<&rustc_hash::FxHashSet<VertexId>>,
2353 ) -> Result<(), ExcelError> {
2354 use formualizer_common::{ExcelErrorExtra, ExcelErrorKind};
2355 let (expected_rows, expected_cols) = if target_cells.is_empty() {
2357 (0u32, 0u32)
2358 } else {
2359 let mut min_r = u32::MAX;
2360 let mut max_r = 0u32;
2361 let mut min_c = u32::MAX;
2362 let mut max_c = 0u32;
2363 for cell in target_cells {
2364 let r = cell.coord.row();
2365 let c = cell.coord.col();
2366 if r < min_r {
2367 min_r = r;
2368 }
2369 if r > max_r {
2370 max_r = r;
2371 }
2372 if c < min_c {
2373 min_c = c;
2374 }
2375 if c > max_c {
2376 max_c = c;
2377 }
2378 }
2379 (
2380 max_r.saturating_sub(min_r).saturating_add(1),
2381 max_c.saturating_sub(min_c).saturating_add(1),
2382 )
2383 };
2384 for cell in target_cells {
2386 let owned_by_anchor = match self.spill_cell_to_anchor.get(cell) {
2388 Some(&existing_anchor) if existing_anchor == anchor => true,
2389 Some(_other) => {
2390 return Err(ExcelError::new(ExcelErrorKind::Spill)
2391 .with_message("BlockedBySpill")
2392 .with_extra(ExcelErrorExtra::Spill {
2393 expected_rows,
2394 expected_cols,
2395 }));
2396 }
2397 None => false,
2398 };
2399
2400 if owned_by_anchor {
2401 continue;
2402 }
2403
2404 if let Some(&vid) = self.cell_to_vertex.get(cell)
2406 && vid != anchor
2407 {
2408 match self.store.kind(vid) {
2410 VertexKind::FormulaScalar | VertexKind::FormulaArray => {
2411 if let Some(allow) = overwritable_formulas
2412 && allow.contains(&vid)
2413 {
2414 continue;
2415 }
2416 return Err(ExcelError::new(ExcelErrorKind::Spill)
2417 .with_message("BlockedByFormula")
2418 .with_extra(ExcelErrorExtra::Spill {
2419 expected_rows,
2420 expected_cols,
2421 }));
2422 }
2423 _ => {
2424 if let Some(vref) = self.vertex_values.get(&vid) {
2426 let v = self.data_store.retrieve_value(*vref);
2427 if !matches!(v, LiteralValue::Empty) {
2428 return Err(ExcelError::new(ExcelErrorKind::Spill)
2429 .with_message("BlockedByValue")
2430 .with_extra(ExcelErrorExtra::Spill {
2431 expected_rows,
2432 expected_cols,
2433 }));
2434 }
2435 }
2436 }
2437 }
2438 }
2439 }
2440 Ok(())
2441 }
2442
2443 pub fn commit_spill_region_atomic_with_fault(
2450 &mut self,
2451 anchor: VertexId,
2452 target_cells: Vec<CellRef>,
2453 values: Vec<Vec<LiteralValue>>,
2454 fault_after_ops: Option<usize>,
2455 ) -> Result<(), ExcelError> {
2456 let anchor_cell = self
2460 .get_cell_ref(anchor)
2461 .expect("anchor cell ref for spill commit");
2462 let anchor_sheet_name = self.sheet_name(anchor_cell.sheet_id).to_string();
2463 let anchor_row = anchor_cell.coord.row();
2464 let anchor_col = anchor_cell.coord.col();
2465
2466 let prev_cells = self
2468 .spill_anchor_to_cells
2469 .get(&anchor)
2470 .cloned()
2471 .unwrap_or_default();
2472 let new_set: std::collections::HashSet<CellRef, CoordBuildHasher> =
2475 target_cells.iter().copied().collect();
2476 let prev_set: std::collections::HashSet<CellRef, CoordBuildHasher> =
2477 prev_cells.iter().copied().collect();
2478
2479 #[derive(Clone)]
2481 struct Op {
2482 sheet: String,
2483 row: u32,
2484 col: u32,
2485 new_value: LiteralValue,
2486 }
2487 let mut ops: Vec<Op> = Vec::new();
2488
2489 for cell in prev_cells.iter() {
2491 if !new_set.contains(cell) {
2492 let sheet = self.sheet_name(cell.sheet_id).to_string();
2493 ops.push(Op {
2494 sheet,
2495 row: cell.coord.row(),
2496 col: cell.coord.col(),
2497 new_value: LiteralValue::Empty,
2498 });
2499 }
2500 }
2501
2502 if !target_cells.is_empty() {
2504 let first = target_cells.first().copied().unwrap();
2505 let row0 = first.coord.row();
2506 let col0 = first.coord.col();
2507 let sheet = self.sheet_name(first.sheet_id).to_string();
2508 for (r_off, row_vals) in values.iter().enumerate() {
2509 for (c_off, v) in row_vals.iter().enumerate() {
2510 ops.push(Op {
2511 sheet: sheet.clone(),
2512 row: row0 + r_off as u32,
2513 col: col0 + c_off as u32,
2514 new_value: v.clone(),
2515 });
2516 }
2517 }
2518 }
2519
2520 #[derive(Clone)]
2522 struct OldVal {
2523 present: bool,
2524 value: LiteralValue,
2525 }
2526 let mut old_values: Vec<((String, u32, u32), OldVal)> = Vec::with_capacity(ops.len());
2527
2528 for op in &ops {
2530 let old = self
2532 .get_cell_value(&op.sheet, op.row + 1, op.col + 1)
2533 .unwrap_or(LiteralValue::Empty);
2534 let present = true; old_values.push((
2536 (op.sheet.clone(), op.row, op.col),
2537 OldVal {
2538 present,
2539 value: old,
2540 },
2541 ));
2542 }
2543
2544 for (applied, op) in ops.iter().enumerate() {
2546 if let Some(n) = fault_after_ops
2547 && applied == n
2548 {
2549 for idx in (0..applied).rev() {
2550 let ((ref sheet, row, col), ref old) = old_values[idx];
2551 if sheet == &anchor_sheet_name && row == anchor_row && col == anchor_col {
2552 self.update_vertex_value(anchor, old.value.clone());
2553 } else {
2554 let _ = self.set_cell_value(sheet, row + 1, col + 1, old.value.clone());
2555 }
2556 }
2557 return Err(ExcelError::new(ExcelErrorKind::Error)
2558 .with_message("Injected persistence fault during spill commit"));
2559 }
2560 if op.sheet == anchor_sheet_name && op.row == anchor_row && op.col == anchor_col {
2561 self.update_vertex_value(anchor, op.new_value.clone());
2562 } else {
2563 let _ =
2564 self.set_cell_value(&op.sheet, op.row + 1, op.col + 1, op.new_value.clone());
2565 }
2566 }
2567
2568 for cell in prev_cells.iter() {
2571 if !new_set.contains(cell) {
2572 self.spill_cell_to_anchor.remove(cell);
2573 }
2574 }
2575 for cell in &target_cells {
2577 self.spill_cell_to_anchor.insert(*cell, anchor);
2578 }
2579 self.spill_anchor_to_cells.insert(anchor, target_cells);
2580 Ok(())
2581 }
2582
2583 pub(crate) fn spill_cells_for_anchor(&self, anchor: VertexId) -> Option<&[CellRef]> {
2584 self.spill_anchor_to_cells
2585 .get(&anchor)
2586 .map(|v| v.as_slice())
2587 }
2588
2589 pub(crate) fn spill_registry_has_anchor(&self, anchor: VertexId) -> bool {
2590 self.spill_anchor_to_cells.contains_key(&anchor)
2591 }
2592
2593 pub(crate) fn spill_registry_anchor_for_cell(&self, cell: CellRef) -> Option<VertexId> {
2594 self.spill_cell_to_anchor.get(&cell).copied()
2595 }
2596
2597 pub(crate) fn spill_registry_counts(&self) -> (usize, usize) {
2598 (
2599 self.spill_anchor_to_cells.len(),
2600 self.spill_cell_to_anchor.len(),
2601 )
2602 }
2603
2604 pub fn clear_spill_region(&mut self, anchor: VertexId) {
2606 let _ = self.clear_spill_region_bulk(anchor);
2607 }
2608
2609 pub fn clear_spill_region_bulk(&mut self, anchor: VertexId) -> Vec<CellRef> {
2618 let anchor_cell = self.get_cell_ref(anchor);
2619 let Some(cells) = self.spill_anchor_to_cells.remove(&anchor) else {
2620 return Vec::new();
2621 };
2622
2623 for cell in cells.iter() {
2625 self.spill_cell_to_anchor.remove(cell);
2626 }
2627
2628 let empty_ref = if self.value_cache_enabled {
2630 Some(self.data_store.store_value(LiteralValue::Empty))
2631 } else {
2632 None
2633 };
2634
2635 let mut changed_vertices: Vec<VertexId> = Vec::new();
2637 for cell in cells.iter().copied() {
2638 let is_anchor = anchor_cell.map(|a| a == cell).unwrap_or(false);
2639 if is_anchor {
2640 continue;
2641 }
2642 let Some(&vid) = self.cell_to_vertex.get(&cell) else {
2643 continue;
2644 };
2645 if self.vertex_formulas.remove(&vid).is_some() {
2647 self.remove_dependent_edges(vid);
2650 }
2651 self.store.set_kind(vid, VertexKind::Cell);
2652 if let Some(er) = empty_ref {
2653 self.vertex_values.insert(vid, er);
2654 } else {
2655 self.vertex_values.remove(&vid);
2656 }
2657 self.store.set_dirty(vid, false);
2658 self.dirty_vertices.remove(&vid);
2659 changed_vertices.push(vid);
2660 }
2661
2662 if !changed_vertices.is_empty() {
2664 self.mark_dirty_many_value_cells(&changed_vertices);
2665 }
2666
2667 cells
2668 }
2669
2670 fn mark_dirty_many_value_cells(&mut self, vertex_ids: &[VertexId]) -> Vec<VertexId> {
2671 if vertex_ids.is_empty() {
2672 return Vec::new();
2673 }
2674
2675 if self.edges.delta_size() > 0 {
2677 self.edges.rebuild();
2678 }
2679
2680 let mut affected: FxHashSet<VertexId> = FxHashSet::default();
2681 let mut to_visit: Vec<VertexId> = Vec::new();
2682 let mut visited_for_propagation: FxHashSet<VertexId> = FxHashSet::default();
2683
2684 for &src in vertex_ids {
2686 affected.insert(src);
2687 }
2688
2689 for &src in vertex_ids {
2691 to_visit.extend(self.edges.in_edges(src));
2692 if let Some(name_set) = self.cell_to_name_dependents.get(&src) {
2693 for &name_vertex in name_set {
2694 to_visit.push(name_vertex);
2695 }
2696 }
2697 }
2698
2699 let mut bounds_by_sheet: FxHashMap<SheetId, (u32, u32, u32, u32)> = FxHashMap::default();
2701 for &src in vertex_ids {
2702 let view = self.store.view(src);
2703 let sid = view.sheet_id();
2704 let r = view.row();
2705 let c = view.col();
2706 bounds_by_sheet
2707 .entry(sid)
2708 .and_modify(|b| {
2709 b.0 = b.0.min(r);
2710 b.1 = b.1.max(r);
2711 b.2 = b.2.min(c);
2712 b.3 = b.3.max(c);
2713 })
2714 .or_insert((r, r, c, c));
2715 }
2716
2717 for (sid, (sr, er, sc, ec)) in bounds_by_sheet {
2718 to_visit.extend(self.collect_range_dependents_for_rect(sid, sr, sc, er, ec));
2719 }
2720
2721 while let Some(id) = to_visit.pop() {
2722 if !visited_for_propagation.insert(id) {
2723 continue;
2724 }
2725 affected.insert(id);
2726 self.store.set_dirty(id, true);
2727 to_visit.extend(self.edges.in_edges(id));
2728 to_visit.extend(self.collect_range_dependents_for_vertex(id));
2729 }
2730
2731 self.dirty_vertices.extend(&affected);
2732 affected.into_iter().collect()
2733 }
2734
2735 fn collect_range_dependents_for_vertex(&self, vertex_id: VertexId) -> Vec<VertexId> {
2736 match self.store.kind(vertex_id) {
2737 VertexKind::Cell
2738 | VertexKind::Empty
2739 | VertexKind::FormulaScalar
2740 | VertexKind::FormulaArray => {
2741 let view = self.store.view(vertex_id);
2742 self.collect_range_dependents_for_rect(
2743 view.sheet_id(),
2744 view.row(),
2745 view.col(),
2746 view.row(),
2747 view.col(),
2748 )
2749 }
2750 _ => Vec::new(),
2751 }
2752 }
2753
2754 fn collect_range_dependents_for_rect(
2755 &self,
2756 sheet_id: SheetId,
2757 start_row: u32,
2758 start_col: u32,
2759 end_row: u32,
2760 end_col: u32,
2761 ) -> Vec<VertexId> {
2762 if self.stripe_to_dependents.is_empty() {
2763 return Vec::new();
2764 }
2765 let mut candidates: FxHashSet<VertexId> = FxHashSet::default();
2766
2767 for col in start_col..=end_col {
2768 let key = StripeKey {
2769 sheet_id,
2770 stripe_type: StripeType::Column,
2771 index: col,
2772 };
2773 if let Some(deps) = self.stripe_to_dependents.get(&key) {
2774 candidates.extend(deps);
2775 }
2776 }
2777 for row in start_row..=end_row {
2778 let key = StripeKey {
2779 sheet_id,
2780 stripe_type: StripeType::Row,
2781 index: row,
2782 };
2783 if let Some(deps) = self.stripe_to_dependents.get(&key) {
2784 candidates.extend(deps);
2785 }
2786 }
2787 if self.config.enable_block_stripes {
2788 let br0 = start_row / BLOCK_H;
2789 let br1 = end_row / BLOCK_H;
2790 let bc0 = start_col / BLOCK_W;
2791 let bc1 = end_col / BLOCK_W;
2792 for br in br0..=br1 {
2793 for bc in bc0..=bc1 {
2794 let key = StripeKey {
2795 sheet_id,
2796 stripe_type: StripeType::Block,
2797 index: block_index(br * BLOCK_H, bc * BLOCK_W),
2798 };
2799 if let Some(deps) = self.stripe_to_dependents.get(&key) {
2800 candidates.extend(deps);
2801 }
2802 }
2803 }
2804 }
2805
2806 let mut out: Vec<VertexId> = Vec::new();
2808 for dep_id in candidates {
2809 let Some(ranges) = self.formula_to_range_deps.get(&dep_id) else {
2810 continue;
2811 };
2812 let mut hit = false;
2813 for range in ranges {
2814 let range_sheet_id = match range.sheet {
2815 SharedSheetLocator::Id(id) => id,
2816 _ => sheet_id,
2817 };
2818 if range_sheet_id != sheet_id {
2819 continue;
2820 }
2821 let sr0 = range.start_row.map(|b| b.index).unwrap_or(0);
2822 let er0 = range.end_row.map(|b| b.index).unwrap_or(u32::MAX);
2823 let sc0 = range.start_col.map(|b| b.index).unwrap_or(0);
2824 let ec0 = range.end_col.map(|b| b.index).unwrap_or(u32::MAX);
2825 let overlap =
2826 sr0 <= end_row && er0 >= start_row && sc0 <= end_col && ec0 >= start_col;
2827 if overlap {
2828 hit = true;
2829 break;
2830 }
2831 }
2832 if hit {
2833 out.push(dep_id);
2834 }
2835 }
2836 out
2837 }
2838
2839 pub(crate) fn vertex_exists(&self, vertex_id: VertexId) -> bool {
2841 if vertex_id.0 < FIRST_NORMAL_VERTEX {
2842 return false;
2843 }
2844 let index = (vertex_id.0 - FIRST_NORMAL_VERTEX) as usize;
2845 index < self.store.len()
2846 }
2847
2848 pub(crate) fn get_vertex_kind(&self, vertex_id: VertexId) -> VertexKind {
2850 self.store.kind(vertex_id)
2851 }
2852
2853 pub(crate) fn get_vertex_sheet_id(&self, vertex_id: VertexId) -> SheetId {
2855 self.store.sheet_id(vertex_id)
2856 }
2857
2858 pub fn get_formula_id(&self, vertex_id: VertexId) -> Option<AstNodeId> {
2859 self.vertex_formulas.get(&vertex_id).copied()
2860 }
2861
2862 pub fn get_formula_id_and_volatile(&self, vertex_id: VertexId) -> Option<(AstNodeId, bool)> {
2863 let ast_id = self.get_formula_id(vertex_id)?;
2864 Some((ast_id, self.is_volatile(vertex_id)))
2865 }
2866
2867 pub fn get_formula_node(&self, vertex_id: VertexId) -> Option<&super::arena::AstNodeData> {
2868 let ast_id = self.get_formula_id(vertex_id)?;
2869 self.data_store.get_node(ast_id)
2870 }
2871
2872 pub fn get_formula_node_and_volatile(
2873 &self,
2874 vertex_id: VertexId,
2875 ) -> Option<(&super::arena::AstNodeData, bool)> {
2876 let (ast_id, vol) = self.get_formula_id_and_volatile(vertex_id)?;
2877 let node = self.data_store.get_node(ast_id)?;
2878 Some((node, vol))
2879 }
2880
2881 pub fn get_formula(&self, vertex_id: VertexId) -> Option<ASTNode> {
2885 let ast_id = self.get_formula_id(vertex_id)?;
2886 self.data_store.retrieve_ast(ast_id, &self.sheet_reg)
2887 }
2888
2889 pub fn get_value(&self, vertex_id: VertexId) -> Option<LiteralValue> {
2891 if !self.value_cache_enabled {
2892 match self.store.kind(vertex_id) {
2895 VertexKind::Cell
2896 | VertexKind::FormulaScalar
2897 | VertexKind::FormulaArray
2898 | VertexKind::Empty => {
2899 #[cfg(debug_assertions)]
2900 {
2901 self.graph_value_read_attempts
2902 .fetch_add(1, Ordering::Relaxed);
2903 }
2904 return None;
2905 }
2906 _ => {
2907 }
2909 }
2910 }
2911 self.vertex_values
2912 .get(&vertex_id)
2913 .map(|&value_ref| self.data_store.retrieve_value(value_ref))
2914 }
2915
2916 pub(crate) fn get_cell_ref(&self, vertex_id: VertexId) -> Option<CellRef> {
2918 let packed_coord = self.store.coord(vertex_id);
2919 let sheet_id = self.store.sheet_id(vertex_id);
2920 let coord = Coord::new(packed_coord.row(), packed_coord.col(), true, true);
2921 Some(CellRef::new(sheet_id, coord))
2922 }
2923
2924 pub(crate) fn make_cell_ref_internal(&self, sheet_id: SheetId, row: u32, col: u32) -> CellRef {
2926 let coord = Coord::new(row, col, true, true);
2927 CellRef::new(sheet_id, coord)
2928 }
2929
2930 pub fn make_cell_ref(&self, sheet_name: &str, row: u32, col: u32) -> CellRef {
2932 let sheet_id = self.sheet_reg.get_id(sheet_name).unwrap_or(0);
2933 let coord = Coord::from_excel(row, col, true, true);
2934 CellRef::new(sheet_id, coord)
2935 }
2936
2937 pub(crate) fn is_dirty(&self, vertex_id: VertexId) -> bool {
2939 self.store.is_dirty(vertex_id)
2940 }
2941
2942 pub(crate) fn is_volatile(&self, vertex_id: VertexId) -> bool {
2944 self.store.is_volatile(vertex_id)
2945 }
2946
2947 pub(crate) fn is_dynamic(&self, vertex_id: VertexId) -> bool {
2948 self.store.is_dynamic(vertex_id)
2949 }
2950
2951 pub fn get_vertex_id_for_address(&self, addr: &CellRef) -> Option<&VertexId> {
2953 self.cell_to_vertex.get(addr)
2954 }
2955
2956 #[cfg(test)]
2957 pub fn cell_to_vertex(
2958 &self,
2959 ) -> &std::collections::HashMap<CellRef, VertexId, CoordBuildHasher> {
2960 &self.cell_to_vertex
2961 }
2962
2963 #[inline]
2967 pub(crate) fn dependencies_slice(&self, vertex_id: VertexId) -> Option<&[VertexId]> {
2968 self.edges.out_edges_ref(vertex_id)
2969 }
2970
2971 pub(crate) fn get_dependencies(&self, vertex_id: VertexId) -> Vec<VertexId> {
2973 self.edges.out_edges(vertex_id)
2974 }
2975
2976 pub(crate) fn has_self_loop(&self, vertex_id: VertexId) -> bool {
2978 if let Some(deps) = self.dependencies_slice(vertex_id) {
2979 deps.contains(&vertex_id)
2980 } else {
2981 self.edges.out_edges(vertex_id).contains(&vertex_id)
2982 }
2983 }
2984
2985 #[inline]
2989 pub(crate) fn dependents_slice(&self, vertex_id: VertexId) -> Option<&[VertexId]> {
2990 self.edges.in_edges_ref(vertex_id)
2991 }
2992
2993 pub(crate) fn get_dependents(&self, vertex_id: VertexId) -> Vec<VertexId> {
2996 if self.edges.delta_size() > 0 {
2999 #[cfg(test)]
3000 {
3001 if let Ok(mut g) = self.instr.lock() {
3004 g.dependents_scan_fallback_calls += 1;
3005 g.dependents_scan_vertices_scanned += self.cell_to_vertex.len() as u64;
3006 }
3007 }
3008 let mut dependents = Vec::new();
3010 for (&_addr, &vid) in &self.cell_to_vertex {
3011 let out_edges = self.edges.out_edges(vid);
3012 if out_edges.contains(&vertex_id) {
3013 dependents.push(vid);
3014 }
3015 }
3016 for named in self.named_ranges.values() {
3017 let vid = named.vertex;
3018 let out_edges = self.edges.out_edges(vid);
3019 if out_edges.contains(&vertex_id) {
3020 dependents.push(vid);
3021 }
3022 }
3023 for named in self.sheet_named_ranges.values() {
3024 let vid = named.vertex;
3025 let out_edges = self.edges.out_edges(vid);
3026 if out_edges.contains(&vertex_id) {
3027 dependents.push(vid);
3028 }
3029 }
3030 dependents
3031 } else {
3032 self.edges.in_edges(vertex_id).to_vec()
3034 }
3035 }
3036
3037 #[doc(hidden)]
3041 pub fn snapshot_vertex(&self, id: VertexId) -> crate::engine::VertexSnapshot {
3042 let coord = self.store.coord(id);
3043 let sheet_id = self.store.sheet_id(id);
3044 let kind = self.store.kind(id);
3045 let flags = self.store.flags(id);
3046
3047 let value_ref = self.vertex_values.get(&id).copied();
3049 let formula_ref = self.vertex_formulas.get(&id).copied();
3050
3051 let out_edges = self.get_dependencies(id);
3053
3054 crate::engine::VertexSnapshot {
3055 coord,
3056 sheet_id,
3057 kind,
3058 flags,
3059 value_ref,
3060 formula_ref,
3061 out_edges,
3062 }
3063 }
3064
3065 #[doc(hidden)]
3067 pub fn remove_all_edges(&mut self, id: VertexId) {
3068 self.edges.begin_batch();
3070
3071 self.remove_dependent_edges(id);
3073
3074 self.edges.rebuild();
3077
3078 let dependents = self.get_dependents(id);
3080 if self.pk_order.is_some()
3081 && let Some(mut pk) = self.pk_order.take()
3082 {
3083 for dependent in &dependents {
3084 pk.remove_edge(id, *dependent);
3085 }
3086 self.pk_order = Some(pk);
3087 }
3088 for dependent in dependents {
3089 self.edges.remove_edge(dependent, id);
3090 }
3091
3092 self.edges.end_batch();
3094 }
3095
3096 #[doc(hidden)]
3098 pub fn mark_as_ref_error(&mut self, id: VertexId) {
3099 if !self.value_cache_enabled {
3100 match self.store.kind(id) {
3101 VertexKind::Cell
3102 | VertexKind::FormulaScalar
3103 | VertexKind::FormulaArray
3104 | VertexKind::Empty => {
3105 self.ref_error_vertices.insert(id);
3106 self.vertex_values.remove(&id);
3109 let _ = self.mark_dirty(id);
3110 return;
3111 }
3112 _ => {
3113 }
3115 }
3116 }
3117 let error = LiteralValue::Error(ExcelError::new(ExcelErrorKind::Ref));
3118 let value_ref = self.data_store.store_value(error);
3119 self.vertex_values.insert(id, value_ref);
3120 let _ = self.mark_dirty(id);
3121 }
3122
3123 pub fn is_ref_error(&self, id: VertexId) -> bool {
3125 if !self.value_cache_enabled {
3126 match self.store.kind(id) {
3127 VertexKind::Cell
3128 | VertexKind::FormulaScalar
3129 | VertexKind::FormulaArray
3130 | VertexKind::Empty => {
3131 return self.ref_error_vertices.contains(&id);
3132 }
3133 _ => {
3134 }
3136 }
3137 }
3138 if let Some(value_ref) = self.vertex_values.get(&id) {
3139 let value = self.data_store.retrieve_value(*value_ref);
3140 if let LiteralValue::Error(err) = value {
3141 return err.kind == ExcelErrorKind::Ref;
3142 }
3143 }
3144 false
3145 }
3146
3147 #[doc(hidden)]
3149 pub fn mark_dependents_dirty(&mut self, id: VertexId) {
3150 let dependents = self.get_dependents(id);
3151 for dep_id in dependents {
3152 self.store.set_dirty(dep_id, true);
3153 self.dirty_vertices.insert(dep_id);
3154 }
3155 }
3156
3157 #[doc(hidden)]
3159 pub fn mark_volatile(&mut self, id: VertexId, volatile: bool) {
3160 self.store.set_volatile(id, volatile);
3161 if volatile {
3162 self.volatile_vertices.insert(id);
3163 } else {
3164 self.volatile_vertices.remove(&id);
3165 }
3166 }
3167
3168 #[doc(hidden)]
3170 pub fn set_coord(&mut self, id: VertexId, coord: AbsCoord) {
3171 self.store.set_coord(id, coord);
3172 }
3173
3174 #[doc(hidden)]
3176 pub fn update_edge_coord(&mut self, id: VertexId, coord: AbsCoord) {
3177 self.edges.update_coord(id, coord);
3178 }
3179
3180 #[doc(hidden)]
3182 pub fn mark_deleted(&mut self, id: VertexId, deleted: bool) {
3183 self.store.mark_deleted(id, deleted);
3184 }
3185
3186 #[doc(hidden)]
3188 pub fn set_kind(&mut self, id: VertexId, kind: VertexKind) {
3189 self.store.set_kind(id, kind);
3190 }
3191
3192 #[doc(hidden)]
3194 pub fn set_dirty(&mut self, id: VertexId, dirty: bool) {
3195 self.store.set_dirty(id, dirty);
3196 if dirty {
3197 self.dirty_vertices.insert(id);
3198 } else {
3199 self.dirty_vertices.remove(&id);
3200 }
3201 }
3202
3203 #[cfg(test)]
3205 pub(crate) fn get_kind(&self, id: VertexId) -> VertexKind {
3206 self.store.kind(id)
3207 }
3208
3209 #[cfg(test)]
3211 pub(crate) fn get_flags(&self, id: VertexId) -> u8 {
3212 self.store.flags(id)
3213 }
3214
3215 #[cfg(test)]
3217 pub(crate) fn is_deleted(&self, id: VertexId) -> bool {
3218 self.store.is_deleted(id)
3219 }
3220
3221 #[doc(hidden)]
3223 pub fn rebuild_edges(&mut self) {
3224 self.edges.rebuild();
3225 }
3226
3227 #[doc(hidden)]
3229 pub fn edges_delta_size(&self) -> usize {
3230 self.edges.delta_size()
3231 }
3232
3233 pub fn get_vertex_for_cell(&self, addr: &CellRef) -> Option<VertexId> {
3235 self.cell_to_vertex.get(addr).copied()
3236 }
3237
3238 pub fn get_coord(&self, id: VertexId) -> AbsCoord {
3240 self.store.coord(id)
3241 }
3242
3243 pub fn get_sheet_id(&self, id: VertexId) -> SheetId {
3245 self.store.sheet_id(id)
3246 }
3247
3248 pub fn vertices_in_sheet(&self, sheet_id: SheetId) -> impl Iterator<Item = VertexId> + '_ {
3250 self.store
3251 .all_vertices()
3252 .filter(move |&id| self.vertex_exists(id) && self.store.sheet_id(id) == sheet_id)
3253 }
3254
3255 pub fn vertex_has_formula(&self, id: VertexId) -> bool {
3257 self.vertex_formulas.contains_key(&id)
3258 }
3259
3260 pub fn vertices_with_formulas(&self) -> impl Iterator<Item = VertexId> + '_ {
3262 self.vertex_formulas.keys().copied()
3263 }
3264
3265 pub fn update_vertex_formula(&mut self, id: VertexId, ast: ASTNode) -> Result<(), ExcelError> {
3267 let sheet_id = self.store.sheet_id(id);
3269
3270 let has_ref_marker = ast.get_dependencies().into_iter().any(|r| {
3274 matches!(
3275 r,
3276 ReferenceType::Cell { sheet: Some(s), .. }
3277 | ReferenceType::Range { sheet: Some(s), .. } if s == "#REF"
3278 )
3279 });
3280 if has_ref_marker {
3281 let ast_id = self.data_store.store_ast(&ast, &self.sheet_reg);
3283 self.vertex_formulas.insert(id, ast_id);
3284 self.mark_as_ref_error(id);
3285 self.store.set_kind(id, VertexKind::FormulaScalar);
3286 return Ok(());
3287 }
3288
3289 let (new_dependencies, new_range_dependencies, _, named_dependencies) =
3291 self.extract_dependencies(&ast, sheet_id)?;
3292
3293 self.remove_dependent_edges(id);
3295 self.detach_vertex_from_names(id);
3296
3297 let ast_id = self.data_store.store_ast(&ast, &self.sheet_reg);
3299 self.vertex_formulas.insert(id, ast_id);
3300
3301 self.add_dependent_edges(id, &new_dependencies);
3303 self.add_range_dependent_edges(id, &new_range_dependencies, sheet_id);
3304
3305 if !named_dependencies.is_empty() {
3306 self.attach_vertex_to_names(id, &named_dependencies);
3307 }
3308
3309 self.store.set_kind(id, VertexKind::FormulaScalar);
3311
3312 Ok(())
3313 }
3314
3315 pub fn mark_vertex_dirty(&mut self, vertex_id: VertexId) {
3317 self.store.set_dirty(vertex_id, true);
3318 self.dirty_vertices.insert(vertex_id);
3319 }
3320
3321 pub fn mark_vertices_dirty_batch(&mut self, vertices: &[VertexId]) {
3323 self.dirty_vertices.reserve(vertices.len());
3324 for &vertex_id in vertices {
3325 self.store.set_dirty(vertex_id, true);
3326 }
3327 self.dirty_vertices.extend(vertices.iter().copied());
3328 }
3329
3330 pub fn update_cell_mapping(
3332 &mut self,
3333 id: VertexId,
3334 old_addr: Option<CellRef>,
3335 new_addr: CellRef,
3336 ) {
3337 if let Some(old) = old_addr {
3339 self.cell_to_vertex.remove(&old);
3340 }
3341 self.cell_to_vertex.insert(new_addr, id);
3343 }
3344
3345 pub fn remove_cell_mapping(&mut self, addr: &CellRef) {
3347 self.cell_to_vertex.remove(addr);
3348 }
3349
3350 pub fn get_cell_ref_for_vertex(&self, id: VertexId) -> Option<CellRef> {
3352 let coord = self.store.coord(id);
3353 let sheet_id = self.store.sheet_id(id);
3354 let cell_ref = CellRef::new(sheet_id, Coord::new(coord.row(), coord.col(), true, true));
3356 if self.cell_to_vertex.get(&cell_ref) == Some(&id) {
3358 Some(cell_ref)
3359 } else {
3360 None
3361 }
3362 }
3363
3364 pub(crate) fn rebuild_formula_dependencies(&mut self, vertex_id: VertexId, ast: &ASTNode) {
3370 let sheet_id = self.store.sheet_id(vertex_id);
3371
3372 self.remove_dependent_edges(vertex_id);
3374 self.detach_vertex_from_names(vertex_id);
3375 self.clear_pending_name_references(vertex_id);
3376
3377 let (
3378 new_dependencies,
3379 new_range_dependencies,
3380 _created_placeholders,
3381 named_dependencies,
3382 unresolved_names,
3383 ) = match self.extract_dependencies_with_pending_names(ast, sheet_id) {
3384 Ok(v) => v,
3385 Err(_) => {
3386 self.mark_as_ref_error(vertex_id);
3387 return;
3388 }
3389 };
3390
3391 if new_dependencies.contains(&vertex_id) {
3393 self.mark_as_ref_error(vertex_id);
3394 return;
3395 }
3396
3397 for &name_vertex in &named_dependencies {
3398 let mut visited = FxHashSet::default();
3399 if self.name_depends_on_vertex(name_vertex, vertex_id, &mut visited) {
3400 self.mark_as_ref_error(vertex_id);
3401 return;
3402 }
3403 }
3404
3405 self.ref_error_vertices.remove(&vertex_id);
3407 self.vertex_values.remove(&vertex_id);
3408
3409 if !named_dependencies.is_empty() {
3410 self.attach_vertex_to_names(vertex_id, &named_dependencies);
3411 }
3412 for unresolved_name in &unresolved_names {
3413 self.record_pending_name_reference(sheet_id, unresolved_name, vertex_id);
3414 }
3415
3416 self.add_dependent_edges(vertex_id, &new_dependencies);
3417 self.add_range_dependent_edges(vertex_id, &new_range_dependencies, sheet_id);
3418 let _ = self.mark_dirty(vertex_id);
3419 }
3420}
3421
3422