deduplicate cell next pos loop

This commit is contained in:
PgBiel 2025-03-05 17:09:13 -03:00
parent 2d4a81ec24
commit ad049491b4

View File

@ -1876,32 +1876,13 @@ fn resolve_cell_position(
// Note that the counter ignores any cells with fixed positions, // Note that the counter ignores any cells with fixed positions,
// but automatically-positioned cells will avoid conflicts by // but automatically-positioned cells will avoid conflicts by
// simply skipping existing cells, headers and footers. // simply skipping existing cells, headers and footers.
let mut resolved_index = *auto_index; let resolved_index = find_next_available_position::<false>(
header,
loop { footer,
if let Some(Some(_)) = resolved_cells.get(resolved_index) { resolved_cells,
// Skip any non-absent cell positions (`Some(None)`) to columns,
// determine where this cell will be placed. An out of *auto_index,
// bounds position (thus `None`) is also a valid new )?;
// position (only requires expanding the vector).
resolved_index += 1;
} else if let Some(header) =
header.filter(|header| resolved_index / columns < header.end)
{
// Skip header
resolved_index = header.end * columns;
} else if let Some((footer_end, _, _)) =
footer.filter(|(end, _, footer)| {
resolved_index / columns >= footer.start
&& resolved_index / columns < *end
})
{
// Skip footer
resolved_index = *footer_end * columns;
} else {
break;
}
}
// Ensure the next cell with automatic position will be // Ensure the next cell with automatic position will be
// placed after this one (maybe not immediately after). // placed after this one (maybe not immediately after).
@ -1947,49 +1928,19 @@ fn resolve_cell_position(
// row / the header or footer's first row (specified through // row / the header or footer's first row (specified through
// 'first_available_row'). Otherwise, start searching at the // 'first_available_row'). Otherwise, start searching at the
// first row. // first row.
let mut resolved_y = first_available_row; let initial_index = cell_index(cell_x, first_available_row)?;
// There may be row groups, so we have to not only skip // Try each row until either we reach an absent position at the
// rows where the requested column is occupied to find the // requested column ('Some(None)') or an out of bounds position
// first suitable row, but also skip rows belonging to // ('None'), in which case we'd create a new row to place this
// headers or footers. // cell in.
loop { find_next_available_position::<true>(
if let Some(Some(_)) = header,
resolved_cells.get(cell_index(cell_x, resolved_y)?) footer,
{ resolved_cells,
// Try each row until either we reach an absent columns,
// position at the requested column (`Some(None)`) initial_index,
// or an out of bounds position (`None`), in which )
// case we'd create a new row to place this cell
// in.
//
// Therefore, this loop will always finish, even if
// the cell has to go all the way to the end to be
// at its requested column. However, if the cell is
// in a header or footer, that could cause the
// header or footer to expand and conflict with
// other cells along the way, but that is checked
// separately later in the 'expand_row_group'
// function.
resolved_y += 1;
} else if let Some(header) =
header.filter(|header| resolved_y < header.end)
{
// Skip header
resolved_y = header.end;
} else if let Some((footer_end, _, _)) =
footer.filter(|(end, _, footer)| {
resolved_y >= footer.start && resolved_y < *end
})
{
// Skip footer
resolved_y = *footer_end;
} else {
break;
}
}
cell_index(cell_x, resolved_y)
} }
} }
// Cell has only chosen its row, not its column. // Cell has only chosen its row, not its column.
@ -2027,6 +1978,64 @@ fn resolve_cell_position(
} }
} }
/// Finds the first available position after the initial index in the resolved
/// grid of cells. Skips any non-absent positions (positions which already
/// have cells specified by the user) as well as any headers and footers.
#[inline]
fn find_next_available_position<const SKIP_ROWS: bool>(
header: Option<&Header>,
footer: Option<&(usize, Span, Footer)>,
resolved_cells: &[Option<Entry<'_>>],
columns: usize,
initial_index: usize,
) -> HintedStrResult<usize> {
let mut resolved_index = initial_index;
loop {
if let Some(Some(_)) = resolved_cells.get(resolved_index) {
// Skip any non-absent cell positions (`Some(None)`) to
// determine where this cell will be placed. An out of
// bounds position (thus `None`) is also a valid new
// position (only requires expanding the vector).
if SKIP_ROWS {
// Skip one row at a time (cell chose its column, so we don't
// change it).
resolved_index =
resolved_index.checked_add(columns).ok_or_else(|| {
HintedString::from(eco_format!("cell position too large"))
})?;
} else {
// Ensure we don't run unnecessary checks in the hot path
// (for fully automatically-positioned cells). Memory usage
// would become impractically large before this overflows.
resolved_index += 1;
}
} else if let Some(header) =
header.filter(|header| resolved_index < header.end * columns)
{
// Skip header (can't place a cell inside it from outside it).
resolved_index = header.end * columns;
if SKIP_ROWS {
// Ensure the cell's chosen column is kept after the
// header.
resolved_index += initial_index % columns;
}
} else if let Some((footer_end, _, _)) = footer.filter(|(end, _, footer)| {
resolved_index >= footer.start * columns && resolved_index < *end * columns
}) {
// Skip footer, for the same reason.
resolved_index = *footer_end * columns;
if SKIP_ROWS {
resolved_index += initial_index % columns;
}
} else {
return Ok(resolved_index);
}
}
}
/// Computes the `y` of the next available empty row, given the auto index as /// Computes the `y` of the next available empty row, given the auto index as
/// an initial index for search, since we know that there are no empty rows /// an initial index for search, since we know that there are no empty rows
/// before automatically-positioned cells, as they are placed sequentially. /// before automatically-positioned cells, as they are placed sequentially.