1use crate::SheetId;
2use crate::engine::TombstoneRegistry;
3use crate::engine::named_range::{NameScope, NamedDefinition, NamedRange};
4use crate::engine::sheet_registry::SheetRegistry;
5use formualizer_common::{ExcelError, ExcelErrorKind, LiteralValue};
6use formualizer_parse::parser::{ASTNode, ASTNodeType, ReferenceType};
7use rustc_hash::{FxHashMap, FxHashSet};
8
9#[cfg(debug_assertions)]
10use std::sync::atomic::{AtomicU64, Ordering};
11
12#[cfg(test)]
13#[derive(Debug, Default, Clone)]
14pub struct GraphInstrumentation {
15 pub edges_added: u64,
16 pub stripe_inserts: u64,
17 pub stripe_removes: u64,
18 pub dependents_scan_fallback_calls: u64,
19 pub dependents_scan_vertices_scanned: u64,
20}
21
22mod ast_utils;
23pub mod editor;
24mod formula_analysis;
25mod names;
26mod range_deps;
27mod sheets;
28pub mod snapshot;
29mod sources;
30mod tables;
31
32use super::arena::{AstNodeId, DataStore, ValueRef};
33use super::delta_edges::CsrMutableEdges;
34use super::sheet_index::SheetIndex;
35use super::vertex::{VertexId, VertexKind};
36use super::vertex_store::{FIRST_NORMAL_VERTEX, VertexStore};
37use crate::engine::topo::{
38 GraphAdapter,
39 pk::{DynamicTopo, PkConfig},
40};
41use crate::reference::{CellRef, Coord, SharedRangeRef, SharedRef, SharedSheetLocator};
42use formualizer_common::Coord as AbsCoord;
43#[inline]
46fn normalize_stored_literal(value: LiteralValue) -> LiteralValue {
47 match value {
48 LiteralValue::Int(i) => LiteralValue::Number(i as f64),
50 other => other,
51 }
52}
53
54pub use editor::change_log::{ChangeEvent, ChangeLog};
55
56#[derive(Debug, Clone, PartialEq, Eq, Hash)]
60pub enum DependencyRef {
61 Cell(VertexId),
63 Range {
65 sheet: String,
66 start_row: u32,
67 start_col: u32,
68 end_row: u32, end_col: u32, },
71 WholeColumn { sheet: String, col: u32 },
73 WholeRow { sheet: String, row: u32 },
75}
76
77#[derive(Debug, Clone, Hash, PartialEq, Eq)]
79pub struct StripeKey {
80 pub sheet_id: SheetId,
81 pub stripe_type: StripeType,
82 pub index: u32, }
84
85#[derive(Debug, Clone, Hash, PartialEq, Eq)]
86pub enum StripeType {
87 Row,
88 Column,
89 Block, }
91
92const BLOCK_H: u32 = 256;
94const BLOCK_W: u32 = 256;
95
96pub fn block_index(row: u32, col: u32) -> u32 {
97 (row / BLOCK_H) << 16 | (col / BLOCK_W)
98}
99
100#[derive(Debug, Clone)]
103pub struct OperationSummary {
104 pub affected_vertices: Vec<VertexId>,
106 pub created_placeholders: Vec<CellRef>,
108}
109
110#[derive(Debug)]
112pub struct DependencyGraph {
113 store: VertexStore,
115
116 edges: CsrMutableEdges,
118
119 data_store: DataStore,
121 vertex_values: FxHashMap<VertexId, ValueRef>,
122 vertex_formulas: FxHashMap<VertexId, AstNodeId>,
123
124 value_cache_enabled: bool,
129
130 #[cfg(debug_assertions)]
133 graph_value_read_attempts: AtomicU64,
134
135 cell_to_vertex: FxHashMap<CellRef, VertexId>,
137
138 dirty_vertices: FxHashSet<VertexId>,
140 volatile_vertices: FxHashSet<VertexId>,
141
142 ref_error_vertices: FxHashSet<VertexId>,
148
149 formula_to_range_deps: FxHashMap<VertexId, Vec<SharedRangeRef<'static>>>,
152
153 stripe_to_dependents: FxHashMap<StripeKey, FxHashSet<VertexId>>,
156
157 sheet_indexes: FxHashMap<SheetId, SheetIndex>,
160
161 sheet_reg: SheetRegistry,
163 default_sheet_id: SheetId,
164
165 named_ranges: FxHashMap<String, NamedRange>,
168
169 named_ranges_lookup: FxHashMap<String, String>,
174
175 sheet_named_ranges: FxHashMap<(SheetId, String), NamedRange>,
177
178 sheet_named_ranges_lookup: FxHashMap<(SheetId, String), String>,
183
184 vertex_to_names: FxHashMap<VertexId, Vec<VertexId>>,
186
187 name_vertex_lookup: FxHashMap<VertexId, (NameScope, String)>,
189
190 pending_name_links: FxHashMap<String, FxHashSet<(SheetId, VertexId)>>,
195
196 vertex_to_pending_names: FxHashMap<VertexId, FxHashSet<String>>,
199
200 tables: FxHashMap<String, tables::TableEntry>,
202 tables_lookup: FxHashMap<String, String>,
204 table_vertex_lookup: FxHashMap<VertexId, String>,
205
206 source_scalars: FxHashMap<String, sources::SourceScalarEntry>,
208 source_tables: FxHashMap<String, sources::SourceTableEntry>,
209 source_vertex_lookup: FxHashMap<VertexId, String>,
210
211 name_vertex_seq: u32,
213
214 source_vertex_seq: u32,
216
217 cell_to_name_dependents: FxHashMap<VertexId, FxHashSet<VertexId>>,
219 name_to_cell_dependencies: FxHashMap<VertexId, Vec<VertexId>>,
221
222 config: super::EvalConfig,
224
225 pk_order: Option<DynamicTopo<VertexId>>,
227
228 spill_anchor_to_cells: FxHashMap<VertexId, Vec<CellRef>>,
230 spill_cell_to_anchor: FxHashMap<CellRef, VertexId>,
231
232 first_load_assume_new: bool,
234 ensure_touched_sheets: FxHashSet<SheetId>,
235
236 pub tombstone_registry: TombstoneRegistry,
238
239 #[cfg(test)]
240 instr: std::sync::Mutex<GraphInstrumentation>,
241}
242
243impl Default for DependencyGraph {
244 fn default() -> Self {
245 Self::new()
246 }
247}
248
249impl DependencyGraph {
250 pub fn range_expansion_limit(&self) -> usize {
252 self.config.range_expansion_limit
253 }
254
255 pub fn get_config(&self) -> &super::EvalConfig {
256 &self.config
257 }
258
259 #[inline]
260 pub(crate) fn value_cache_enabled(&self) -> bool {
261 self.value_cache_enabled
262 }
263
264 #[cfg(test)]
268 pub fn debug_graph_value_read_attempts(&self) -> u64 {
269 #[cfg(debug_assertions)]
270 {
271 self.graph_value_read_attempts.load(Ordering::Relaxed)
272 }
273 #[cfg(not(debug_assertions))]
274 {
275 0
276 }
277 }
278
279 pub fn plan_dependencies<'a, I>(
281 &mut self,
282 items: I,
283 policy: &formualizer_parse::parser::CollectPolicy,
284 volatile: Option<&[bool]>,
285 ) -> Result<crate::engine::plan::DependencyPlan, formualizer_common::ExcelError>
286 where
287 I: IntoIterator<Item = (&'a str, u32, u32, &'a formualizer_parse::parser::ASTNode)>,
288 {
289 crate::engine::plan::build_dependency_plan(
290 &mut self.sheet_reg,
291 items.into_iter(),
292 policy,
293 volatile,
294 )
295 }
296
297 pub fn ensure_vertices_batch(
300 &mut self,
301 coords: &[(SheetId, AbsCoord)],
302 ) -> Vec<(AbsCoord, u32)> {
303 use rustc_hash::FxHashMap;
304 let mut grouped: FxHashMap<SheetId, Vec<AbsCoord>> = FxHashMap::default();
305 for (sid, pc) in coords.iter().copied() {
306 let addr = CellRef::new(sid, Coord::new(pc.row(), pc.col(), true, true));
307 if self.cell_to_vertex.contains_key(&addr) {
308 continue;
309 }
310 grouped.entry(sid).or_default().push(pc);
311 }
312 let mut add_batch: Vec<(AbsCoord, u32)> = Vec::new();
313 for (sid, pcs) in grouped {
314 if pcs.is_empty() {
315 continue;
316 }
317 self.ensure_touched_sheets.insert(sid);
319 let vids = self.store.allocate_contiguous(sid, &pcs, 0x00);
320 for (i, pc) in pcs.iter().enumerate() {
321 let vid = vids[i];
322 add_batch.push((*pc, vid.0));
323 let addr = CellRef::new(sid, Coord::new(pc.row(), pc.col(), true, true));
324 self.cell_to_vertex.insert(addr, vid);
325 match self.config.sheet_index_mode {
327 crate::engine::SheetIndexMode::Eager
328 | crate::engine::SheetIndexMode::FastBatch => {
329 self.sheet_index_mut(sid).add_vertex(*pc, vid);
330 }
331 crate::engine::SheetIndexMode::Lazy => {
332 }
334 }
335 }
336 }
337 if !add_batch.is_empty() {
338 self.edges.add_vertices_batch(&add_batch);
339 }
340 add_batch
341 }
342
343 pub fn set_first_load_assume_new(&mut self, enabled: bool) {
345 self.first_load_assume_new = enabled;
346 }
347
348 pub fn reset_ensure_touched(&mut self) {
350 self.ensure_touched_sheets.clear();
351 }
352
353 pub fn store_asts_batch<'a, I>(&mut self, asts: I) -> Vec<AstNodeId>
355 where
356 I: IntoIterator<Item = &'a formualizer_parse::parser::ASTNode>,
357 {
358 self.data_store.store_asts_batch(asts, &self.sheet_reg)
359 }
360
361 pub fn vid_for_sid_pc(&self, sid: SheetId, pc: AbsCoord) -> Option<VertexId> {
363 let addr = CellRef::new(sid, Coord::new(pc.row(), pc.col(), true, true));
364 self.cell_to_vertex.get(&addr).copied()
365 }
366
367 pub fn vid_for_plan_idx(
369 &self,
370 plan: &crate::engine::plan::DependencyPlan,
371 idx: u32,
372 ) -> Option<VertexId> {
373 let (sid, pc) = plan.global_cells.get(idx as usize).copied()?;
374 self.vid_for_sid_pc(sid, pc)
375 }
376 pub fn assign_formula_vertex(
378 &mut self,
379 vid: VertexId,
380 ast_id: AstNodeId,
381 volatile: bool,
382 dynamic: bool,
383 ) {
384 if self.vertex_formulas.contains_key(&vid) {
385 self.remove_dependent_edges(vid);
386 }
387 self.store
388 .set_kind(vid, crate::engine::vertex::VertexKind::FormulaScalar);
389 self.vertex_values.remove(&vid);
390 self.vertex_formulas.insert(vid, ast_id);
391 self.mark_volatile(vid, volatile);
392 self.store.set_dynamic(vid, dynamic);
393
394 self.mark_vertex_dirty(vid);
396 }
397
398 pub fn add_edges_nobatch(&mut self, dependent: VertexId, dependencies: &[VertexId]) {
400 self.add_dependent_edges_nobatch(dependent, dependencies);
401 }
402
403 pub fn iter_vertex_ids(&self) -> impl Iterator<Item = VertexId> + '_ {
405 self.store.all_vertices()
406 }
407
408 pub fn vertex_coord(&self, vid: VertexId) -> AbsCoord {
410 self.store.coord(vid)
411 }
412
413 pub fn vertex_count(&self) -> usize {
415 self.store.len()
416 }
417
418 pub fn build_edges_from_adjacency(
420 &mut self,
421 adjacency: Vec<(u32, Vec<u32>)>,
422 coords: Vec<AbsCoord>,
423 vertex_ids: Vec<u32>,
424 ) {
425 self.edges
426 .build_from_adjacency(adjacency, coords, vertex_ids);
427 }
428 pub fn used_row_bounds_for_columns(
430 &self,
431 sheet_id: SheetId,
432 start_col: u32,
433 end_col: u32,
434 ) -> Option<(u32, u32)> {
435 if let Some(index) = self.sheet_indexes.get(&sheet_id)
437 && !index.is_empty()
438 {
439 let mut min_r: Option<u32> = None;
440 let mut max_r: Option<u32> = None;
441 for vid in index.vertices_in_col_range(start_col, end_col) {
442 let r = self.store.coord(vid).row();
443 min_r = Some(min_r.map(|m| m.min(r)).unwrap_or(r));
444 max_r = Some(max_r.map(|m| m.max(r)).unwrap_or(r));
445 }
446 return match (min_r, max_r) {
447 (Some(a), Some(b)) => Some((a, b)),
448 _ => None,
449 };
450 }
451 let mut min_r: Option<u32> = None;
453 let mut max_r: Option<u32> = None;
454 for cref in self.cell_to_vertex.keys() {
455 if cref.sheet_id == sheet_id {
456 let c = cref.coord.col();
457 if c >= start_col && c <= end_col {
458 let r = cref.coord.row();
459 min_r = Some(min_r.map(|m| m.min(r)).unwrap_or(r));
460 max_r = Some(max_r.map(|m| m.max(r)).unwrap_or(r));
461 }
462 }
463 }
464 match (min_r, max_r) {
465 (Some(a), Some(b)) => Some((a, b)),
466 _ => None,
467 }
468 }
469
470 pub fn finalize_sheet_index(&mut self, sheet: &str) {
472 let Some(sheet_id) = self.sheet_reg.get_id(sheet) else {
473 return;
474 };
475 if let Some(idx) = self.sheet_indexes.get(&sheet_id)
477 && !idx.is_empty()
478 {
479 return;
480 }
481 let mut idx = SheetIndex::new();
482 let mut batch: Vec<(AbsCoord, VertexId)> = Vec::with_capacity(self.cell_to_vertex.len());
484 for (cref, vid) in &self.cell_to_vertex {
485 if cref.sheet_id == sheet_id {
486 batch.push((AbsCoord::new(cref.coord.row(), cref.coord.col()), *vid));
487 }
488 }
489 idx.add_vertices_batch(&batch);
491 self.sheet_indexes.insert(sheet_id, idx);
492 }
493
494 pub fn set_sheet_index_mode(&mut self, mode: crate::engine::SheetIndexMode) {
495 self.config.sheet_index_mode = mode;
496 }
497
498 pub fn used_col_bounds_for_rows(
500 &self,
501 sheet_id: SheetId,
502 start_row: u32,
503 end_row: u32,
504 ) -> Option<(u32, u32)> {
505 if let Some(index) = self.sheet_indexes.get(&sheet_id)
506 && !index.is_empty()
507 {
508 let mut min_c: Option<u32> = None;
509 let mut max_c: Option<u32> = None;
510 for vid in index.vertices_in_row_range(start_row, end_row) {
511 let c = self.store.coord(vid).col();
512 min_c = Some(min_c.map(|m| m.min(c)).unwrap_or(c));
513 max_c = Some(max_c.map(|m| m.max(c)).unwrap_or(c));
514 }
515 return match (min_c, max_c) {
516 (Some(a), Some(b)) => Some((a, b)),
517 _ => None,
518 };
519 }
520 let mut min_c: Option<u32> = None;
522 let mut max_c: Option<u32> = None;
523 for cref in self.cell_to_vertex.keys() {
524 if cref.sheet_id == sheet_id {
525 let r = cref.coord.row();
526 if r >= start_row && r <= end_row {
527 let c = cref.coord.col();
528 min_c = Some(min_c.map(|m| m.min(c)).unwrap_or(c));
529 max_c = Some(max_c.map(|m| m.max(c)).unwrap_or(c));
530 }
531 }
532 }
533 match (min_c, max_c) {
534 (Some(a), Some(b)) => Some((a, b)),
535 _ => None,
536 }
537 }
538
539 pub fn sheet_has_formulas(&self, sheet_id: SheetId) -> bool {
541 for &vid in self.vertex_formulas.keys() {
543 if self.store.sheet_id(vid) == sheet_id {
544 return true;
545 }
546 }
547 false
548 }
549 pub fn new() -> Self {
550 Self::new_with_config(super::EvalConfig::default())
551 }
552
553 pub fn new_with_config(config: super::EvalConfig) -> Self {
554 let mut sheet_reg = SheetRegistry::new();
555 let default_sheet_id = sheet_reg.id_for(&config.default_sheet_name);
556
557 let mut g = Self {
558 store: VertexStore::new(),
559 edges: CsrMutableEdges::new(),
560 data_store: DataStore::new(),
561 vertex_values: FxHashMap::default(),
562 vertex_formulas: FxHashMap::default(),
563 value_cache_enabled: false,
566 #[cfg(debug_assertions)]
567 graph_value_read_attempts: AtomicU64::new(0),
568 cell_to_vertex: FxHashMap::default(),
569 dirty_vertices: FxHashSet::default(),
570 volatile_vertices: FxHashSet::default(),
571 ref_error_vertices: FxHashSet::default(),
572 formula_to_range_deps: FxHashMap::default(),
573 stripe_to_dependents: FxHashMap::default(),
574 sheet_indexes: FxHashMap::default(),
575 sheet_reg,
576 default_sheet_id,
577 named_ranges: FxHashMap::default(),
578 named_ranges_lookup: FxHashMap::default(),
579 sheet_named_ranges: FxHashMap::default(),
580 sheet_named_ranges_lookup: FxHashMap::default(),
581 vertex_to_names: FxHashMap::default(),
582 name_vertex_lookup: FxHashMap::default(),
583 pending_name_links: FxHashMap::default(),
584 vertex_to_pending_names: FxHashMap::default(),
585 tables: FxHashMap::default(),
586 tables_lookup: FxHashMap::default(),
587 table_vertex_lookup: FxHashMap::default(),
588 source_scalars: FxHashMap::default(),
589 source_tables: FxHashMap::default(),
590 source_vertex_lookup: FxHashMap::default(),
591 name_vertex_seq: 0,
592 source_vertex_seq: 0,
593 cell_to_name_dependents: FxHashMap::default(),
594 name_to_cell_dependencies: FxHashMap::default(),
595 config: config.clone(),
596 pk_order: None,
597 spill_anchor_to_cells: FxHashMap::default(),
598 spill_cell_to_anchor: FxHashMap::default(),
599 first_load_assume_new: false,
600 ensure_touched_sheets: FxHashSet::default(),
601 tombstone_registry: TombstoneRegistry::default(),
602 #[cfg(test)]
603 instr: std::sync::Mutex::new(GraphInstrumentation::default()),
604 };
605
606 if config.use_dynamic_topo {
607 let nodes = g
609 .store
610 .all_vertices()
611 .filter(|&id| g.store.vertex_exists_active(id));
612 let mut pk = DynamicTopo::new(
613 nodes,
614 PkConfig {
615 visit_budget: config.pk_visit_budget,
616 compaction_interval_ops: config.pk_compaction_interval_ops,
617 },
618 );
619 let adapter = GraphAdapter { g: &g };
621 pk.rebuild_full(&adapter);
622 g.pk_order = Some(pk);
623 }
624
625 g
626 }
627
628 pub(crate) fn pk_layers_for(&self, subset: &[VertexId]) -> Option<Vec<crate::engine::Layer>> {
630 let pk = self.pk_order.as_ref()?;
631 let adapter = crate::engine::topo::GraphAdapter { g: self };
632 let layers = pk.layers_for(&adapter, subset, self.config.max_layer_width);
633 Some(
634 layers
635 .into_iter()
636 .map(|vs| crate::engine::Layer { vertices: vs })
637 .collect(),
638 )
639 }
640
641 #[inline]
642 pub(crate) fn dynamic_topo_enabled(&self) -> bool {
643 self.pk_order.is_some()
644 }
645
646 #[cfg(test)]
647 pub fn reset_instr(&mut self) {
648 if let Ok(mut g) = self.instr.lock() {
649 *g = GraphInstrumentation::default();
650 }
651 }
652
653 #[cfg(test)]
654 pub fn instr(&self) -> GraphInstrumentation {
655 self.instr.lock().map(|g| g.clone()).unwrap_or_default()
656 }
657
658 pub fn begin_batch(&mut self) {
660 self.edges.begin_batch();
661 }
662
663 pub fn end_batch(&mut self) {
665 self.edges.end_batch();
666 }
667
668 pub fn default_sheet_id(&self) -> SheetId {
669 self.default_sheet_id
670 }
671
672 pub fn default_sheet_name(&self) -> &str {
673 self.sheet_reg.name(self.default_sheet_id)
674 }
675
676 pub fn set_default_sheet_by_name(&mut self, name: &str) {
677 self.default_sheet_id = self.sheet_id_mut(name);
678 }
679
680 pub fn set_default_sheet_by_id(&mut self, id: SheetId) {
681 self.default_sheet_id = id;
682 }
683
684 pub fn sheet_id_mut(&mut self, name: &str) -> SheetId {
686 self.sheet_reg.id_for(name)
687 }
688
689 pub fn sheet_id(&self, name: &str) -> Option<SheetId> {
690 self.sheet_reg.get_id(name)
691 }
692
693 fn resolve_existing_sheet_id(&self, name: &str) -> Result<SheetId, ExcelError> {
695 self.sheet_id(name).ok_or_else(|| {
696 ExcelError::new(ExcelErrorKind::Ref).with_message(format!("Sheet not found: {name}"))
697 })
698 }
699
700 pub fn sheet_name(&self, id: SheetId) -> &str {
702 self.sheet_reg.name(id)
703 }
704
705 pub fn sheet_reg(&self) -> &SheetRegistry {
707 &self.sheet_reg
708 }
709
710 pub(crate) fn data_store(&self) -> &DataStore {
711 &self.data_store
712 }
713
714 pub fn to_a1(&self, cell_ref: CellRef) -> String {
716 format!("{}!{}", self.sheet_name(cell_ref.sheet_id), cell_ref.coord)
717 }
718
719 pub(crate) fn vertex_len(&self) -> usize {
720 self.store.len()
721 }
722
723 pub fn sheet_index_mut(&mut self, sheet_id: SheetId) -> &mut SheetIndex {
726 self.sheet_indexes.entry(sheet_id).or_default()
727 }
728
729 pub fn sheet_index(&self, sheet_id: SheetId) -> Option<&SheetIndex> {
731 self.sheet_indexes.get(&sheet_id)
732 }
733
734 pub fn set_cell_value(
736 &mut self,
737 sheet: &str,
738 row: u32,
739 col: u32,
740 value: LiteralValue,
741 ) -> Result<OperationSummary, ExcelError> {
742 let value = normalize_stored_literal(value);
743 let sheet_id = self.sheet_id_mut(sheet);
744 let coord = Coord::from_excel(row, col, true, true);
746 let addr = CellRef::new(sheet_id, coord);
747 let mut created_placeholders = Vec::new();
748
749 let vertex_id = if let Some(&existing_id) = self.cell_to_vertex.get(&addr) {
750 let is_formula = matches!(
752 self.store.kind(existing_id),
753 VertexKind::FormulaScalar | VertexKind::FormulaArray
754 );
755
756 if is_formula {
757 self.remove_dependent_edges(existing_id);
758 self.detach_vertex_from_names(existing_id);
759 self.clear_pending_name_references(existing_id);
760 self.vertex_formulas.remove(&existing_id);
761 }
762
763 self.store.set_kind(existing_id, VertexKind::Cell);
765 if self.value_cache_enabled {
766 let value_ref = self.data_store.store_value(value);
767 self.vertex_values.insert(existing_id, value_ref);
768 } else {
769 self.vertex_values.remove(&existing_id);
771 }
772 existing_id
773 } else {
774 created_placeholders.push(addr);
776 let packed_coord = AbsCoord::from_excel(row, col);
777 let vertex_id = self.store.allocate(packed_coord, sheet_id, 0x01); self.edges.add_vertex(packed_coord, vertex_id.0);
781
782 self.sheet_index_mut(sheet_id)
784 .add_vertex(packed_coord, vertex_id);
785
786 self.store.set_kind(vertex_id, VertexKind::Cell);
787 if self.value_cache_enabled {
788 let value_ref = self.data_store.store_value(value);
789 self.vertex_values.insert(vertex_id, value_ref);
790 }
791 self.cell_to_vertex.insert(addr, vertex_id);
792 vertex_id
793 };
794
795 self.ref_error_vertices.remove(&vertex_id);
797
798 Ok(OperationSummary {
799 affected_vertices: self.mark_dirty(vertex_id),
800 created_placeholders,
801 })
802 }
803
804 pub fn reserve_cells(&mut self, additional: usize) {
806 self.store.reserve(additional);
807 if self.value_cache_enabled {
808 self.vertex_values.reserve(additional);
809 }
810 self.cell_to_vertex.reserve(additional);
811 }
813
814 pub fn set_cell_value_bulk_untracked(
816 &mut self,
817 sheet: &str,
818 row: u32,
819 col: u32,
820 value: LiteralValue,
821 ) {
822 let value = normalize_stored_literal(value);
823 let sheet_id = self.sheet_id_mut(sheet);
824 let coord = Coord::from_excel(row, col, true, true);
825 let addr = CellRef::new(sheet_id, coord);
826 if let Some(&existing_id) = self.cell_to_vertex.get(&addr) {
827 if matches!(
829 self.store.kind(existing_id),
830 VertexKind::FormulaScalar | VertexKind::FormulaArray
831 ) {
832 self.remove_dependent_edges(existing_id);
833 self.detach_vertex_from_names(existing_id);
834 self.clear_pending_name_references(existing_id);
835 self.vertex_formulas.remove(&existing_id);
836 }
837 if self.value_cache_enabled {
838 let value_ref = self.data_store.store_value(value);
839 self.vertex_values.insert(existing_id, value_ref);
840 } else {
841 self.vertex_values.remove(&existing_id);
842 }
843 self.store.set_kind(existing_id, VertexKind::Cell);
844 self.ref_error_vertices.remove(&existing_id);
845 return;
846 }
847 let packed_coord = AbsCoord::from_excel(row, col);
848 let vertex_id = self.store.allocate(packed_coord, sheet_id, 0x00); self.edges.add_vertex(packed_coord, vertex_id.0);
850 self.sheet_index_mut(sheet_id)
851 .add_vertex(packed_coord, vertex_id);
852 self.store.set_kind(vertex_id, VertexKind::Cell);
853 self.ref_error_vertices.remove(&vertex_id);
854 if self.value_cache_enabled {
855 let value_ref = self.data_store.store_value(value);
856 self.vertex_values.insert(vertex_id, value_ref);
857 }
858 self.cell_to_vertex.insert(addr, vertex_id);
859 }
860
861 pub fn bulk_insert_values<I>(&mut self, sheet: &str, cells: I)
863 where
864 I: IntoIterator<Item = (u32, u32, LiteralValue)>,
865 {
866 use crate::instant::FzInstant as Instant;
867 let t0 = Instant::now();
868 let collected: Vec<(u32, u32, LiteralValue)> = cells.into_iter().collect();
870 if collected.is_empty() {
871 return;
872 }
873 let sheet_id = self.sheet_id_mut(sheet);
874 self.reserve_cells(collected.len());
875 let t_reserve = Instant::now();
876 let mut new_vertices: Vec<(AbsCoord, u32)> = Vec::with_capacity(collected.len());
877 let mut index_items: Vec<(AbsCoord, VertexId)> = Vec::with_capacity(collected.len());
878 let mut new_value_coords: Vec<(AbsCoord, VertexId)> = Vec::with_capacity(collected.len());
880 let mut new_value_literals: Vec<LiteralValue> = Vec::with_capacity(collected.len());
881 let assume_new = self.first_load_assume_new
883 && self
884 .sheet_id(sheet)
885 .map(|sid| !self.ensure_touched_sheets.contains(&sid))
886 .unwrap_or(false);
887
888 for (row, col, value) in collected {
889 let value = normalize_stored_literal(value);
890 let coord = Coord::from_excel(row, col, true, true);
891 let addr = CellRef::new(sheet_id, coord);
892 if !assume_new && let Some(&existing_id) = self.cell_to_vertex.get(&addr) {
893 if matches!(
894 self.store.kind(existing_id),
895 VertexKind::FormulaScalar | VertexKind::FormulaArray
896 ) {
897 self.remove_dependent_edges(existing_id);
898 self.detach_vertex_from_names(existing_id);
899 self.clear_pending_name_references(existing_id);
900 self.vertex_formulas.remove(&existing_id);
901 }
902 if self.value_cache_enabled {
903 let value_ref = self.data_store.store_value(value);
904 self.vertex_values.insert(existing_id, value_ref);
905 } else {
906 self.vertex_values.remove(&existing_id);
907 }
908 self.store.set_kind(existing_id, VertexKind::Cell);
909 continue;
910 }
911 let packed = AbsCoord::from_excel(row, col);
912 let vertex_id = self.store.allocate(packed, sheet_id, 0x00);
913 self.store.set_kind(vertex_id, VertexKind::Cell);
914 new_value_coords.push((packed, vertex_id));
916 new_value_literals.push(value);
917 self.cell_to_vertex.insert(addr, vertex_id);
918 new_vertices.push((packed, vertex_id.0));
919 index_items.push((packed, vertex_id));
920 }
921 if self.value_cache_enabled && !new_value_literals.is_empty() {
923 let vrefs = self.data_store.store_values_batch(new_value_literals);
924 debug_assert_eq!(vrefs.len(), new_value_coords.len());
925 for (i, (_pc, vid)) in new_value_coords.iter().enumerate() {
926 self.vertex_values.insert(*vid, vrefs[i]);
927 }
928 }
929 let t_after_alloc = Instant::now();
930 if !new_vertices.is_empty() {
931 let t_edges_start = Instant::now();
932 self.edges.add_vertices_batch(&new_vertices);
933 let t_edges_done = Instant::now();
934
935 match self.config.sheet_index_mode {
936 crate::engine::SheetIndexMode::Eager => {
937 self.sheet_index_mut(sheet_id)
938 .add_vertices_batch(&index_items);
939 }
940 crate::engine::SheetIndexMode::Lazy => {
941 }
943 crate::engine::SheetIndexMode::FastBatch => {
944 self.sheet_index_mut(sheet_id)
946 .add_vertices_batch(&index_items);
947 }
948 }
949 let t_index_done = Instant::now();
950 }
951 }
952
953 pub fn set_cell_formula(
955 &mut self,
956 sheet: &str,
957 row: u32,
958 col: u32,
959 ast: ASTNode,
960 ) -> Result<OperationSummary, ExcelError> {
961 let volatile = self.is_ast_volatile(&ast);
962 self.set_cell_formula_with_volatility(sheet, row, col, ast, volatile)
963 }
964
965 pub fn set_cell_formula_with_volatility(
967 &mut self,
968 sheet: &str,
969 row: u32,
970 col: u32,
971 ast: ASTNode,
972 volatile: bool,
973 ) -> Result<OperationSummary, ExcelError> {
974 let dbg = std::env::var("FZ_DEBUG_LOAD")
975 .ok()
976 .is_some_and(|v| v != "0");
977 let dep_ms_thresh: u128 = std::env::var("FZ_DEBUG_DEP_MS")
978 .ok()
979 .and_then(|s| s.parse().ok())
980 .unwrap_or(0);
981 let sample_n: usize = std::env::var("FZ_DEBUG_SAMPLE_N")
982 .ok()
983 .and_then(|s| s.parse().ok())
984 .unwrap_or(0);
985 let t0 = if dbg {
986 Some(crate::instant::FzInstant::now())
987 } else {
988 None
989 };
990 let sheet_id = self.sheet_id_mut(sheet);
991 let coord = Coord::from_excel(row, col, true, true);
992 let addr = CellRef::new(sheet_id, coord);
993
994 let mut ast = ast;
997 self.rewrite_structured_references_for_cell(&mut ast, addr)?;
998
999 let t_dep0 = if dbg {
1001 Some(crate::instant::FzInstant::now())
1002 } else {
1003 None
1004 };
1005 let (
1006 new_dependencies,
1007 new_range_dependencies,
1008 mut created_placeholders,
1009 named_dependencies,
1010 unresolved_names,
1011 ) = self.extract_dependencies_with_pending_names(&ast, sheet_id)?;
1012 if let (true, Some(t)) = (dbg, t_dep0) {
1013 let elapsed = t.elapsed().as_millis();
1014 let do_log = (dep_ms_thresh > 0 && elapsed >= dep_ms_thresh)
1016 || (sample_n > 0 && (row as usize).is_multiple_of(sample_n));
1017 if dep_ms_thresh == 0 && sample_n == 0 {
1018 if row.is_multiple_of(1000) {
1020 eprintln!(
1021 "[fz][dep] {}!{} extracted: deps={}, ranges={}, placeholders={}, names={} in {} ms",
1022 self.sheet_name(sheet_id),
1023 crate::reference::Coord::from_excel(row, col, true, true),
1024 new_dependencies.len(),
1025 new_range_dependencies.len(),
1026 created_placeholders.len(),
1027 named_dependencies.len(),
1028 elapsed
1029 );
1030 }
1031 } else if do_log {
1032 eprintln!(
1033 "[fz][dep] {}!{} extracted: deps={}, ranges={}, placeholders={}, names={} in {} ms",
1034 self.sheet_name(sheet_id),
1035 crate::reference::Coord::from_excel(row, col, true, true),
1036 new_dependencies.len(),
1037 new_range_dependencies.len(),
1038 created_placeholders.len(),
1039 named_dependencies.len(),
1040 elapsed
1041 );
1042 }
1043 }
1044
1045 let addr_vertex_id = self.get_or_create_vertex(&addr, &mut created_placeholders);
1047
1048 self.ref_error_vertices.remove(&addr_vertex_id);
1050
1051 if new_dependencies.contains(&addr_vertex_id) {
1052 return Err(ExcelError::new(ExcelErrorKind::Circ)
1053 .with_message("Self-reference detected".to_string()));
1054 }
1055
1056 for &name_vertex in &named_dependencies {
1057 let mut visited = FxHashSet::default();
1058 if self.name_depends_on_vertex(name_vertex, addr_vertex_id, &mut visited) {
1059 return Err(ExcelError::new(ExcelErrorKind::Circ)
1060 .with_message("Circular reference through named range".to_string()));
1061 }
1062 }
1063
1064 self.remove_dependent_edges(addr_vertex_id);
1066 self.detach_vertex_from_names(addr_vertex_id);
1067 self.clear_pending_name_references(addr_vertex_id);
1068
1069 self.store
1071 .set_kind(addr_vertex_id, VertexKind::FormulaScalar);
1072 let ast_id = self.data_store.store_ast(&ast, &self.sheet_reg);
1073 self.vertex_formulas.insert(addr_vertex_id, ast_id);
1074 self.store.set_dirty(addr_vertex_id, true);
1075
1076 self.vertex_values.remove(&addr_vertex_id);
1078
1079 self.mark_volatile(addr_vertex_id, volatile);
1080 let dynamic = self.is_ast_dynamic(&ast);
1081 self.store.set_dynamic(addr_vertex_id, dynamic);
1082
1083 if !named_dependencies.is_empty() {
1084 self.attach_vertex_to_names(addr_vertex_id, &named_dependencies);
1085 }
1086 for unresolved_name in &unresolved_names {
1087 self.record_pending_name_reference(sheet_id, unresolved_name, addr_vertex_id);
1088 }
1089
1090 if let (true, Some(t)) = (dbg, t0) {
1091 let elapsed = t.elapsed().as_millis();
1092 let log_set = dep_ms_thresh > 0 && elapsed >= dep_ms_thresh;
1093 if log_set {
1094 eprintln!(
1095 "[fz][set] {}!{} total {} ms",
1096 self.sheet_name(sheet_id),
1097 crate::reference::Coord::from_excel(row, col, true, true),
1098 elapsed
1099 );
1100 }
1101 }
1102
1103 self.add_dependent_edges(addr_vertex_id, &new_dependencies);
1105 self.add_range_dependent_edges(addr_vertex_id, &new_range_dependencies, sheet_id);
1106
1107 Ok(OperationSummary {
1108 affected_vertices: self.mark_dirty(addr_vertex_id),
1109 created_placeholders,
1110 })
1111 }
1112
1113 pub(crate) fn rewrite_structured_references_for_cell(
1114 &self,
1115 ast: &mut ASTNode,
1116 cell: CellRef,
1117 ) -> Result<(), ExcelError> {
1118 self.rewrite_structured_references_node(ast, cell)
1119 }
1120
1121 fn rewrite_structured_references_node(
1122 &self,
1123 node: &mut ASTNode,
1124 cell: CellRef,
1125 ) -> Result<(), ExcelError> {
1126 match &mut node.node_type {
1127 ASTNodeType::Reference { reference, .. } => {
1128 self.rewrite_structured_reference(reference, cell)
1129 }
1130 ASTNodeType::UnaryOp { expr, .. } => {
1131 self.rewrite_structured_references_node(expr, cell)
1132 }
1133 ASTNodeType::BinaryOp { left, right, .. } => {
1134 self.rewrite_structured_references_node(left, cell)?;
1135 self.rewrite_structured_references_node(right, cell)
1136 }
1137 ASTNodeType::Function { args, .. } => {
1138 for a in args.iter_mut() {
1139 self.rewrite_structured_references_node(a, cell)?;
1140 }
1141 Ok(())
1142 }
1143 ASTNodeType::Array(rows) => {
1144 for r in rows.iter_mut() {
1145 for item in r.iter_mut() {
1146 self.rewrite_structured_references_node(item, cell)?;
1147 }
1148 }
1149 Ok(())
1150 }
1151 ASTNodeType::Literal(_) => Ok(()),
1152 }
1153 }
1154
1155 fn rewrite_structured_reference(
1156 &self,
1157 reference: &mut ReferenceType,
1158 cell: CellRef,
1159 ) -> Result<(), ExcelError> {
1160 use formualizer_parse::parser::{SpecialItem, TableSpecifier};
1161
1162 let ReferenceType::Table(tref) = reference else {
1163 return Ok(());
1164 };
1165
1166 if !tref.name.is_empty() {
1168 return Ok(());
1169 }
1170
1171 let col_name = match &tref.specifier {
1172 Some(TableSpecifier::Combination(parts)) => {
1173 let mut saw_this_row = false;
1174 let mut col: Option<&str> = None;
1175 for p in parts {
1176 match p.as_ref() {
1177 TableSpecifier::SpecialItem(SpecialItem::ThisRow) => {
1178 saw_this_row = true;
1179 }
1180 TableSpecifier::Column(c) => {
1181 if col.is_some() {
1182 return Err(ExcelError::new(ExcelErrorKind::NImpl).with_message(
1183 "This-row structured reference with multiple columns is not supported"
1184 .to_string(),
1185 ));
1186 }
1187 col = Some(c.as_str());
1188 }
1189 other => {
1190 return Err(ExcelError::new(ExcelErrorKind::NImpl).with_message(
1191 format!(
1192 "Unsupported this-row structured reference component: {other}"
1193 ),
1194 ));
1195 }
1196 }
1197 }
1198 if !saw_this_row {
1199 return Err(ExcelError::new(ExcelErrorKind::NImpl).with_message(
1200 "Unnamed structured reference requires a this-row selector".to_string(),
1201 ));
1202 }
1203 col.ok_or_else(|| {
1204 ExcelError::new(ExcelErrorKind::NImpl).with_message(
1205 "This-row structured reference missing column selector".to_string(),
1206 )
1207 })?
1208 }
1209 _ => {
1210 return Err(ExcelError::new(ExcelErrorKind::NImpl).with_message(
1211 "Unnamed structured reference form is not supported".to_string(),
1212 ));
1213 }
1214 };
1215
1216 let Some(table) = self.find_table_containing_cell(cell) else {
1217 return Err(ExcelError::new(ExcelErrorKind::Name)
1218 .with_message("This-row structured reference used outside a table".to_string()));
1219 };
1220
1221 let row0 = cell.coord.row();
1222 let col0 = cell.coord.col();
1223 let sr0 = table.range.start.coord.row();
1224 let sc0 = table.range.start.coord.col();
1225 let er0 = table.range.end.coord.row();
1226 let ec0 = table.range.end.coord.col();
1227
1228 if row0 < sr0 || row0 > er0 || col0 < sc0 || col0 > ec0 {
1229 return Err(ExcelError::new(ExcelErrorKind::Name)
1230 .with_message("This-row structured reference used outside a table".to_string()));
1231 }
1232
1233 if table.header_row && row0 == sr0 {
1234 return Err(ExcelError::new(ExcelErrorKind::Ref).with_message(
1235 "This-row structured references are not valid in the table header row".to_string(),
1236 ));
1237 }
1238
1239 let data_start = if table.header_row { sr0 + 1 } else { sr0 };
1240 if row0 < data_start {
1241 return Err(ExcelError::new(ExcelErrorKind::Ref).with_message(
1242 "This-row structured references require a data/totals row context".to_string(),
1243 ));
1244 }
1245
1246 let Some(idx) = table.col_index(col_name) else {
1247 return Err(ExcelError::new(ExcelErrorKind::Ref).with_message(format!(
1248 "Unknown table column in this-row reference: {col_name}"
1249 )));
1250 };
1251 let target_col0 = sc0 + (idx as u32);
1252 let target_row = row0 + 1;
1253 let target_col = target_col0 + 1;
1254
1255 *reference = ReferenceType::Cell {
1256 sheet: None,
1257 row: target_row,
1258 col: target_col,
1259 row_abs: true,
1260 col_abs: true,
1261 };
1262
1263 Ok(())
1264 }
1265
1266 fn find_table_containing_cell(&self, cell: CellRef) -> Option<&tables::TableEntry> {
1267 let row0 = cell.coord.row();
1268 let col0 = cell.coord.col();
1269
1270 let mut best: Option<&tables::TableEntry> = None;
1271 let mut best_area: u64 = u64::MAX;
1272 let mut best_name: &str = "";
1273
1274 for t in self.tables.values() {
1275 if t.sheet_id() != cell.sheet_id {
1276 continue;
1277 }
1278 let sr0 = t.range.start.coord.row();
1279 let sc0 = t.range.start.coord.col();
1280 let er0 = t.range.end.coord.row();
1281 let ec0 = t.range.end.coord.col();
1282 if row0 < sr0 || row0 > er0 || col0 < sc0 || col0 > ec0 {
1283 continue;
1284 }
1285
1286 let h = (er0 - sr0 + 1) as u64;
1287 let w = (ec0 - sc0 + 1) as u64;
1288 let area = h.saturating_mul(w);
1289 let name = t.name.as_str();
1290 let better = match best {
1291 None => true,
1292 Some(_) => area < best_area || (area == best_area && name < best_name),
1293 };
1294 if better {
1295 best = Some(t);
1296 best_area = area;
1297 best_name = name;
1298 }
1299 }
1300
1301 best
1302 }
1303
1304 pub fn set_cell_value_ref(
1305 &mut self,
1306 cell: formualizer_common::SheetCellRef<'_>,
1307 value: LiteralValue,
1308 ) -> Result<OperationSummary, ExcelError> {
1309 let owned = cell.into_owned();
1310 let sheet_id = match owned.sheet {
1311 formualizer_common::SheetLocator::Id(id) => id,
1312 formualizer_common::SheetLocator::Name(name) => self.sheet_id_mut(name.as_ref()),
1313 formualizer_common::SheetLocator::Current => self.default_sheet_id,
1314 };
1315 let sheet_name = self.sheet_name(sheet_id).to_string();
1316 self.set_cell_value(
1317 &sheet_name,
1318 owned.coord.row() + 1,
1319 owned.coord.col() + 1,
1320 value,
1321 )
1322 }
1323
1324 pub fn set_cell_formula_ref(
1325 &mut self,
1326 cell: formualizer_common::SheetCellRef<'_>,
1327 ast: ASTNode,
1328 ) -> Result<OperationSummary, ExcelError> {
1329 let owned = cell.into_owned();
1330 let sheet_id = match owned.sheet {
1331 formualizer_common::SheetLocator::Id(id) => id,
1332 formualizer_common::SheetLocator::Name(name) => self.sheet_id_mut(name.as_ref()),
1333 formualizer_common::SheetLocator::Current => self.default_sheet_id,
1334 };
1335 let sheet_name = self.sheet_name(sheet_id).to_string();
1336 self.set_cell_formula(
1337 &sheet_name,
1338 owned.coord.row() + 1,
1339 owned.coord.col() + 1,
1340 ast,
1341 )
1342 }
1343
1344 pub fn get_cell_value_ref(
1345 &self,
1346 cell: formualizer_common::SheetCellRef<'_>,
1347 ) -> Option<LiteralValue> {
1348 let owned = cell.into_owned();
1349 let sheet_id = match owned.sheet {
1350 formualizer_common::SheetLocator::Id(id) => id,
1351 formualizer_common::SheetLocator::Name(name) => self.sheet_id(name.as_ref())?,
1352 formualizer_common::SheetLocator::Current => self.default_sheet_id,
1353 };
1354 let sheet_name = self.sheet_name(sheet_id);
1355 self.get_cell_value(sheet_name, owned.coord.row() + 1, owned.coord.col() + 1)
1356 }
1357
1358 pub fn get_cell_value(&self, sheet: &str, row: u32, col: u32) -> Option<LiteralValue> {
1360 if !self.value_cache_enabled {
1361 #[cfg(debug_assertions)]
1362 {
1363 self.graph_value_read_attempts
1364 .fetch_add(1, Ordering::Relaxed);
1365 }
1366 return None;
1367 }
1368 let sheet_id = self.sheet_reg.get_id(sheet)?;
1369 let coord = Coord::from_excel(row, col, true, true);
1370 let addr = CellRef::new(sheet_id, coord);
1371
1372 self.cell_to_vertex.get(&addr).and_then(|&vertex_id| {
1373 self.vertex_values
1375 .get(&vertex_id)
1376 .map(|&value_ref| self.data_store.retrieve_value(value_ref))
1377 })
1378 }
1379
1380 fn mark_dirty(&mut self, vertex_id: VertexId) -> Vec<VertexId> {
1382 let mut affected = FxHashSet::default();
1383 let mut to_visit = Vec::new();
1384 let mut visited_for_propagation = FxHashSet::default();
1385
1386 let is_formula = matches!(
1389 self.store.kind(vertex_id),
1390 VertexKind::FormulaScalar
1391 | VertexKind::FormulaArray
1392 | VertexKind::NamedScalar
1393 | VertexKind::NamedArray
1394 );
1395
1396 if is_formula {
1397 to_visit.push(vertex_id);
1398 } else {
1399 affected.insert(vertex_id);
1401 }
1402
1403 {
1405 if let Some(dependents) = self.dependents_slice(vertex_id) {
1407 to_visit.extend(dependents.iter().copied());
1408 } else {
1409 let dependents = self.get_dependents(vertex_id);
1410 to_visit.extend(dependents);
1411 }
1412
1413 if let Some(name_set) = self.cell_to_name_dependents.get(&vertex_id) {
1414 for &name_vertex in name_set {
1415 to_visit.push(name_vertex);
1416 }
1417 }
1418
1419 to_visit.extend(self.collect_range_dependents_for_vertex(vertex_id));
1420 }
1421
1422 while let Some(id) = to_visit.pop() {
1423 if !visited_for_propagation.insert(id) {
1424 continue; }
1426 affected.insert(id);
1427
1428 self.store.set_dirty(id, true);
1430
1431 if let Some(dependents) = self.dependents_slice(id) {
1433 to_visit.extend(dependents.iter().copied());
1434 } else {
1435 let dependents = self.get_dependents(id);
1436 to_visit.extend(dependents);
1437 }
1438 to_visit.extend(self.collect_range_dependents_for_vertex(id));
1439 }
1440
1441 self.dirty_vertices.extend(&affected);
1443
1444 affected.into_iter().collect()
1446 }
1447
1448 pub fn get_evaluation_vertices(&self) -> Vec<VertexId> {
1450 let mut combined = FxHashSet::default();
1451 combined.extend(&self.dirty_vertices);
1452 combined.extend(&self.volatile_vertices);
1453
1454 let mut result: Vec<VertexId> = combined
1455 .into_iter()
1456 .filter(|&id| {
1457 matches!(
1459 self.store.kind(id),
1460 VertexKind::FormulaScalar
1461 | VertexKind::FormulaArray
1462 | VertexKind::NamedScalar
1463 | VertexKind::NamedArray
1464 )
1465 })
1466 .collect();
1467 result.sort_unstable();
1468 result
1469 }
1470
1471 pub fn clear_dirty_flags(&mut self, vertices: &[VertexId]) {
1473 for &vertex_id in vertices {
1474 self.store.set_dirty(vertex_id, false);
1475 self.dirty_vertices.remove(&vertex_id);
1476 }
1477 }
1478
1479 pub fn clear_volatile_flags(&mut self) {
1481 self.volatile_vertices.clear();
1482 }
1483
1484 pub(crate) fn redirty_volatiles(&mut self) {
1486 let volatile_ids: Vec<VertexId> = self.volatile_vertices.iter().copied().collect();
1487 for id in volatile_ids {
1488 self.mark_dirty(id);
1489 }
1490 }
1491
1492 fn get_or_create_vertex(
1493 &mut self,
1494 addr: &CellRef,
1495 created_placeholders: &mut Vec<CellRef>,
1496 ) -> VertexId {
1497 if let Some(&vertex_id) = self.cell_to_vertex.get(addr) {
1498 return vertex_id;
1499 }
1500
1501 created_placeholders.push(*addr);
1502 let packed_coord = AbsCoord::new(addr.coord.row(), addr.coord.col());
1503 let vertex_id = self.store.allocate(packed_coord, addr.sheet_id, 0x00);
1504
1505 self.edges.add_vertex(packed_coord, vertex_id.0);
1507
1508 self.sheet_index_mut(addr.sheet_id)
1510 .add_vertex(packed_coord, vertex_id);
1511
1512 self.store.set_kind(vertex_id, VertexKind::Empty);
1513 self.cell_to_vertex.insert(*addr, vertex_id);
1514 vertex_id
1515 }
1516
1517 fn add_dependent_edges(&mut self, dependent: VertexId, dependencies: &[VertexId]) {
1518 self.edges.begin_batch();
1520
1521 let mut skip_deps: rustc_hash::FxHashSet<VertexId> = rustc_hash::FxHashSet::default();
1524 if self.pk_order.is_some()
1525 && let Some(mut pk) = self.pk_order.take()
1526 {
1527 pk.ensure_nodes(std::iter::once(dependent));
1528 pk.ensure_nodes(dependencies.iter().copied());
1529 {
1530 let adapter = GraphAdapter { g: self };
1531 for &dep_id in dependencies {
1532 match pk.try_add_edge(&adapter, dep_id, dependent) {
1533 Ok(_) => {}
1534 Err(_cycle) => {
1535 if self.config.pk_reject_cycle_edges {
1536 skip_deps.insert(dep_id);
1537 } else {
1538 pk.rebuild_full(&adapter);
1539 }
1540 }
1541 }
1542 }
1543 } self.pk_order = Some(pk);
1545 }
1546
1547 for &dep_id in dependencies {
1549 if self.config.pk_reject_cycle_edges && skip_deps.contains(&dep_id) {
1550 continue;
1551 }
1552 self.edges.add_edge(dependent, dep_id);
1553 #[cfg(test)]
1554 {
1555 if let Ok(mut g) = self.instr.lock() {
1556 g.edges_added += 1;
1557 }
1558 }
1559 }
1560
1561 self.edges.end_batch();
1562 }
1563
1564 fn add_dependent_edges_nobatch(&mut self, dependent: VertexId, dependencies: &[VertexId]) {
1566 let mut skip_deps: rustc_hash::FxHashSet<VertexId> = rustc_hash::FxHashSet::default();
1568 if self.pk_order.is_some()
1569 && let Some(mut pk) = self.pk_order.take()
1570 {
1571 pk.ensure_nodes(std::iter::once(dependent));
1572 pk.ensure_nodes(dependencies.iter().copied());
1573 {
1574 let adapter = GraphAdapter { g: self };
1575 for &dep_id in dependencies {
1576 match pk.try_add_edge(&adapter, dep_id, dependent) {
1577 Ok(_) => {}
1578 Err(_cycle) => {
1579 if self.config.pk_reject_cycle_edges {
1580 skip_deps.insert(dep_id);
1581 } else {
1582 pk.rebuild_full(&adapter);
1583 }
1584 }
1585 }
1586 }
1587 }
1588 self.pk_order = Some(pk);
1589 }
1590
1591 for &dep_id in dependencies {
1592 if self.config.pk_reject_cycle_edges && skip_deps.contains(&dep_id) {
1593 continue;
1594 }
1595 self.edges.add_edge(dependent, dep_id);
1596 #[cfg(test)]
1597 {
1598 if let Ok(mut g) = self.instr.lock() {
1599 g.edges_added += 1;
1600 }
1601 }
1602 }
1603 }
1604
1605 pub fn bulk_set_formulas<I>(&mut self, sheet: &str, items: I) -> Result<usize, ExcelError>
1607 where
1608 I: IntoIterator<Item = (u32, u32, ASTNode)>,
1609 {
1610 let collected: Vec<(u32, u32, ASTNode)> = items.into_iter().collect();
1611 if collected.is_empty() {
1612 return Ok(0);
1613 }
1614 let vol_flags: Vec<bool> = collected
1615 .iter()
1616 .map(|(_, _, ast)| self.is_ast_volatile(ast))
1617 .collect();
1618 self.bulk_set_formulas_with_volatility(sheet, collected, vol_flags)
1619 }
1620
1621 pub fn bulk_set_formulas_with_volatility(
1622 &mut self,
1623 sheet: &str,
1624 collected: Vec<(u32, u32, ASTNode)>,
1625 vol_flags: Vec<bool>,
1626 ) -> Result<usize, ExcelError> {
1627 use formualizer_parse::parser::CollectPolicy;
1628 let sheet_id = self.sheet_id_mut(sheet);
1629
1630 if collected.is_empty() {
1631 return Ok(0);
1632 }
1633
1634 let tiny_refs = collected.iter().map(|(r, c, ast)| (sheet, *r, *c, ast));
1636 let policy = CollectPolicy {
1637 expand_small_ranges: true,
1638 range_expansion_limit: self.config.range_expansion_limit,
1639 include_names: true,
1640 };
1641 let plan = crate::engine::plan::build_dependency_plan(
1642 &mut self.sheet_reg,
1643 tiny_refs,
1644 &policy,
1645 Some(&vol_flags),
1646 )?;
1647
1648 let mut created_placeholders: Vec<CellRef> = Vec::new();
1650
1651 let mut target_vids: Vec<VertexId> = Vec::with_capacity(plan.formula_targets.len());
1653 for (sid, pc) in &plan.formula_targets {
1654 let addr = CellRef::new(*sid, Coord::new(pc.row(), pc.col(), true, true));
1655 let vid = if let Some(&existing) = self.cell_to_vertex.get(&addr) {
1656 existing
1657 } else {
1658 self.get_or_create_vertex(&addr, &mut created_placeholders)
1659 };
1660 target_vids.push(vid);
1661 }
1662
1663 let mut dep_vids: Vec<VertexId> = Vec::with_capacity(plan.global_cells.len());
1665 for (sid, pc) in &plan.global_cells {
1666 let addr = CellRef::new(*sid, Coord::new(pc.row(), pc.col(), true, true));
1667 let vid = if let Some(&existing) = self.cell_to_vertex.get(&addr) {
1668 existing
1669 } else {
1670 self.get_or_create_vertex(&addr, &mut created_placeholders)
1671 };
1672 dep_vids.push(vid);
1673 }
1674
1675 let ast_ids = self
1677 .data_store
1678 .store_asts_batch(collected.iter().map(|(_, _, ast)| ast), &self.sheet_reg);
1679 for (i, &tvid) in target_vids.iter().enumerate() {
1680 if self.vertex_formulas.contains_key(&tvid) {
1682 self.remove_dependent_edges(tvid);
1683 }
1684 self.store.set_kind(tvid, VertexKind::FormulaScalar);
1685 self.store.set_dirty(tvid, true);
1686 self.vertex_values.remove(&tvid);
1687 self.vertex_formulas.insert(tvid, ast_ids[i]);
1688 self.mark_volatile(tvid, vol_flags.get(i).copied().unwrap_or(false));
1689
1690 let dynamic = self.is_ast_dynamic(&collected[i].2);
1691 self.store.set_dynamic(tvid, dynamic);
1692 }
1693
1694 self.edges.begin_batch();
1696 for (i, tvid) in target_vids.iter().copied().enumerate() {
1697 let mut deps: Vec<VertexId> = Vec::new();
1698
1699 if let Some(indices) = plan.per_formula_cells.get(i) {
1701 deps.reserve(indices.len());
1702 for &idx in indices {
1703 if let Some(vid) = dep_vids.get(idx as usize) {
1704 deps.push(*vid);
1705 }
1706 }
1707 }
1708
1709 if let Some(names) = plan.per_formula_names.get(i)
1710 && !names.is_empty()
1711 {
1712 let mut name_vertices = Vec::new();
1713 let formula_sheet = plan
1714 .formula_targets
1715 .get(i)
1716 .map(|(sid, _)| *sid)
1717 .unwrap_or(sheet_id);
1718 for name in names {
1719 if let Some(named) = self.resolve_name_entry(name, formula_sheet) {
1720 deps.push(named.vertex);
1721 name_vertices.push(named.vertex);
1722 } else if let Some(source) = self.resolve_source_scalar_entry(name) {
1723 deps.push(source.vertex);
1724 } else {
1725 self.record_pending_name_reference(formula_sheet, name, tvid);
1726 }
1727 }
1728 if !name_vertices.is_empty() {
1729 self.attach_vertex_to_names(tvid, &name_vertices);
1730 }
1731 }
1732
1733 if let Some(tables) = plan.per_formula_tables.get(i)
1734 && !tables.is_empty()
1735 {
1736 for table_name in tables {
1737 if let Some(table) = self.resolve_table_entry(table_name) {
1738 deps.push(table.vertex);
1739 } else if let Some(source) = self.resolve_source_table_entry(table_name) {
1740 deps.push(source.vertex);
1741 }
1742 }
1743 }
1744
1745 if !deps.is_empty() {
1746 self.add_dependent_edges_nobatch(tvid, &deps);
1747 }
1748
1749 if let Some(rks) = plan.per_formula_ranges.get(i) {
1751 self.add_range_deps_from_keys(tvid, rks, sheet_id);
1752 }
1753 }
1754 self.edges.end_batch();
1755
1756 Ok(collected.len())
1757 }
1758
1759 pub fn add_dependency_edge(&mut self, dependent: VertexId, dependency: VertexId) {
1761 if dependent == dependency {
1762 return;
1763 }
1764 if self.pk_order.is_some()
1766 && let Some(mut pk) = self.pk_order.take()
1767 {
1768 pk.ensure_nodes(std::iter::once(dependent));
1769 pk.ensure_nodes(std::iter::once(dependency));
1770 let adapter = GraphAdapter { g: self };
1771 if pk.try_add_edge(&adapter, dependency, dependent).is_err() {
1772 pk.rebuild_full(&adapter);
1774 }
1775 self.pk_order = Some(pk);
1776 }
1777 self.edges.add_edge(dependent, dependency);
1778 self.store.set_dirty(dependent, true);
1779 self.dirty_vertices.insert(dependent);
1780 }
1781
1782 fn remove_dependent_edges(&mut self, vertex: VertexId) {
1783 let dependencies = self.edges.out_edges(vertex);
1785
1786 self.edges.begin_batch();
1787 if self.pk_order.is_some()
1788 && let Some(mut pk) = self.pk_order.take()
1789 {
1790 for dep in &dependencies {
1791 pk.remove_edge(*dep, vertex);
1792 }
1793 self.pk_order = Some(pk);
1794 }
1795 for dep in dependencies {
1796 self.edges.remove_edge(vertex, dep);
1797 }
1798 self.edges.end_batch();
1799
1800 if let Some(old_ranges) = self.formula_to_range_deps.remove(&vertex) {
1802 let old_sheet_id = self.store.sheet_id(vertex);
1803
1804 for range in &old_ranges {
1805 let sheet_id = match range.sheet {
1806 SharedSheetLocator::Id(id) => id,
1807 _ => old_sheet_id,
1808 };
1809 let s_row = range.start_row.map(|b| b.index);
1810 let e_row = range.end_row.map(|b| b.index);
1811 let s_col = range.start_col.map(|b| b.index);
1812 let e_col = range.end_col.map(|b| b.index);
1813
1814 let mut keys_to_clean = FxHashSet::default();
1815
1816 let col_stripes = (s_row.is_none() && e_row.is_none())
1817 || (s_col.is_some() && e_col.is_some() && (s_row.is_none() || e_row.is_none()));
1818 let row_stripes = (s_col.is_none() && e_col.is_none())
1819 || (s_row.is_some() && e_row.is_some() && (s_col.is_none() || e_col.is_none()));
1820
1821 if col_stripes && !row_stripes {
1822 let sc = s_col.unwrap_or(0);
1823 let ec = e_col.unwrap_or(sc);
1824 for col in sc..=ec {
1825 keys_to_clean.insert(StripeKey {
1826 sheet_id,
1827 stripe_type: StripeType::Column,
1828 index: col,
1829 });
1830 }
1831 } else if row_stripes && !col_stripes {
1832 let sr = s_row.unwrap_or(0);
1833 let er = e_row.unwrap_or(sr);
1834 for row in sr..=er {
1835 keys_to_clean.insert(StripeKey {
1836 sheet_id,
1837 stripe_type: StripeType::Row,
1838 index: row,
1839 });
1840 }
1841 } else {
1842 let start_row = s_row.unwrap_or(0);
1843 let start_col = s_col.unwrap_or(0);
1844 let end_row = e_row.unwrap_or(start_row);
1845 let end_col = e_col.unwrap_or(start_col);
1846
1847 let height = end_row.saturating_sub(start_row) + 1;
1848 let width = end_col.saturating_sub(start_col) + 1;
1849
1850 if self.config.enable_block_stripes && height > 1 && width > 1 {
1851 let start_block_row = start_row / BLOCK_H;
1852 let end_block_row = end_row / BLOCK_H;
1853 let start_block_col = start_col / BLOCK_W;
1854 let end_block_col = end_col / BLOCK_W;
1855
1856 for block_row in start_block_row..=end_block_row {
1857 for block_col in start_block_col..=end_block_col {
1858 keys_to_clean.insert(StripeKey {
1859 sheet_id,
1860 stripe_type: StripeType::Block,
1861 index: block_index(block_row * BLOCK_H, block_col * BLOCK_W),
1862 });
1863 }
1864 }
1865 } else if height > width {
1866 for col in start_col..=end_col {
1867 keys_to_clean.insert(StripeKey {
1868 sheet_id,
1869 stripe_type: StripeType::Column,
1870 index: col,
1871 });
1872 }
1873 } else {
1874 for row in start_row..=end_row {
1875 keys_to_clean.insert(StripeKey {
1876 sheet_id,
1877 stripe_type: StripeType::Row,
1878 index: row,
1879 });
1880 }
1881 }
1882 }
1883
1884 for key in keys_to_clean {
1885 if let Some(dependents) = self.stripe_to_dependents.get_mut(&key) {
1886 dependents.remove(&vertex);
1887 if dependents.is_empty() {
1888 self.stripe_to_dependents.remove(&key);
1889 #[cfg(test)]
1890 {
1891 if let Ok(mut g) = self.instr.lock() {
1892 g.stripe_removes += 1;
1893 }
1894 }
1895 }
1896 }
1897 }
1898 }
1899 }
1900 }
1901
1902 pub(crate) fn update_vertex_value(&mut self, vertex_id: VertexId, value: LiteralValue) {
1908 if !self.value_cache_enabled {
1909 match self.store.kind(vertex_id) {
1911 VertexKind::Cell
1912 | VertexKind::FormulaScalar
1913 | VertexKind::FormulaArray
1914 | VertexKind::Empty => {
1915 self.vertex_values.remove(&vertex_id);
1916 return;
1917 }
1918 _ => {
1919 }
1921 }
1922 }
1923 let value_ref = self.data_store.store_value(normalize_stored_literal(value));
1924 self.vertex_values.insert(vertex_id, value_ref);
1925 }
1926
1927 pub fn plan_spill_region(
1929 &self,
1930 anchor: VertexId,
1931 target_cells: &[CellRef],
1932 ) -> Result<(), ExcelError> {
1933 self.plan_spill_region_allowing_formula_overwrite(anchor, target_cells, None)
1934 }
1935
1936 pub(crate) fn plan_spill_region_allowing_formula_overwrite(
1941 &self,
1942 anchor: VertexId,
1943 target_cells: &[CellRef],
1944 overwritable_formulas: Option<&rustc_hash::FxHashSet<VertexId>>,
1945 ) -> Result<(), ExcelError> {
1946 use formualizer_common::{ExcelErrorExtra, ExcelErrorKind};
1947 let (expected_rows, expected_cols) = if target_cells.is_empty() {
1949 (0u32, 0u32)
1950 } else {
1951 let mut min_r = u32::MAX;
1952 let mut max_r = 0u32;
1953 let mut min_c = u32::MAX;
1954 let mut max_c = 0u32;
1955 for cell in target_cells {
1956 let r = cell.coord.row();
1957 let c = cell.coord.col();
1958 if r < min_r {
1959 min_r = r;
1960 }
1961 if r > max_r {
1962 max_r = r;
1963 }
1964 if c < min_c {
1965 min_c = c;
1966 }
1967 if c > max_c {
1968 max_c = c;
1969 }
1970 }
1971 (
1972 max_r.saturating_sub(min_r).saturating_add(1),
1973 max_c.saturating_sub(min_c).saturating_add(1),
1974 )
1975 };
1976 for cell in target_cells {
1978 let owned_by_anchor = match self.spill_cell_to_anchor.get(cell) {
1980 Some(&existing_anchor) if existing_anchor == anchor => true,
1981 Some(_other) => {
1982 return Err(ExcelError::new(ExcelErrorKind::Spill)
1983 .with_message("BlockedBySpill")
1984 .with_extra(ExcelErrorExtra::Spill {
1985 expected_rows,
1986 expected_cols,
1987 }));
1988 }
1989 None => false,
1990 };
1991
1992 if owned_by_anchor {
1993 continue;
1994 }
1995
1996 if let Some(&vid) = self.cell_to_vertex.get(cell)
1998 && vid != anchor
1999 {
2000 match self.store.kind(vid) {
2002 VertexKind::FormulaScalar | VertexKind::FormulaArray => {
2003 if let Some(allow) = overwritable_formulas
2004 && allow.contains(&vid)
2005 {
2006 continue;
2007 }
2008 return Err(ExcelError::new(ExcelErrorKind::Spill)
2009 .with_message("BlockedByFormula")
2010 .with_extra(ExcelErrorExtra::Spill {
2011 expected_rows,
2012 expected_cols,
2013 }));
2014 }
2015 _ => {
2016 if let Some(vref) = self.vertex_values.get(&vid) {
2018 let v = self.data_store.retrieve_value(*vref);
2019 if !matches!(v, LiteralValue::Empty) {
2020 return Err(ExcelError::new(ExcelErrorKind::Spill)
2021 .with_message("BlockedByValue")
2022 .with_extra(ExcelErrorExtra::Spill {
2023 expected_rows,
2024 expected_cols,
2025 }));
2026 }
2027 }
2028 }
2029 }
2030 }
2031 }
2032 Ok(())
2033 }
2034
2035 pub fn commit_spill_region_atomic_with_fault(
2042 &mut self,
2043 anchor: VertexId,
2044 target_cells: Vec<CellRef>,
2045 values: Vec<Vec<LiteralValue>>,
2046 fault_after_ops: Option<usize>,
2047 ) -> Result<(), ExcelError> {
2048 use rustc_hash::FxHashSet;
2049
2050 let anchor_cell = self
2054 .get_cell_ref(anchor)
2055 .expect("anchor cell ref for spill commit");
2056 let anchor_sheet_name = self.sheet_name(anchor_cell.sheet_id).to_string();
2057 let anchor_row = anchor_cell.coord.row();
2058 let anchor_col = anchor_cell.coord.col();
2059
2060 let prev_cells = self
2062 .spill_anchor_to_cells
2063 .get(&anchor)
2064 .cloned()
2065 .unwrap_or_default();
2066 let new_set: FxHashSet<CellRef> = target_cells.iter().copied().collect();
2067 let prev_set: FxHashSet<CellRef> = prev_cells.iter().copied().collect();
2068
2069 #[derive(Clone)]
2071 struct Op {
2072 sheet: String,
2073 row: u32,
2074 col: u32,
2075 new_value: LiteralValue,
2076 }
2077 let mut ops: Vec<Op> = Vec::new();
2078
2079 for cell in prev_cells.iter() {
2081 if !new_set.contains(cell) {
2082 let sheet = self.sheet_name(cell.sheet_id).to_string();
2083 ops.push(Op {
2084 sheet,
2085 row: cell.coord.row(),
2086 col: cell.coord.col(),
2087 new_value: LiteralValue::Empty,
2088 });
2089 }
2090 }
2091
2092 if !target_cells.is_empty() {
2094 let first = target_cells.first().copied().unwrap();
2095 let row0 = first.coord.row();
2096 let col0 = first.coord.col();
2097 let sheet = self.sheet_name(first.sheet_id).to_string();
2098 for (r_off, row_vals) in values.iter().enumerate() {
2099 for (c_off, v) in row_vals.iter().enumerate() {
2100 ops.push(Op {
2101 sheet: sheet.clone(),
2102 row: row0 + r_off as u32,
2103 col: col0 + c_off as u32,
2104 new_value: v.clone(),
2105 });
2106 }
2107 }
2108 }
2109
2110 #[derive(Clone)]
2112 struct OldVal {
2113 present: bool,
2114 value: LiteralValue,
2115 }
2116 let mut old_values: Vec<((String, u32, u32), OldVal)> = Vec::with_capacity(ops.len());
2117
2118 for op in &ops {
2120 let old = self
2122 .get_cell_value(&op.sheet, op.row + 1, op.col + 1)
2123 .unwrap_or(LiteralValue::Empty);
2124 let present = true; old_values.push((
2126 (op.sheet.clone(), op.row, op.col),
2127 OldVal {
2128 present,
2129 value: old,
2130 },
2131 ));
2132 }
2133
2134 for (applied, op) in ops.iter().enumerate() {
2136 if let Some(n) = fault_after_ops
2137 && applied == n
2138 {
2139 for idx in (0..applied).rev() {
2140 let ((ref sheet, row, col), ref old) = old_values[idx];
2141 if sheet == &anchor_sheet_name && row == anchor_row && col == anchor_col {
2142 self.update_vertex_value(anchor, old.value.clone());
2143 } else {
2144 let _ = self.set_cell_value(sheet, row + 1, col + 1, old.value.clone());
2145 }
2146 }
2147 return Err(ExcelError::new(ExcelErrorKind::Error)
2148 .with_message("Injected persistence fault during spill commit"));
2149 }
2150 if op.sheet == anchor_sheet_name && op.row == anchor_row && op.col == anchor_col {
2151 self.update_vertex_value(anchor, op.new_value.clone());
2152 } else {
2153 let _ =
2154 self.set_cell_value(&op.sheet, op.row + 1, op.col + 1, op.new_value.clone());
2155 }
2156 }
2157
2158 for cell in prev_cells.iter() {
2161 if !new_set.contains(cell) {
2162 self.spill_cell_to_anchor.remove(cell);
2163 }
2164 }
2165 for cell in &target_cells {
2167 self.spill_cell_to_anchor.insert(*cell, anchor);
2168 }
2169 self.spill_anchor_to_cells.insert(anchor, target_cells);
2170 Ok(())
2171 }
2172
2173 pub(crate) fn spill_cells_for_anchor(&self, anchor: VertexId) -> Option<&[CellRef]> {
2174 self.spill_anchor_to_cells
2175 .get(&anchor)
2176 .map(|v| v.as_slice())
2177 }
2178
2179 pub(crate) fn spill_registry_has_anchor(&self, anchor: VertexId) -> bool {
2180 self.spill_anchor_to_cells.contains_key(&anchor)
2181 }
2182
2183 pub(crate) fn spill_registry_anchor_for_cell(&self, cell: CellRef) -> Option<VertexId> {
2184 self.spill_cell_to_anchor.get(&cell).copied()
2185 }
2186
2187 pub(crate) fn spill_registry_counts(&self) -> (usize, usize) {
2188 (
2189 self.spill_anchor_to_cells.len(),
2190 self.spill_cell_to_anchor.len(),
2191 )
2192 }
2193
2194 pub fn clear_spill_region(&mut self, anchor: VertexId) {
2196 let _ = self.clear_spill_region_bulk(anchor);
2197 }
2198
2199 pub fn clear_spill_region_bulk(&mut self, anchor: VertexId) -> Vec<CellRef> {
2208 let anchor_cell = self.get_cell_ref(anchor);
2209 let Some(cells) = self.spill_anchor_to_cells.remove(&anchor) else {
2210 return Vec::new();
2211 };
2212
2213 for cell in cells.iter() {
2215 self.spill_cell_to_anchor.remove(cell);
2216 }
2217
2218 let empty_ref = if self.value_cache_enabled {
2220 Some(self.data_store.store_value(LiteralValue::Empty))
2221 } else {
2222 None
2223 };
2224
2225 let mut changed_vertices: Vec<VertexId> = Vec::new();
2227 for cell in cells.iter().copied() {
2228 let is_anchor = anchor_cell.map(|a| a == cell).unwrap_or(false);
2229 if is_anchor {
2230 continue;
2231 }
2232 let Some(&vid) = self.cell_to_vertex.get(&cell) else {
2233 continue;
2234 };
2235 if self.vertex_formulas.remove(&vid).is_some() {
2237 self.remove_dependent_edges(vid);
2240 }
2241 self.store.set_kind(vid, VertexKind::Cell);
2242 if let Some(er) = empty_ref {
2243 self.vertex_values.insert(vid, er);
2244 } else {
2245 self.vertex_values.remove(&vid);
2246 }
2247 self.store.set_dirty(vid, false);
2248 self.dirty_vertices.remove(&vid);
2249 changed_vertices.push(vid);
2250 }
2251
2252 if !changed_vertices.is_empty() {
2254 self.mark_dirty_many_value_cells(&changed_vertices);
2255 }
2256
2257 cells
2258 }
2259
2260 fn mark_dirty_many_value_cells(&mut self, vertex_ids: &[VertexId]) -> Vec<VertexId> {
2261 if vertex_ids.is_empty() {
2262 return Vec::new();
2263 }
2264
2265 if self.edges.delta_size() > 0 {
2267 self.edges.rebuild();
2268 }
2269
2270 let mut affected: FxHashSet<VertexId> = FxHashSet::default();
2271 let mut to_visit: Vec<VertexId> = Vec::new();
2272 let mut visited_for_propagation: FxHashSet<VertexId> = FxHashSet::default();
2273
2274 for &src in vertex_ids {
2276 affected.insert(src);
2277 }
2278
2279 for &src in vertex_ids {
2281 to_visit.extend(self.edges.in_edges(src));
2282 if let Some(name_set) = self.cell_to_name_dependents.get(&src) {
2283 for &name_vertex in name_set {
2284 to_visit.push(name_vertex);
2285 }
2286 }
2287 }
2288
2289 let mut bounds_by_sheet: FxHashMap<SheetId, (u32, u32, u32, u32)> = FxHashMap::default();
2291 for &src in vertex_ids {
2292 let view = self.store.view(src);
2293 let sid = view.sheet_id();
2294 let r = view.row();
2295 let c = view.col();
2296 bounds_by_sheet
2297 .entry(sid)
2298 .and_modify(|b| {
2299 b.0 = b.0.min(r);
2300 b.1 = b.1.max(r);
2301 b.2 = b.2.min(c);
2302 b.3 = b.3.max(c);
2303 })
2304 .or_insert((r, r, c, c));
2305 }
2306
2307 for (sid, (sr, er, sc, ec)) in bounds_by_sheet {
2308 to_visit.extend(self.collect_range_dependents_for_rect(sid, sr, sc, er, ec));
2309 }
2310
2311 while let Some(id) = to_visit.pop() {
2312 if !visited_for_propagation.insert(id) {
2313 continue;
2314 }
2315 affected.insert(id);
2316 self.store.set_dirty(id, true);
2317 to_visit.extend(self.edges.in_edges(id));
2318 to_visit.extend(self.collect_range_dependents_for_vertex(id));
2319 }
2320
2321 self.dirty_vertices.extend(&affected);
2322 affected.into_iter().collect()
2323 }
2324
2325 fn collect_range_dependents_for_vertex(&self, vertex_id: VertexId) -> Vec<VertexId> {
2326 match self.store.kind(vertex_id) {
2327 VertexKind::Cell
2328 | VertexKind::Empty
2329 | VertexKind::FormulaScalar
2330 | VertexKind::FormulaArray => {
2331 let view = self.store.view(vertex_id);
2332 self.collect_range_dependents_for_rect(
2333 view.sheet_id(),
2334 view.row(),
2335 view.col(),
2336 view.row(),
2337 view.col(),
2338 )
2339 }
2340 _ => Vec::new(),
2341 }
2342 }
2343
2344 fn collect_range_dependents_for_rect(
2345 &self,
2346 sheet_id: SheetId,
2347 start_row: u32,
2348 start_col: u32,
2349 end_row: u32,
2350 end_col: u32,
2351 ) -> Vec<VertexId> {
2352 if self.stripe_to_dependents.is_empty() {
2353 return Vec::new();
2354 }
2355 let mut candidates: FxHashSet<VertexId> = FxHashSet::default();
2356
2357 for col in start_col..=end_col {
2358 let key = StripeKey {
2359 sheet_id,
2360 stripe_type: StripeType::Column,
2361 index: col,
2362 };
2363 if let Some(deps) = self.stripe_to_dependents.get(&key) {
2364 candidates.extend(deps);
2365 }
2366 }
2367 for row in start_row..=end_row {
2368 let key = StripeKey {
2369 sheet_id,
2370 stripe_type: StripeType::Row,
2371 index: row,
2372 };
2373 if let Some(deps) = self.stripe_to_dependents.get(&key) {
2374 candidates.extend(deps);
2375 }
2376 }
2377 if self.config.enable_block_stripes {
2378 let br0 = start_row / BLOCK_H;
2379 let br1 = end_row / BLOCK_H;
2380 let bc0 = start_col / BLOCK_W;
2381 let bc1 = end_col / BLOCK_W;
2382 for br in br0..=br1 {
2383 for bc in bc0..=bc1 {
2384 let key = StripeKey {
2385 sheet_id,
2386 stripe_type: StripeType::Block,
2387 index: block_index(br * BLOCK_H, bc * BLOCK_W),
2388 };
2389 if let Some(deps) = self.stripe_to_dependents.get(&key) {
2390 candidates.extend(deps);
2391 }
2392 }
2393 }
2394 }
2395
2396 let mut out: Vec<VertexId> = Vec::new();
2398 for dep_id in candidates {
2399 let Some(ranges) = self.formula_to_range_deps.get(&dep_id) else {
2400 continue;
2401 };
2402 let mut hit = false;
2403 for range in ranges {
2404 let range_sheet_id = match range.sheet {
2405 SharedSheetLocator::Id(id) => id,
2406 _ => sheet_id,
2407 };
2408 if range_sheet_id != sheet_id {
2409 continue;
2410 }
2411 let sr0 = range.start_row.map(|b| b.index).unwrap_or(0);
2412 let er0 = range.end_row.map(|b| b.index).unwrap_or(u32::MAX);
2413 let sc0 = range.start_col.map(|b| b.index).unwrap_or(0);
2414 let ec0 = range.end_col.map(|b| b.index).unwrap_or(u32::MAX);
2415 let overlap =
2416 sr0 <= end_row && er0 >= start_row && sc0 <= end_col && ec0 >= start_col;
2417 if overlap {
2418 hit = true;
2419 break;
2420 }
2421 }
2422 if hit {
2423 out.push(dep_id);
2424 }
2425 }
2426 out
2427 }
2428
2429 pub(crate) fn vertex_exists(&self, vertex_id: VertexId) -> bool {
2431 if vertex_id.0 < FIRST_NORMAL_VERTEX {
2432 return false;
2433 }
2434 let index = (vertex_id.0 - FIRST_NORMAL_VERTEX) as usize;
2435 index < self.store.len()
2436 }
2437
2438 pub(crate) fn get_vertex_kind(&self, vertex_id: VertexId) -> VertexKind {
2440 self.store.kind(vertex_id)
2441 }
2442
2443 pub(crate) fn get_vertex_sheet_id(&self, vertex_id: VertexId) -> SheetId {
2445 self.store.sheet_id(vertex_id)
2446 }
2447
2448 pub fn get_formula_id(&self, vertex_id: VertexId) -> Option<AstNodeId> {
2449 self.vertex_formulas.get(&vertex_id).copied()
2450 }
2451
2452 pub fn get_formula_id_and_volatile(&self, vertex_id: VertexId) -> Option<(AstNodeId, bool)> {
2453 let ast_id = self.get_formula_id(vertex_id)?;
2454 Some((ast_id, self.is_volatile(vertex_id)))
2455 }
2456
2457 pub fn get_formula_node(&self, vertex_id: VertexId) -> Option<&super::arena::AstNodeData> {
2458 let ast_id = self.get_formula_id(vertex_id)?;
2459 self.data_store.get_node(ast_id)
2460 }
2461
2462 pub fn get_formula_node_and_volatile(
2463 &self,
2464 vertex_id: VertexId,
2465 ) -> Option<(&super::arena::AstNodeData, bool)> {
2466 let (ast_id, vol) = self.get_formula_id_and_volatile(vertex_id)?;
2467 let node = self.data_store.get_node(ast_id)?;
2468 Some((node, vol))
2469 }
2470
2471 pub fn get_formula(&self, vertex_id: VertexId) -> Option<ASTNode> {
2475 let ast_id = self.get_formula_id(vertex_id)?;
2476 self.data_store.retrieve_ast(ast_id, &self.sheet_reg)
2477 }
2478
2479 pub fn get_value(&self, vertex_id: VertexId) -> Option<LiteralValue> {
2481 if !self.value_cache_enabled {
2482 match self.store.kind(vertex_id) {
2485 VertexKind::Cell
2486 | VertexKind::FormulaScalar
2487 | VertexKind::FormulaArray
2488 | VertexKind::Empty => {
2489 #[cfg(debug_assertions)]
2490 {
2491 self.graph_value_read_attempts
2492 .fetch_add(1, Ordering::Relaxed);
2493 }
2494 return None;
2495 }
2496 _ => {
2497 }
2499 }
2500 }
2501 self.vertex_values
2502 .get(&vertex_id)
2503 .map(|&value_ref| self.data_store.retrieve_value(value_ref))
2504 }
2505
2506 pub(crate) fn get_cell_ref(&self, vertex_id: VertexId) -> Option<CellRef> {
2508 let packed_coord = self.store.coord(vertex_id);
2509 let sheet_id = self.store.sheet_id(vertex_id);
2510 let coord = Coord::new(packed_coord.row(), packed_coord.col(), true, true);
2511 Some(CellRef::new(sheet_id, coord))
2512 }
2513
2514 pub(crate) fn make_cell_ref_internal(&self, sheet_id: SheetId, row: u32, col: u32) -> CellRef {
2516 let coord = Coord::new(row, col, true, true);
2517 CellRef::new(sheet_id, coord)
2518 }
2519
2520 pub fn make_cell_ref(&self, sheet_name: &str, row: u32, col: u32) -> CellRef {
2522 let sheet_id = self.sheet_reg.get_id(sheet_name).unwrap_or(0);
2523 let coord = Coord::from_excel(row, col, true, true);
2524 CellRef::new(sheet_id, coord)
2525 }
2526
2527 pub(crate) fn is_dirty(&self, vertex_id: VertexId) -> bool {
2529 self.store.is_dirty(vertex_id)
2530 }
2531
2532 pub(crate) fn is_volatile(&self, vertex_id: VertexId) -> bool {
2534 self.store.is_volatile(vertex_id)
2535 }
2536
2537 pub(crate) fn is_dynamic(&self, vertex_id: VertexId) -> bool {
2538 self.store.is_dynamic(vertex_id)
2539 }
2540
2541 pub fn get_vertex_id_for_address(&self, addr: &CellRef) -> Option<&VertexId> {
2543 self.cell_to_vertex.get(addr)
2544 }
2545
2546 #[cfg(test)]
2547 pub fn cell_to_vertex(&self) -> &FxHashMap<CellRef, VertexId> {
2548 &self.cell_to_vertex
2549 }
2550
2551 #[inline]
2555 pub(crate) fn dependencies_slice(&self, vertex_id: VertexId) -> Option<&[VertexId]> {
2556 self.edges.out_edges_ref(vertex_id)
2557 }
2558
2559 pub(crate) fn get_dependencies(&self, vertex_id: VertexId) -> Vec<VertexId> {
2561 self.edges.out_edges(vertex_id)
2562 }
2563
2564 pub(crate) fn has_self_loop(&self, vertex_id: VertexId) -> bool {
2566 if let Some(deps) = self.dependencies_slice(vertex_id) {
2567 deps.contains(&vertex_id)
2568 } else {
2569 self.edges.out_edges(vertex_id).contains(&vertex_id)
2570 }
2571 }
2572
2573 #[inline]
2577 pub(crate) fn dependents_slice(&self, vertex_id: VertexId) -> Option<&[VertexId]> {
2578 self.edges.in_edges_ref(vertex_id)
2579 }
2580
2581 pub(crate) fn get_dependents(&self, vertex_id: VertexId) -> Vec<VertexId> {
2584 if self.edges.delta_size() > 0 {
2587 #[cfg(test)]
2588 {
2589 if let Ok(mut g) = self.instr.lock() {
2592 g.dependents_scan_fallback_calls += 1;
2593 g.dependents_scan_vertices_scanned += self.cell_to_vertex.len() as u64;
2594 }
2595 }
2596 let mut dependents = Vec::new();
2598 for (&_addr, &vid) in &self.cell_to_vertex {
2599 let out_edges = self.edges.out_edges(vid);
2600 if out_edges.contains(&vertex_id) {
2601 dependents.push(vid);
2602 }
2603 }
2604 for named in self.named_ranges.values() {
2605 let vid = named.vertex;
2606 let out_edges = self.edges.out_edges(vid);
2607 if out_edges.contains(&vertex_id) {
2608 dependents.push(vid);
2609 }
2610 }
2611 for named in self.sheet_named_ranges.values() {
2612 let vid = named.vertex;
2613 let out_edges = self.edges.out_edges(vid);
2614 if out_edges.contains(&vertex_id) {
2615 dependents.push(vid);
2616 }
2617 }
2618 dependents
2619 } else {
2620 self.edges.in_edges(vertex_id).to_vec()
2622 }
2623 }
2624
2625 #[doc(hidden)]
2629 pub fn snapshot_vertex(&self, id: VertexId) -> crate::engine::VertexSnapshot {
2630 let coord = self.store.coord(id);
2631 let sheet_id = self.store.sheet_id(id);
2632 let kind = self.store.kind(id);
2633 let flags = self.store.flags(id);
2634
2635 let value_ref = self.vertex_values.get(&id).copied();
2637 let formula_ref = self.vertex_formulas.get(&id).copied();
2638
2639 let out_edges = self.get_dependencies(id);
2641
2642 crate::engine::VertexSnapshot {
2643 coord,
2644 sheet_id,
2645 kind,
2646 flags,
2647 value_ref,
2648 formula_ref,
2649 out_edges,
2650 }
2651 }
2652
2653 #[doc(hidden)]
2655 pub fn remove_all_edges(&mut self, id: VertexId) {
2656 self.edges.begin_batch();
2658
2659 self.remove_dependent_edges(id);
2661
2662 self.edges.rebuild();
2665
2666 let dependents = self.get_dependents(id);
2668 if self.pk_order.is_some()
2669 && let Some(mut pk) = self.pk_order.take()
2670 {
2671 for dependent in &dependents {
2672 pk.remove_edge(id, *dependent);
2673 }
2674 self.pk_order = Some(pk);
2675 }
2676 for dependent in dependents {
2677 self.edges.remove_edge(dependent, id);
2678 }
2679
2680 self.edges.end_batch();
2682 }
2683
2684 #[doc(hidden)]
2686 pub fn mark_as_ref_error(&mut self, id: VertexId) {
2687 if !self.value_cache_enabled {
2688 match self.store.kind(id) {
2689 VertexKind::Cell
2690 | VertexKind::FormulaScalar
2691 | VertexKind::FormulaArray
2692 | VertexKind::Empty => {
2693 self.ref_error_vertices.insert(id);
2694 self.vertex_values.remove(&id);
2697 let _ = self.mark_dirty(id);
2698 return;
2699 }
2700 _ => {
2701 }
2703 }
2704 }
2705 let error = LiteralValue::Error(ExcelError::new(ExcelErrorKind::Ref));
2706 let value_ref = self.data_store.store_value(error);
2707 self.vertex_values.insert(id, value_ref);
2708 let _ = self.mark_dirty(id);
2709 }
2710
2711 pub fn is_ref_error(&self, id: VertexId) -> bool {
2713 if !self.value_cache_enabled {
2714 match self.store.kind(id) {
2715 VertexKind::Cell
2716 | VertexKind::FormulaScalar
2717 | VertexKind::FormulaArray
2718 | VertexKind::Empty => {
2719 return self.ref_error_vertices.contains(&id);
2720 }
2721 _ => {
2722 }
2724 }
2725 }
2726 if let Some(value_ref) = self.vertex_values.get(&id) {
2727 let value = self.data_store.retrieve_value(*value_ref);
2728 if let LiteralValue::Error(err) = value {
2729 return err.kind == ExcelErrorKind::Ref;
2730 }
2731 }
2732 false
2733 }
2734
2735 #[doc(hidden)]
2737 pub fn mark_dependents_dirty(&mut self, id: VertexId) {
2738 let dependents = self.get_dependents(id);
2739 for dep_id in dependents {
2740 self.store.set_dirty(dep_id, true);
2741 self.dirty_vertices.insert(dep_id);
2742 }
2743 }
2744
2745 #[doc(hidden)]
2747 pub fn mark_volatile(&mut self, id: VertexId, volatile: bool) {
2748 self.store.set_volatile(id, volatile);
2749 if volatile {
2750 self.volatile_vertices.insert(id);
2751 } else {
2752 self.volatile_vertices.remove(&id);
2753 }
2754 }
2755
2756 #[doc(hidden)]
2758 pub fn set_coord(&mut self, id: VertexId, coord: AbsCoord) {
2759 self.store.set_coord(id, coord);
2760 }
2761
2762 #[doc(hidden)]
2764 pub fn update_edge_coord(&mut self, id: VertexId, coord: AbsCoord) {
2765 self.edges.update_coord(id, coord);
2766 }
2767
2768 #[doc(hidden)]
2770 pub fn mark_deleted(&mut self, id: VertexId, deleted: bool) {
2771 self.store.mark_deleted(id, deleted);
2772 }
2773
2774 #[doc(hidden)]
2776 pub fn set_kind(&mut self, id: VertexId, kind: VertexKind) {
2777 self.store.set_kind(id, kind);
2778 }
2779
2780 #[doc(hidden)]
2782 pub fn set_dirty(&mut self, id: VertexId, dirty: bool) {
2783 self.store.set_dirty(id, dirty);
2784 if dirty {
2785 self.dirty_vertices.insert(id);
2786 } else {
2787 self.dirty_vertices.remove(&id);
2788 }
2789 }
2790
2791 #[cfg(test)]
2793 pub(crate) fn get_kind(&self, id: VertexId) -> VertexKind {
2794 self.store.kind(id)
2795 }
2796
2797 #[cfg(test)]
2799 pub(crate) fn get_flags(&self, id: VertexId) -> u8 {
2800 self.store.flags(id)
2801 }
2802
2803 #[cfg(test)]
2805 pub(crate) fn is_deleted(&self, id: VertexId) -> bool {
2806 self.store.is_deleted(id)
2807 }
2808
2809 #[doc(hidden)]
2811 pub fn rebuild_edges(&mut self) {
2812 self.edges.rebuild();
2813 }
2814
2815 #[doc(hidden)]
2817 pub fn edges_delta_size(&self) -> usize {
2818 self.edges.delta_size()
2819 }
2820
2821 pub fn get_vertex_for_cell(&self, addr: &CellRef) -> Option<VertexId> {
2823 self.cell_to_vertex.get(addr).copied()
2824 }
2825
2826 pub fn get_coord(&self, id: VertexId) -> AbsCoord {
2828 self.store.coord(id)
2829 }
2830
2831 pub fn get_sheet_id(&self, id: VertexId) -> SheetId {
2833 self.store.sheet_id(id)
2834 }
2835
2836 pub fn vertices_in_sheet(&self, sheet_id: SheetId) -> impl Iterator<Item = VertexId> + '_ {
2838 self.store
2839 .all_vertices()
2840 .filter(move |&id| self.vertex_exists(id) && self.store.sheet_id(id) == sheet_id)
2841 }
2842
2843 pub fn vertex_has_formula(&self, id: VertexId) -> bool {
2845 self.vertex_formulas.contains_key(&id)
2846 }
2847
2848 pub fn vertices_with_formulas(&self) -> impl Iterator<Item = VertexId> + '_ {
2850 self.vertex_formulas.keys().copied()
2851 }
2852
2853 pub fn update_vertex_formula(&mut self, id: VertexId, ast: ASTNode) -> Result<(), ExcelError> {
2855 let sheet_id = self.store.sheet_id(id);
2857
2858 let has_ref_marker = ast.get_dependencies().into_iter().any(|r| {
2862 matches!(
2863 r,
2864 ReferenceType::Cell { sheet: Some(s), .. }
2865 | ReferenceType::Range { sheet: Some(s), .. } if s == "#REF"
2866 )
2867 });
2868 if has_ref_marker {
2869 let ast_id = self.data_store.store_ast(&ast, &self.sheet_reg);
2871 self.vertex_formulas.insert(id, ast_id);
2872 self.mark_as_ref_error(id);
2873 self.store.set_kind(id, VertexKind::FormulaScalar);
2874 return Ok(());
2875 }
2876
2877 let (new_dependencies, new_range_dependencies, _, named_dependencies) =
2879 self.extract_dependencies(&ast, sheet_id)?;
2880
2881 self.remove_dependent_edges(id);
2883 self.detach_vertex_from_names(id);
2884
2885 let ast_id = self.data_store.store_ast(&ast, &self.sheet_reg);
2887 self.vertex_formulas.insert(id, ast_id);
2888
2889 self.add_dependent_edges(id, &new_dependencies);
2891 self.add_range_dependent_edges(id, &new_range_dependencies, sheet_id);
2892
2893 if !named_dependencies.is_empty() {
2894 self.attach_vertex_to_names(id, &named_dependencies);
2895 }
2896
2897 self.store.set_kind(id, VertexKind::FormulaScalar);
2899
2900 Ok(())
2901 }
2902
2903 pub fn mark_vertex_dirty(&mut self, vertex_id: VertexId) {
2905 self.store.set_dirty(vertex_id, true);
2906 self.dirty_vertices.insert(vertex_id);
2907 }
2908
2909 pub fn update_cell_mapping(
2911 &mut self,
2912 id: VertexId,
2913 old_addr: Option<CellRef>,
2914 new_addr: CellRef,
2915 ) {
2916 if let Some(old) = old_addr {
2918 self.cell_to_vertex.remove(&old);
2919 }
2920 self.cell_to_vertex.insert(new_addr, id);
2922 }
2923
2924 pub fn remove_cell_mapping(&mut self, addr: &CellRef) {
2926 self.cell_to_vertex.remove(addr);
2927 }
2928
2929 pub fn get_cell_ref_for_vertex(&self, id: VertexId) -> Option<CellRef> {
2931 let coord = self.store.coord(id);
2932 let sheet_id = self.store.sheet_id(id);
2933 let cell_ref = CellRef::new(sheet_id, Coord::new(coord.row(), coord.col(), true, true));
2935 if self.cell_to_vertex.get(&cell_ref) == Some(&id) {
2937 Some(cell_ref)
2938 } else {
2939 None
2940 }
2941 }
2942
2943 pub(crate) fn rebuild_formula_dependencies(&mut self, vertex_id: VertexId, ast: &ASTNode) {
2949 let sheet_id = self.store.sheet_id(vertex_id);
2950
2951 self.remove_dependent_edges(vertex_id);
2953 self.detach_vertex_from_names(vertex_id);
2954 self.clear_pending_name_references(vertex_id);
2955
2956 let (
2957 new_dependencies,
2958 new_range_dependencies,
2959 _created_placeholders,
2960 named_dependencies,
2961 unresolved_names,
2962 ) = match self.extract_dependencies_with_pending_names(ast, sheet_id) {
2963 Ok(v) => v,
2964 Err(_) => {
2965 self.mark_as_ref_error(vertex_id);
2966 return;
2967 }
2968 };
2969
2970 if new_dependencies.contains(&vertex_id) {
2972 self.mark_as_ref_error(vertex_id);
2973 return;
2974 }
2975
2976 for &name_vertex in &named_dependencies {
2977 let mut visited = FxHashSet::default();
2978 if self.name_depends_on_vertex(name_vertex, vertex_id, &mut visited) {
2979 self.mark_as_ref_error(vertex_id);
2980 return;
2981 }
2982 }
2983
2984 self.ref_error_vertices.remove(&vertex_id);
2986 self.vertex_values.remove(&vertex_id);
2987
2988 if !named_dependencies.is_empty() {
2989 self.attach_vertex_to_names(vertex_id, &named_dependencies);
2990 }
2991 for unresolved_name in &unresolved_names {
2992 self.record_pending_name_reference(sheet_id, unresolved_name, vertex_id);
2993 }
2994
2995 self.add_dependent_edges(vertex_id, &new_dependencies);
2996 self.add_range_dependent_edges(vertex_id, &new_range_dependencies, sheet_id);
2997 let _ = self.mark_dirty(vertex_id);
2998 }
2999}
3000
3001