1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
//! The spill-anchor-rectangle cache (issue #984).
//!
//! `Workbook::anchor_rectangles` (private, `crate::recalc`) is a full scan of
//! every authored cell on every sheet, looking for an array-valued (spilled)
//! cell — and it used to run unconditionally, once per incremental recalc
//! call and twice more per spill-widen pass, regardless of whether the
//! workbook had any spills, any formulas, or any range precedents at all.
//! This module holds that result on the workbook and hands it back until a
//! mutation could have changed it, mirroring [`crate::graph_cache`]'s
//! `CachedGraph`/`GraphCache` pattern.
//!
//! # What the map is a function of
//!
//! Reading `anchor_rectangles`, the map depends on exactly: every authored
//! cell's `(sheet, address, value)` where the value is `Value::Array`. It is
//! **not** a function of formulas, names, or tables as such — only of which
//! cells currently hold an array value.
//!
//! # The invalidation contract — a genuinely separate schedule from the graph
//!
//! This cache **cannot** ride [`crate::graph_cache::GraphCache`]'s
//! invalidation schedule. Recalc's own value write-back
//! (`Workbook::apply_changes`, through `sheets_mut_untracked`) is exactly the
//! write that places, resizes, or removes a spill — and the graph-cache module
//! docs deliberately document that write-back as one of the two cases that
//! must **not** invalidate the graph cache, since it preserves formula text
//! and adds/removes no node or edge. So an ordinary recalc that changes a
//! spill's footprint changes this cache's answer while leaving the graph cache
//! warm on purpose: sharing the graph's schedule would silently serve stale
//! rectangles across the spill-widen loop's own before/after comparison — a
//! correctness bug, not just a missed optimization.
//!
//! A literal write can also create or destroy an array-valued cell directly
//! (`Workbook::set` accepts `CellInput::Literal(Value::Array(..))`, and
//! `check_value_limits` has a dedicated `Value::Array` branch enforcing the
//! element-count cap — array literals are a supported input shape, not just a
//! formula-evaluation output), so this cache cannot be keyed off "did a
//! formula change" either.
//!
//! The narrow, correct condition — checked at every grid-cell write site
//! (`Workbook::set`, `Workbook::clear`, and `apply_changes`'s per-cell
//! write-back) — is: **the old value was `Value::Array`, or the new value is
//! `Value::Array`.** That covers a new spill, a cleared spill, and a resized
//! spill (a resize already satisfies "old was Array", so no separate size
//! comparison is needed). Every other worksheet-mutating entry point on
//! [`Workbook`](crate::Workbook) that hands out unobserved write access
//! (`sheets_mut`, `sheet_mut`, `insert_sheet`, `remove_sheet`, `rename_sheet`)
//! invalidates unconditionally on the same worst-case reasoning the graph
//! cache already applies to those methods — the borrow, or the incoming
//! sheet's cell content, is unobservable from here.
use BTreeMap;
use ;
use Arc;
use crateCellRef;
use crateSpillRect;
/// The workbook's spill-anchor-rectangle cache slot.
///
/// A field of [`Workbook`](crate::Workbook), so it must not disturb the
/// workbook's value-object contract: it is skipped by serde, compares equal to
/// every other cache, and hashes to nothing. Two workbooks with the same
/// content are still equal and still hash the same whether or not either has
/// recalculated.
pub
/// Every cache compares equal to every other: the cache is derived state, so
/// two workbooks that differ only in whether they have recalculated are the
/// same workbook (schema spec §8 — the document is the value).
/// Hashes to nothing, for the same reason [`PartialEq`] ignores it: `a == b`
/// must imply `hash(a) == hash(b)`.