safe conversions

This commit is contained in:
2025-04-27 11:26:12 +02:00
parent ae56f78075
commit ebd3d4008b
2 changed files with 100 additions and 45 deletions

View File

@@ -16,6 +16,8 @@ pub struct TerminalText {
cursor: Cursor,
}
mod safe_casts;
use log::{debug, trace};
impl TerminalText {
@@ -55,10 +57,11 @@ impl TerminalText {
pub fn resize(&mut self, width: u32, height: u32) {
trace!("Resizing window - Width: {width} Height: {height}");
self.buffer.set_size(
&mut self.font_system,
Some(width as f32),
Some(height as f32),
Some(safe_casts::u32_to_f32_or_max(width)),
Some(safe_casts::u32_to_f32_or_max(height)),
);
// Update the buffer's wrapping based on the new width
self.buffer.set_wrap(&mut self.font_system, Wrap::Glyph);
@@ -68,6 +71,14 @@ impl TerminalText {
}
fn merge_dirty_regions(&self) -> TextBounds {
if self.dirty_regions.is_empty() {
return TextBounds {
left: 0,
top: 0,
right: 0,
bottom: 0,
};
}
TextBounds {
left: self
.dirty_regions
@@ -101,29 +112,27 @@ impl TerminalText {
},
);
if !self.dirty_regions.is_empty() {
let region = self.merge_dirty_regions();
trace!("Preparing region {:?}", region);
self.renderer
.prepare(
device,
queue,
&mut self.font_system,
&mut self.atlas,
&self.viewport,
[TextArea {
buffer: &self.buffer,
left: 0.0,
top: 0.0,
scale: 1.0,
bounds: region,
default_color: Color::rgb(255, 255, 255),
custom_glyphs: &[],
}],
&mut self.cache,
)
.unwrap();
}
let region = self.merge_dirty_regions();
trace!("Preparing region {:?}", region);
self.renderer
.prepare(
device,
queue,
&mut self.font_system,
&mut self.atlas,
&self.viewport,
[TextArea {
buffer: &self.buffer,
left: 0.0,
top: 0.0,
scale: 1.0,
bounds: region,
default_color: Color::rgb(255, 255, 255),
custom_glyphs: &[],
}],
&mut self.cache,
)
.unwrap();
self.dirty_regions.clear();
}
@@ -235,28 +244,15 @@ impl TerminalText {
}
// Calculate bounds based on visual line positions
let top_f32 = (top_line as f32 * line_height).floor();
let bottom_f32 = (bottom_line as f32 * line_height).ceil();
let top_f32 = (safe_casts::usize_to_f32_or_max(top_line) * line_height).floor();
let bottom_f32 = (safe_casts::usize_to_f32_or_max(bottom_line) * line_height).ceil();
// Safe conversions with overflow checks
let top = if top_f32 > i32::MAX as f32 {
i32::MAX
} else if top_f32 < i32::MIN as f32 {
i32::MIN
} else {
top_f32 as i32
};
let bottom = if bottom_f32 > i32::MAX as f32 {
i32::MAX
} else if bottom_f32 < i32::MIN as f32 {
i32::MIN
} else {
bottom_f32 as i32
};
let top = safe_casts::f32_to_i32_or_bound(top_f32);
let bottom = safe_casts::f32_to_i32_or_bound(bottom_f32);
// Ensure viewport width doesn't exceed i32::MAX
let right = viewport_width.min(i32::MAX as u32) as i32;
let right = safe_casts::u32_to_i32_or_max(viewport_width.min(i32::MAX as u32));
TextBounds {
left: 0,
@@ -267,7 +263,7 @@ impl TerminalText {
}
fn get_visible_line_range(&self) -> (usize, usize) {
let viewport_height = self.viewport.resolution().height as f32;
let viewport_height = safe_casts::u32_to_f32_or_max(self.viewport.resolution().height);
let line_height = self.buffer.metrics().line_height;
// Start from line 0 (no scrolling yet)
@@ -282,7 +278,9 @@ impl TerminalText {
last_logical_line = run.line_i;
// If we've exceeded the viewport height, we can stop counting
if ((start_line + visual_line_count) as f32 * line_height) > viewport_height {
if (safe_casts::usize_to_f32_or_max(start_line + visual_line_count) * line_height)
> viewport_height
{
break;
}
}

View File

@@ -0,0 +1,57 @@
use log::trace;
// Safe conversions with overflow checks
pub fn u32_to_i32_or_max(n: u32) -> i32 {
if n > i32::MAX as u32 {
trace!(
"Overflow casting {n}::u32 as i32, defaulting to {}",
i32::MAX
);
i32::MAX
} else {
n as i32
}
}
pub fn u32_to_f32_or_max(n: u32) -> f32 {
if n > f32::MAX as u32 {
trace!(
"Overflow casting {n}::u32 as f32, defaulting to {}",
f32::MAX
);
f32::MAX
} else {
n as f32
}
}
pub fn usize_to_f32_or_max(n: usize) -> f32 {
if n > f32::MAX as usize {
trace!(
"Overflow casting {n}::usize as f32, defaulting to {}",
f32::MAX
);
f32::MAX
} else {
n as f32
}
}
pub fn f32_to_i32_or_bound(n: f32) -> i32 {
if n > i32::MAX as f32 {
trace!(
"Overflow casting {n}::f32 as i32, defaulting to {}",
i32::MAX
);
i32::MAX
} else if n < i32::MIN as f32 {
trace!(
"Underflow casting {n}::f32 as i32, defaulting to {}",
i32::MIN
);
i32::MIN
} else {
n as i32
}
}