Limit Rect size to prevent u16 overflow

pull/94/head
Karoline Pauls 6 years ago committed by Florian Dehau
parent 50fef0fb26
commit 5cee2afc6d

@ -45,12 +45,7 @@ impl Backend for CrosstermBackend {
fn size(&self) -> io::Result<Rect> {
let terminal = crossterm::terminal::terminal(&self.screen);
let (width, height) = terminal.terminal_size();
Ok(Rect {
x: 0,
y: 0,
width,
height,
})
Ok(Rect::new(0, 0, width, height))
}
fn flush(&mut self) -> io::Result<()> {

@ -57,12 +57,23 @@ impl Backend for RustboxBackend {
Ok(())
}
fn size(&self) -> Result<Rect, io::Error> {
Ok(Rect {
x: 0,
y: 0,
width: self.rustbox.width() as u16,
height: self.rustbox.height() as u16,
})
let term_width = self.rustbox.width();
let term_height = self.rustbox.height();
let max = u16::max_value();
Ok(Rect::new(
0,
0,
if term_width > usize::from(max) {
max
} else {
term_width as u16
},
if term_height > usize::from(max) {
max
} else {
term_height as u16
},
))
}
fn flush(&mut self) -> Result<(), io::Error> {
self.rustbox.present();

@ -112,12 +112,7 @@ where
/// Return the size of the terminal
fn size(&self) -> io::Result<Rect> {
let terminal = try!(termion::terminal_size());
Ok(Rect {
x: 0,
y: 0,
width: terminal.0,
height: terminal.1,
})
Ok(Rect::new(0, 0, terminal.0, terminal.1))
}
fn flush(&mut self) -> io::Result<()> {

@ -16,12 +16,7 @@ impl TestBackend {
TestBackend {
width,
height,
buffer: Buffer::empty(Rect {
x: 0,
y: 0,
width,
height,
}),
buffer: Buffer::empty(Rect::new(0, 0, width, height)),
cursor: false,
}
}
@ -55,12 +50,7 @@ impl Backend for TestBackend {
Ok(())
}
fn size(&self) -> Result<Rect, io::Error> {
Ok(Rect {
x: 0,
y: 0,
width: self.width,
height: self.height,
})
Ok(Rect::new(0, 0, self.width, self.height))
}
fn flush(&mut self) -> Result<(), io::Error> {
Ok(())

@ -279,12 +279,25 @@ impl Default for Rect {
}
impl Rect {
/// Creates a new rect, with width and height limited to keep the area under max u16.
/// If clipped, aspect ratio will be preserved.
pub fn new(x: u16, y: u16, width: u16, height: u16) -> Rect {
let max_area = u16::max_value();
let (clipped_width, clipped_height) =
if u32::from(width) * u32::from(height) > u32::from(max_area) {
let aspect_ratio = f64::from(width) / f64::from(height);
let max_area_f = f64::from(max_area);
let height_f = (max_area_f / aspect_ratio).sqrt();
let width_f = height_f * aspect_ratio;
(width_f as u16, height_f as u16)
} else {
(width, height)
};
Rect {
x,
y,
width,
height,
width: clipped_width,
height: clipped_height,
}
}
@ -354,3 +367,46 @@ impl Rect {
&& self.y + self.height > other.y
}
}
#[test]
fn test_rect_size_truncation() {
for width in 256u16..300u16 {
for height in 256u16..300u16 {
let rect = Rect::new(0, 0, width, height);
rect.area(); // Should not panic.
assert!(rect.width < width || rect.height < height);
// The target dimensions are rounded down so the math will not be too precise
// but let's make sure the ratios don't diverge crazily.
assert!(
(f64::from(rect.width) / f64::from(rect.height)
- f64::from(width) / f64::from(height)).abs()
< 1.0
)
}
}
// One dimension below 255, one above. Area above max u16.
let width = 900;
let height = 100;
let rect = Rect::new(0, 0, width, height);
assert_ne!(rect.width, 900);
assert_ne!(rect.height, 100);
assert!(rect.width < width || rect.height < height);
}
#[test]
fn test_rect_size_preservation() {
for width in 0..256u16 {
for height in 0..256u16 {
let rect = Rect::new(0, 0, width, height);
rect.area(); // Should not panic.
assert_eq!(rect.width, width);
assert_eq!(rect.height, height);
}
}
// One dimension below 255, one above. Area below max u16.
let rect = Rect::new(0, 0, 300, 100);
assert_eq!(rect.width, 300);
assert_eq!(rect.height, 100);
}

@ -0,0 +1,13 @@
extern crate tui;
use tui::backend::{Backend, TestBackend};
use tui::Terminal;
#[test]
fn buffer_size_limited() {
let backend = TestBackend::new(400, 400);
let terminal = Terminal::new(backend).unwrap();
let size = terminal.backend().size().unwrap();
assert_eq!(size.width, 255);
assert_eq!(size.height, 255);
}
Loading…
Cancel
Save