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