Compare commits

...

1 Commits

Author SHA1 Message Date
spinline
1bb3475d61 perf: optimize torrent store with HashMap for O(1) updates
All checks were successful
Build MIPS Binary / build (push) Successful in 4m57s
2026-02-08 23:52:23 +03:00
3 changed files with 47 additions and 46 deletions

View File

@@ -6,52 +6,47 @@ use crate::api;
pub fn Sidebar() -> impl IntoView { pub fn Sidebar() -> impl IntoView {
let store = use_context::<crate::store::TorrentStore>().expect("store not provided"); let store = use_context::<crate::store::TorrentStore>().expect("store not provided");
let total_count = move || store.torrents.get().len(); let total_count = move || store.torrents.with(|map| map.len());
let downloading_count = move || { let downloading_count = move || {
store store.torrents.with(|map| {
.torrents map.values()
.get()
.iter()
.filter(|t| t.status == shared::TorrentStatus::Downloading) .filter(|t| t.status == shared::TorrentStatus::Downloading)
.count() .count()
})
}; };
let seeding_count = move || { let seeding_count = move || {
store store.torrents.with(|map| {
.torrents map.values()
.get()
.iter()
.filter(|t| t.status == shared::TorrentStatus::Seeding) .filter(|t| t.status == shared::TorrentStatus::Seeding)
.count() .count()
})
}; };
let completed_count = move || { let completed_count = move || {
store store.torrents.with(|map| {
.torrents map.values()
.get()
.iter()
.filter(|t| { .filter(|t| {
t.status == shared::TorrentStatus::Seeding t.status == shared::TorrentStatus::Seeding
|| (t.status == shared::TorrentStatus::Paused && t.percent_complete >= 100.0) || (t.status == shared::TorrentStatus::Paused && t.percent_complete >= 100.0)
}) })
.count() .count()
})
}; };
let paused_count = move || { let paused_count = move || {
store store.torrents.with(|map| {
.torrents map.values()
.get()
.iter()
.filter(|t| t.status == shared::TorrentStatus::Paused) .filter(|t| t.status == shared::TorrentStatus::Paused)
.count() .count()
})
}; };
let inactive_count = move || { let inactive_count = move || {
store store.torrents.with(|map| {
.torrents map.values()
.get()
.iter()
.filter(|t| { .filter(|t| {
t.status == shared::TorrentStatus::Paused t.status == shared::TorrentStatus::Paused
|| t.status == shared::TorrentStatus::Error || t.status == shared::TorrentStatus::Error
}) })
.count() .count()
})
}; };
let close_drawer = move || { let close_drawer = move || {

View File

@@ -82,9 +82,10 @@ pub fn TorrentTable() -> impl IntoView {
let sort_dir = create_rw_signal(SortDirection::Descending); let sort_dir = create_rw_signal(SortDirection::Descending);
let filtered_torrents = move || { let filtered_torrents = move || {
let mut torrents = store // Convert HashMap values to Vec for filtering and sorting
.torrents let torrents: Vec<shared::Torrent> = store.torrents.with(|map| map.values().cloned().collect());
.get()
let mut torrents = torrents
.into_iter() .into_iter()
.filter(|t| { .filter(|t| {
let filter = store.filter.get(); let filter = store.filter.get();

View File

@@ -113,9 +113,11 @@ impl FilterStatus {
} }
} }
use std::collections::HashMap;
#[derive(Clone, Copy, Debug)] #[derive(Clone, Copy, Debug)]
pub struct TorrentStore { pub struct TorrentStore {
pub torrents: RwSignal<Vec<Torrent>>, pub torrents: RwSignal<HashMap<String, Torrent>>,
pub filter: RwSignal<FilterStatus>, pub filter: RwSignal<FilterStatus>,
pub search_query: RwSignal<String>, pub search_query: RwSignal<String>,
pub global_stats: RwSignal<GlobalStats>, pub global_stats: RwSignal<GlobalStats>,
@@ -124,7 +126,7 @@ pub struct TorrentStore {
} }
pub fn provide_torrent_store() { pub fn provide_torrent_store() {
let torrents = create_rw_signal(Vec::<Torrent>::new()); let torrents = create_rw_signal(HashMap::new());
let filter = create_rw_signal(FilterStatus::All); let filter = create_rw_signal(FilterStatus::All);
let search_query = create_rw_signal(String::new()); let search_query = create_rw_signal(String::new());
let global_stats = create_rw_signal(GlobalStats::default()); let global_stats = create_rw_signal(GlobalStats::default());
@@ -193,12 +195,15 @@ pub fn provide_torrent_store() {
if let Ok(event) = serde_json::from_str::<AppEvent>(&data_str) { if let Ok(event) = serde_json::from_str::<AppEvent>(&data_str) {
match event { match event {
AppEvent::FullList { torrents: list, .. } => { AppEvent::FullList { torrents: list, .. } => {
torrents.set(list); let map: HashMap<String, Torrent> = list
.into_iter()
.map(|t| (t.hash.clone(), t))
.collect();
torrents.set(map);
} }
AppEvent::Update(update) => { AppEvent::Update(update) => {
torrents.update(|list| { torrents.update(|map| {
if let Some(t) = list.iter_mut().find(|t| t.hash == update.hash) if let Some(t) = map.get_mut(&update.hash) {
{
if let Some(name) = update.name { if let Some(name) = update.name {
t.name = name; t.name = name;
} }