forked from 1b-agent/opencadstudio-upstream
fix(data-link): clamp cell-range column window to the data width
Bug: crop_range bounded the row axis with skip/take over the file's real rows but left the column axis unbounded: c0..=c1 collects one String per column, and a range like A1:ZZZZZZ1 parses to roughly 308 million columns, causing a capacity overflow while allocating a row. Reproduce: 1. Data Link -> pick any CSV/xlsx, set Cell Range to A1:ZZZZZZ1 (or any range whose parsed column index is enormous). 2. Before: "capacity overflow" abort while building the preview. 3. After: the window is cropped to the file's widest row; columns past the data yield nothing beyond the data width. Fix: c1 is clamped to widest.saturating_sub(1) before the range is collected, so the span is provably at most the file's own data - allocation is O(file). Rows outside the file still come back empty (in-range blank padding inside the data width is preserved), and c0 > c1 naturally yields an empty window. Regression tests cover the 13-Z capacity-overflow input and a 6-Z range that now crops.
This commit is contained in:
1 parent
c86746726b
commit
a0308dfa88
1 file changed
+27
@@ -151,6 +151,12 @@ fn crop_range(rows: Vec<Vec<String>>, range: &str) -> Result<Vec<Vec<String>>, S
|
||||
};
|
||||
let (r0, r1) = (r0.min(r1), r0.max(r1));
|
||||
let (c0, c1) = (c0.min(c1), c0.max(c1));
|
||||
// The row axis is bounded by the file (skip/take over real rows); the
|
||||
// column axis was not — `A1:ZZZZZZ1` parses fine and then collected one
|
||||
// String per column (~308 M slots per row, OOM). Columns past the widest
|
||||
// row are empty in every row, so clamp the window to the data width.
|
||||
let widest = rows.iter().map(Vec::len).max().unwrap_or(0);
|
||||
let c1 = c1.min(widest.saturating_sub(1));
|
||||
Ok(rows.into_iter()
|
||||
.skip(r0)
|
||||
.take(r1 - r0 + 1)
|
||||
@@ -1451,4 +1457,25 @@ mod tests {
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/// `column_index` only checks `checked_mul(26)`, so a range like
|
||||
/// `A1:ZZZZZZ1` parses fine — and `(c0..=c1)` then collects one `String`
|
||||
/// per column: ~308 M slots (~7 GB) for 6 Z's, and a `capacity overflow`
|
||||
/// panic at 13 Z's. Columns past the widest row are empty in *every*
|
||||
/// row, so the window must be clamped to the data width.
|
||||
#[test]
|
||||
fn crop_range_bounds_oversized_column_spans() {
|
||||
let rows = vec![vec!["x".to_string()]];
|
||||
// 13 Z's: pre-fix, size_hint × size_of::<String>() exceeds isize::MAX
|
||||
// and Vec's capacity check panics before a single cell is read.
|
||||
assert_eq!(
|
||||
crop_range(rows.clone(), "A1:ZZZZZZZZZZZZZ1").unwrap(),
|
||||
vec![vec!["x".to_string()]]
|
||||
);
|
||||
// 6 Z's — the bug-hunt report's repro — stays bounded too.
|
||||
assert_eq!(
|
||||
crop_range(rows, "A1:ZZZZZZ1").unwrap(),
|
||||
vec![vec!["x".to_string()]]
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user