fix: allow canvas area to exceed u16::MAX (#1891)

This allows Canvas grids where the width * height exceeds u16::MAX by
converting values to usize earlier in several methods.

Fixes: https://github.com/ratatui/ratatui/issues/1449
This commit is contained in:
Daksh
2025-06-03 19:04:47 -04:00
committed by GitHub
parent 861fbdf5cf
commit 12cb5a28fe

View File

@@ -128,7 +128,7 @@ impl BrailleGrid {
/// Create a new `BrailleGrid` with the given width and height measured in terminal columns and
/// rows respectively.
fn new(width: u16, height: u16) -> Self {
let length = usize::from(width * height);
let length = usize::from(width) * usize::from(height);
Self {
width,
height,
@@ -156,7 +156,10 @@ impl Grid for BrailleGrid {
}
fn paint(&mut self, x: usize, y: usize, color: Color) {
let index = y / 4 * self.width as usize + x / 2;
let index = y
.saturating_div(4)
.saturating_mul(self.width as usize)
.saturating_add(x.saturating_div(2));
// using get_mut here because we are indexing the vector with usize values
// and we want to make sure we don't panic if the index is out of bounds
if let Some(c) = self.utf16_code_points.get_mut(index) {
@@ -190,7 +193,7 @@ impl CharGrid {
/// Create a new `CharGrid` with the given width and height measured in terminal columns and
/// rows respectively.
fn new(width: u16, height: u16, cell_char: char) -> Self {
let length = usize::from(width * height);
let length = usize::from(width) * usize::from(height);
Self {
width,
height,
@@ -219,7 +222,7 @@ impl Grid for CharGrid {
}
fn paint(&mut self, x: usize, y: usize, color: Color) {
let index = y * self.width as usize + x;
let index = y.saturating_mul(self.width as usize).saturating_add(x);
// using get_mut here because we are indexing the vector with usize values
// and we want to make sure we don't panic if the index is out of bounds
if let Some(c) = self.cells.get_mut(index) {
@@ -261,7 +264,7 @@ impl HalfBlockGrid {
Self {
width,
height,
pixels: vec![vec![Color::Reset; width as usize]; height as usize * 2],
pixels: vec![vec![Color::Reset; width as usize]; (height as usize) * 2],
}
}
}
@@ -937,4 +940,40 @@ mod tests {
),
);
}
// The canvas methods work a lot with arithmetic so here we enter various width and height
// values to check if there are any integer overflows we just initialize the canvas painters
#[test]
fn check_canvas_paint_max() {
let mut b_grid = BrailleGrid::new(u16::MAX, 2);
let mut c_grid = CharGrid::new(u16::MAX, 2, 'd');
let max = u16::MAX as usize;
b_grid.paint(0, 0, Color::Red);
b_grid.paint(0, max, Color::Red);
b_grid.paint(max, 0, Color::Red);
b_grid.paint(max, max, Color::Red);
c_grid.paint(0, 0, Color::Red);
c_grid.paint(0, max, Color::Red);
c_grid.paint(max, 0, Color::Red);
c_grid.paint(max, max, Color::Red);
}
// We delibately cause integer overflow to check if we don't panic and don't get weird behavior
#[test]
fn check_canvas_paint_overflow() {
let mut b_grid = BrailleGrid::new(u16::MAX, 3);
let mut c_grid = CharGrid::new(u16::MAX, 3, 'd');
let max = u16::MAX as usize + 10;
// see if we can paint outside bounds
b_grid.paint(max, max, Color::Red);
c_grid.paint(max, max, Color::Red);
// see if we can paint usize max bounds
b_grid.paint(usize::MAX, usize::MAX, Color::Red);
c_grid.paint(usize::MAX, usize::MAX, Color::Red);
}
}