diff --git a/src/ui/components/monitor_list.rs b/src/ui/components/monitor_list.rs index af74637..34ee859 100644 --- a/src/ui/components/monitor_list.rs +++ b/src/ui/components/monitor_list.rs @@ -1,7 +1,10 @@ -use std::borrow::Cow; -use std::cmp::min; -use std::collections::HashMap; -use std::sync::OnceLock; +use std::{ + borrow::Cow, + cmp::min, + collections::{HashMap, hash_map::DefaultHasher}, + hash::{Hash, Hasher}, + sync::{OnceLock, RwLock}, +}; use crate::i18n::t; use crate::ui::dashboard::{ @@ -19,6 +22,7 @@ use ratatui::{ const STATUS_LINE_LENGTH: usize = 100; const MAX_NAME_LENGTH: usize = 30; static STATUS_SPANS: OnceLock>> = OnceLock::new(); +static STATUS_LINE_CACHE: OnceLock>>>> = OnceLock::new(); pub fn render_monitor_list(main_frame: &mut Frame, area: Rect, state: &mut DashboardViewState) { let available_height = area.height as usize; @@ -185,7 +189,7 @@ fn create_monitor_item(monitor: &MonitorViewState) -> Row<'_> { let response_text = format!("{:>7}ms", monitor.response_time); let uptime_text = format!("{:>7}%", monitor.uptime_24h); - let status_line_spans = create_status_line_spans(&monitor.status_history); + let status_line_spans = get_cached_status_line(&monitor.status_history); Row::new(vec![ get_formated_line(format!("{} ", status_icon), status_color, Modifier::empty()), @@ -225,18 +229,50 @@ fn get_cached_status_span(status: &MonitorStatus) -> Span<'static> { cache.get(status).cloned().unwrap_or_default() } -fn create_status_line_spans(status_history: &[MonitorStatus]) -> Line<'_> { - let spans: Vec<_> = status_history - .iter() - .rev() - .take(STATUS_LINE_LENGTH) - .map(|status| get_cached_status_span(status)) - .collect(); - Line::from(spans) -} - fn title_style() -> Style { Style::default() .fg(Color::Yellow) .add_modifier(Modifier::BOLD) } + +fn calculate_history_hash(status_history: &[MonitorStatus]) -> u64 { + let mut hasher = DefaultHasher::new(); + status_history + .iter() + .take(STATUS_LINE_LENGTH) + .for_each(|status| { + status.hash(&mut hasher); + }); + hasher.finish() +} + +fn get_cached_status_line(status_history: &[MonitorStatus]) -> Line<'static> { + let hash = calculate_history_hash(status_history); + let cache = STATUS_LINE_CACHE.get_or_init(|| RwLock::new(HashMap::new())); + { + let read = cache.read().unwrap(); + if let Some(spans) = read.get(&hash) { + return Line::from(spans.clone()); + } + } + + let spans: Vec> = status_history + .iter() + .rev() + .take(STATUS_LINE_LENGTH) + .map(|status| get_cached_status_span(status)) + .collect(); + + let mut write = cache.write().unwrap(); + if write.len() > 1000 { + + let keys_to_remove: Vec<_> = write.keys().take(250).copied().collect(); + + for key in keys_to_remove { + write.remove(&key); + } + + } + write.insert(hash, spans.clone()); + Line::from(spans) +}