Skip to main content

ironcalc_base/sheet_data/
chunks.rs

1//! The rows of a sheet, by position: chunks of consecutive rows, allocated as
2//! they are needed.
3
4use std::borrow::Cow;
5
6use bitcode::{Decode, Encode};
7
8use super::row::Row;
9use crate::constants::LAST_ROW;
10use crate::types::Cell;
11
12const CHUNK_BITS: u32 = 10;
13const CHUNK_ROWS: usize = 1 << CHUNK_BITS;
14const CHUNK_MASK: usize = CHUNK_ROWS - 1;
15
16/// The cells of a worksheet, by row and column. Both are 1-indexed.
17///
18/// A row can exist without cells: `set_row` with no cells creates one. The xlsx
19/// importer does that for rows that carry a height or a style and nothing
20/// else, and the exporter writes a `<row>` element for every row that exists
21/// here, so such rows survive a round trip. Removing the last cell of a row
22/// with `remove_cell` removes the row.
23///
24/// Rows outside the sheet, below 1 or beyond the last row, cannot hold cells:
25/// writing to them does nothing and reading them finds nothing.
26#[derive(Encode, Decode, Debug, Clone, Default)]
27pub struct SheetData {
28    /// `chunks[i]` holds rows `i * CHUNK_ROWS ..`, if any of them exists.
29    chunks: Vec<Option<Box<Chunk>>>,
30    /// How many rows exist.
31    row_count: usize,
32}
33
34#[derive(Encode, Decode, Debug, Clone)]
35struct Chunk {
36    /// Always `CHUNK_ROWS` long.
37    rows: Vec<Option<Row>>,
38    /// How many of them exist. A chunk with none is freed.
39    used: usize,
40}
41
42impl Chunk {
43    fn new() -> Self {
44        let mut rows = Vec::new();
45        rows.resize_with(CHUNK_ROWS, || None);
46        Chunk { rows, used: 0 }
47    }
48}
49
50/// The chunk and the place in it of a row, if the row is within the sheet.
51#[inline]
52fn locate(row: i32) -> Option<(usize, usize)> {
53    if !(1..=LAST_ROW).contains(&row) {
54        return None;
55    }
56    let row = row as usize;
57    Some((row >> CHUNK_BITS, row & CHUNK_MASK))
58}
59
60impl SheetData {
61    pub fn new() -> Self {
62        Self::default()
63    }
64
65    #[inline]
66    pub(super) fn row(&self, row: i32) -> Option<&Row> {
67        let (chunk, index) = locate(row)?;
68        self.chunks.get(chunk)?.as_ref()?.rows[index].as_ref()
69    }
70
71    #[inline]
72    fn row_mut(&mut self, row: i32) -> Option<&mut Row> {
73        let (chunk, index) = locate(row)?;
74        self.chunks.get_mut(chunk)?.as_mut()?.rows[index].as_mut()
75    }
76
77    /// The row, created if it does not exist. `None` outside the sheet.
78    fn row_or_insert(&mut self, row: i32) -> Option<&mut Row> {
79        let (chunk, index) = locate(row)?;
80        if chunk >= self.chunks.len() {
81            self.chunks.resize_with(chunk + 1, || None);
82        }
83        let chunk = self.chunks[chunk].get_or_insert_with(|| Box::new(Chunk::new()));
84        if chunk.rows[index].is_none() {
85            chunk.rows[index] = Some(Row::default());
86            chunk.used += 1;
87            self.row_count += 1;
88        }
89        chunk.rows[index].as_mut()
90    }
91
92    /// Every row that exists with its number, in ascending order.
93    fn rows_in_order(&self) -> impl Iterator<Item = (i32, &Row)> {
94        self.chunks
95            .iter()
96            .enumerate()
97            .filter_map(|(chunk_index, chunk)| Some((chunk_index, chunk.as_ref()?)))
98            .flat_map(|(chunk_index, chunk)| {
99                chunk
100                    .rows
101                    .iter()
102                    .enumerate()
103                    .filter_map(move |(index, row)| {
104                        let number = (chunk_index << CHUNK_BITS) | index;
105                        Some((number as i32, row.as_ref()?))
106                    })
107            })
108    }
109
110    /// The cell at a position, if there is one.
111    #[inline]
112    pub fn cell(&self, row: i32, column: i32) -> Option<Cow<'_, Cell>> {
113        self.row(row)?.get(column)
114    }
115
116    /// The cell at a position, to be changed in place.
117    pub fn cell_mut(&mut self, row: i32, column: i32) -> Option<&mut Cell> {
118        self.row_mut(row)?.get_mut(column)
119    }
120
121    /// Puts a cell at a position and returns the one that was there.
122    pub fn set_cell(&mut self, row: i32, column: i32, cell: Cell) -> Option<Cell> {
123        self.row_or_insert(row)?.insert(column, cell)
124    }
125
126    /// Removes the cell at a position and returns it.
127    pub fn remove_cell(&mut self, row: i32, column: i32) -> Option<Cell> {
128        let row_data = self.row_mut(row)?;
129        let cell = row_data.remove(column)?;
130        if row_data.is_empty() {
131            self.remove_row(row);
132        }
133        Some(cell)
134    }
135
136    /// True if the sheet has no rows.
137    pub fn is_empty(&self) -> bool {
138        self.row_count == 0
139    }
140
141    /// Every cell of the sheet with its row and column, in natural order:
142    /// by row, and within a row by column.
143    pub fn cells(&self) -> impl Iterator<Item = (i32, i32, Cow<'_, Cell>)> {
144        self.rows_in_order().flat_map(|(row, row_data)| {
145            row_data
146                .iter()
147                .map(move |(column, cell)| (row, column, cell))
148        })
149    }
150
151    /// The rows that exist, in ascending order.
152    pub fn rows(&self) -> Vec<i32> {
153        self.rows_in_order().map(|(row, _)| row).collect()
154    }
155
156    /// The columns of a row that have cells, in ascending order.
157    pub fn columns_in_row(&self, row: i32) -> Vec<i32> {
158        match self.row(row) {
159            Some(row_data) => row_data.iter().map(|(column, _)| column).collect(),
160            None => Vec::new(),
161        }
162    }
163
164    /// The cells of a row with their column, in ascending order of column.
165    pub fn cells_in_row(&self, row: i32) -> Vec<(i32, Cow<'_, Cell>)> {
166        match self.row(row) {
167            Some(row_data) => row_data.iter().collect(),
168            None => Vec::new(),
169        }
170    }
171
172    /// The cells of a row from `first_column` to `last_column`, both included,
173    /// with their column, in ascending order of column. Reading a stretch of a
174    /// row this way finds the row once, not once per cell.
175    pub fn cells_in_row_between(
176        &self,
177        row: i32,
178        first_column: i32,
179        last_column: i32,
180    ) -> impl Iterator<Item = (i32, Cow<'_, Cell>)> {
181        self.row(row)
182            .map(|row_data| row_data.iter_from(first_column))
183            .into_iter()
184            .flatten()
185            .take_while(move |(column, _)| *column <= last_column)
186    }
187
188    /// The numbers of a row from `first_column` to `last_column`, both
189    /// included, if the row holds them as numbers and nothing else, with none
190    /// missing. `None` is no news about the row: its cells say what it holds.
191    #[inline]
192    pub fn numbers_in_row_between(
193        &self,
194        row: i32,
195        first_column: i32,
196        last_column: i32,
197    ) -> Option<&[f64]> {
198        match self.row(row)? {
199            Row::Numbers {
200                first_column: row_first_column,
201                values,
202                ..
203            } => {
204                let first = Row::offset(*row_first_column, values, first_column)?;
205                let last = Row::offset(*row_first_column, values, last_column)?;
206                values.get(first..=last)
207            }
208            Row::Cells(_) => None,
209        }
210    }
211
212    /// Removes every cell of a row, and the row.
213    pub fn remove_row(&mut self, row: i32) {
214        let Some((chunk_index, index)) = locate(row) else {
215            return;
216        };
217        let Some(Some(chunk)) = self.chunks.get_mut(chunk_index) else {
218            return;
219        };
220        if chunk.rows[index].take().is_some() {
221            chunk.used -= 1;
222            self.row_count -= 1;
223            if chunk.used == 0 {
224                self.chunks[chunk_index] = None;
225            }
226        }
227    }
228
229    /// Replaces the cells of a row. The row exists afterwards, even with no
230    /// cells. If a column is given twice the last one stays.
231    pub fn set_row(&mut self, row: i32, cells: impl IntoIterator<Item = (i32, Cell)>) {
232        let Some(row_data) = self.row_or_insert(row) else {
233            return;
234        };
235        // Collected at its exact size: a row grown cell by cell from empty
236        // starts with room for four, and a sheet with a million short rows
237        // paid for that four times over.
238        let mut cells: Vec<(i32, Cell)> = cells.into_iter().collect();
239        // A file gives the cells of a row from left to right, and then there
240        // is nothing to sort and no column given twice.
241        if !cells.is_sorted_by(|earlier, later| earlier.0 < later.0) {
242            cells.sort_by_key(|(column, _)| *column);
243            // Of two cells in the same column the later one stays: it is moved
244            // into the slot of the earlier one, which `dedup_by` then keeps.
245            cells.dedup_by(|later, earlier| {
246                if later.0 == earlier.0 {
247                    std::mem::swap(&mut later.1, &mut earlier.1);
248                    true
249                } else {
250                    false
251                }
252            });
253        }
254        cells.shrink_to_fit();
255        *row_data = Row::from_cells(cells);
256    }
257}
258
259/// Two sheets are equal when they have the same rows and the same cells.
260/// Which chunks happen to be allocated is not part of that.
261impl PartialEq for SheetData {
262    fn eq(&self, other: &Self) -> bool {
263        self.row_count == other.row_count && self.rows_in_order().eq(other.rows_in_order())
264    }
265}