fix(geom): Rect::width/height no longer overflow on i32-extreme edges (#675) (#808)

width() and height() computed `x1 - x0` / `y1 - y0` with plain i32
subtraction. For extreme edges (e.g. an inverted i32::MAX..i32::MIN
rect, or a valid rect spanning most of the i32 range) the subtraction
overflows: debug builds panic, release builds wrap. warp.* commands
build a Rect straight from the JSON `rect` param, so an extreme value
reached this from user input.

Both now return the exact extent via `abs_diff` when the far edge is
past the near one, and 0 otherwise. The extent of any i32 pair fits in
u32, so no clamping or casting is needed. A plain `saturating_sub`
would avoid the panic but turn a wide valid rect into a width of
i32::MAX, which is wrong.

Follow-up (not in this change): warp_cmds still accepts absurd `rect`
params; it should reject them with a parameter error up front.

Co-authored-by: Luna <luna161@users.noreply.github.com>
This commit is contained in:
Luna 2026-10-08 00:00:59 +02:00 committed by GitHub
parent a508ee9014
commit 0c976f42d5
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
1 changed files with 17 additions and 2 deletions

View File

@ -51,11 +51,13 @@ impl Rect {
pub fn from_size(size: Size) -> Self {
Self::from_xywh(0, 0, size.width, size.height)
}
/// Horizontal extent; 0 when `x1 <= x0`. Exact even for i32-extreme edges.
pub fn width(&self) -> u32 {
(self.x1 - self.x0).max(0) as u32
if self.x1 > self.x0 { self.x1.abs_diff(self.x0) } else { 0 }
}
/// Vertical extent; 0 when `y1 <= y0`. Exact even for i32-extreme edges.
pub fn height(&self) -> u32 {
(self.y1 - self.y0).max(0) as u32
if self.y1 > self.y0 { self.y1.abs_diff(self.y0) } else { 0 }
}
pub fn size(&self) -> Size {
Size::new(self.width(), self.height())
@ -255,4 +257,17 @@ mod tests {
assert_eq!(grown.x1, i32::MAX);
assert_eq!(grown.y0, i32::MIN);
}
#[test]
fn width_and_height_do_not_overflow_on_extreme_edges() {
// Inverted extreme rect: empty, so 0 (x1 - x0 used to overflow and panic).
let r = Rect::new(i32::MAX, i32::MAX, i32::MIN, i32::MIN);
assert_eq!((r.width(), r.height()), (0, 0));
assert!(r.is_empty());
// Widest possible rect: the true extent fits in u32.
let r = Rect::new(i32::MIN, i32::MIN + 20, i32::MAX, i32::MAX - 5);
assert_eq!(r.width(), u32::MAX);
assert_eq!(r.height(), u32::MAX - 25);
assert_eq!(r.size(), Size::new(u32::MAX, u32::MAX - 25));
}
}