From 3d22177de6155e36ea42d73ac4401e7cb314b312 Mon Sep 17 00:00:00 2001 From: Clement Tsang <34804052+ClementTsang@users.noreply.github.com> Date: Wed, 1 Oct 2025 03:55:23 -0400 Subject: [PATCH] bug: check subtraction when calculating network widget y-axis (#1827) This PR adds an explicit check for the case where the `Instant` is somehow too small and falls back to manually checking for the oldest visible timestamp. --- src/canvas/widgets/network_graph.rs | 70 ++++++++++++++++++----------- 1 file changed, 44 insertions(+), 26 deletions(-) diff --git a/src/canvas/widgets/network_graph.rs b/src/canvas/widgets/network_graph.rs index 77d36900..523c978a 100644 --- a/src/canvas/widgets/network_graph.rs +++ b/src/canvas/widgets/network_graph.rs @@ -19,7 +19,7 @@ use crate::{ data_units::*, general::{saturating_log2, saturating_log10}, }, - widgets::NetWidgetHeightCache, + widgets::{NetWidgetHeightCache, NetWidgetState}, }; impl Painter { @@ -79,34 +79,13 @@ impl Painter { let y_max = { if let Some(last_time) = time.last() { - // For now, just do it each time. Might want to cache this later though. - - let (mut biggest, mut biggest_time, first_time) = { - let initial_first_time = *last_time - - Duration::from_millis(network_widget_state.current_display_time); - - match &network_widget_state.height_cache { - Some(NetWidgetHeightCache { - best_point, - right_edge, - period, - }) => { - if *period != network_widget_state.current_display_time - || best_point.0 < initial_first_time - { - (0.0, initial_first_time, initial_first_time) - } else { - (best_point.1, best_point.0, *right_edge) - } - } - None => (0.0, initial_first_time, initial_first_time), - } - }; + let (mut biggest, mut biggest_time, oldest_to_check) = + check_network_height_cache(network_widget_state, last_time, time); for (&time, &v) in rx_points .iter_along_base(time) .rev() - .take_while(|&(&time, _)| time >= first_time) + .take_while(|&(&time, _)| time >= oldest_to_check) { if v > biggest { biggest = v; @@ -117,7 +96,7 @@ impl Painter { for (&time, &v) in tx_points .iter_along_base(time) .rev() - .take_while(|&(&time, _)| time >= first_time) + .take_while(|&(&time, _)| time >= oldest_to_check) { if v > biggest { biggest = v; @@ -299,6 +278,45 @@ impl Painter { } } +#[inline] +fn check_network_height_cache( + network_widget_state: &NetWidgetState, last_time: &std::time::Instant, + time: &[std::time::Instant], +) -> (f64, std::time::Instant, std::time::Instant) { + let visible_duration = Duration::from_millis(network_widget_state.current_display_time); + + if let Some(NetWidgetHeightCache { + best_point, + right_edge, + period, + }) = &network_widget_state.height_cache + { + if *period == network_widget_state.current_display_time + && last_time.duration_since(*right_edge) < visible_duration + { + return (best_point.1, best_point.0, *right_edge); + } + } + + let visible_left_bound = match last_time.checked_sub(visible_duration) { + Some(v) => v, + None => { + // On some systems (like Windows) it can be possible that the current display time + // causes subtraction to fail if, for example, the uptime of the system is too low + // and current_display_time is too high. See https://github.com/ClementTsang/bottom/issues/1825. + // + // As such, instead take the oldest visible time. + time.iter() + .take_while(|t| last_time.duration_since(**t) < visible_duration) + .last() + .cloned() + .unwrap_or(*last_time) + } + }; + + (0.0, visible_left_bound, visible_left_bound) +} + /// Returns the required labels. /// /// TODO: This is _really_ ugly... also there might be a bug with certain heights and too many labels.