ironcalc_base/sheet_data/mod.rs
1//! The cells of a worksheet.
2//!
3//! Everything that reads or writes the cells of a sheet goes through
4//! [`SheetData`], so that how they are stored is nobody else's business.
5//!
6//! # How they are stored
7//!
8//! By position, with no hashing. Rows are grouped in chunks of `CHUNK_ROWS`
9//! consecutive rows, and a chunk is allocated the first time one of its rows is
10//! used, so a sheet with a single cell in its last row costs one chunk and not
11//! a million empty rows. A row is a vector of `(column, cell)` sorted by column.
12//!
13//! A row of nothing but numbers in consecutive columns is not even that: it
14//! is the numbers, eight bytes for each instead of the size of a cell, with
15//! the style they share and the few that have another. Sheets of data are
16//! made of such rows, and a workbook with a hundred million numbers in it is
17//! mostly them. A cell of such a row exists only
18//! while someone looks at it, which is why cells are handed out as
19//! `Cow<Cell>`: borrowed from the sheet when the sheet holds a cell, made on
20//! the spot when it holds a number. Writing anything else into the row turns
21//! it into a row of cells; nothing but `set_row` turns it back.
22//!
23//! ```text
24//! chunks ──► [ chunk 0 | (none) | chunk 2 | ... ] row >> CHUNK_BITS
25//! │
26//! ▼
27//! [ row 0 | row 1 | (none) | ... ] row & CHUNK_MASK
28//! │
29//! ▼
30//! [ (A, cell) (C, cell) (D, cell) ] sorted by column
31//! ```
32//!
33//! Reading a cell is two indexed steps and a short search. Rows and cells come
34//! out in natural order, first by row and then by column, without sorting.
35//! Evaluation reads every cell several times, and with a hash map per row each
36//! read was two hashes and two jumps to unrelated places in memory, which on a
37//! large sheet cost more than running the formulas.
38
39mod chunks;
40mod row;
41mod saved;
42#[cfg(test)]
43mod test;
44
45pub use chunks::SheetData;