Merge pull request #1298 from Cheong-Lau/rustc-hash

perf: use `rustc-hash` for `HashMap` and `HashSet`
This commit is contained in:
Jeremy Soller 2025-10-28 15:07:48 -06:00 committed by GitHub
commit 5863671217
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
11 changed files with 68 additions and 68 deletions

1
Cargo.lock generated
View file

@ -1496,6 +1496,7 @@ dependencies = [
"recently-used-xbel",
"regex",
"rust-embed",
"rustc-hash 2.1.1",
"serde",
"shlex",
"slotmap",

View file

@ -29,6 +29,7 @@ notify-rust = { version = "4", optional = true }
open = "5.3.2"
paste = "1.0"
regex = "1"
rustc-hash = "2.1"
serde = { version = "1", features = ["serde_derive"] }
shlex = { version = "1.3" }
tempfile = "3"

View file

@ -48,10 +48,11 @@ use notify_debouncer_full::{
DebouncedEvent, Debouncer, RecommendedCache, new_debouncer,
notify::{self, RecommendedWatcher},
};
use rustc_hash::{FxHashMap, FxHashSet};
use slotmap::Key as SlotMapKey;
use std::{
any::TypeId,
collections::{BTreeMap, BTreeSet, HashMap, HashSet, VecDeque},
collections::{BTreeMap, BTreeSet, HashMap, VecDeque},
env, fmt, fs,
future::Future,
io,
@ -68,6 +69,7 @@ use trash::TrashItem;
use wayland_client::{Proxy, protocol::wl_output::WlOutput};
use crate::{
FxOrderMap,
clipboard::{ClipboardCopy, ClipboardKind, ClipboardPaste},
config::{
AppTheme, Config, DesktopConfig, Favorite, IconSizes, TIME_CONFIG_ID, TabConfig,
@ -676,17 +678,17 @@ pub struct App {
dialog_pages: DialogPages,
dialog_text_input: widget::Id,
key_binds: HashMap<KeyBind, Action>,
margin: HashMap<window::Id, (f32, f32, f32, f32)>,
margin: FxHashMap<window::Id, (f32, f32, f32, f32)>,
mime_app_cache: MimeAppCache,
modifiers: Modifiers,
mounter_items: HashMap<MounterKey, MounterItems>,
mounter_items: FxHashMap<MounterKey, MounterItems>,
must_save_sort_names: bool,
network_drive_connecting: Option<(MounterKey, String)>,
network_drive_input: String,
#[cfg(feature = "notify")]
notification_opt: Option<Arc<Mutex<notify_rust::NotificationHandle>>>,
#[cfg(all(feature = "wayland", feature = "desktop-applet"))]
overlap: HashMap<String, (window::Id, Rectangle)>,
overlap: FxHashMap<String, (window::Id, Rectangle)>,
pending_operation_id: u64,
pending_operations: BTreeMap<u64, (Operation, Controller)>,
progress_operations: BTreeSet<u64>,
@ -696,17 +698,17 @@ pub struct App {
search_id: widget::Id,
size: Option<Size>,
#[cfg(all(feature = "wayland", feature = "desktop-applet"))]
layer_sizes: HashMap<window::Id, Size>,
layer_sizes: FxHashMap<window::Id, Size>,
#[cfg(all(feature = "wayland", feature = "desktop-applet"))]
surface_ids: HashMap<WlOutput, WindowId>,
surface_ids: FxHashMap<WlOutput, WindowId>,
#[cfg(all(feature = "wayland", feature = "desktop-applet"))]
surface_names: HashMap<WindowId, String>,
surface_names: FxHashMap<WindowId, String>,
toasts: widget::toaster::Toasts<Message>,
watcher_opt: Option<(
Debouncer<RecommendedWatcher, RecommendedCache>,
HashSet<PathBuf>,
FxHashSet<PathBuf>,
)>,
windows: HashMap<window::Id, WindowKind>,
windows: FxHashMap<window::Id, WindowKind>,
nav_dnd_hover: Option<(Location, Instant)>,
tab_dnd_hover: Option<(Entity, Instant)>,
nav_drag_id: DragId,
@ -731,7 +733,7 @@ impl App {
// Associate all paths to its MIME type
// This allows handling paths as groups if possible, such as launching a single video
// player that is passed every path.
let mut groups: HashMap<Mime, Vec<PathBuf>> = HashMap::new();
let mut groups: FxHashMap<Mime, Vec<PathBuf>> = FxHashMap::default();
let mut all_archives = true;
let supported_archive_types = crate::archive::SUPPORTED_ARCHIVE_TYPES
.iter()
@ -948,7 +950,7 @@ impl App {
#[cfg(all(feature = "wayland", feature = "desktop-applet"))]
fn handle_overlap(&mut self) {
let mut overlaps: HashMap<_, _> = self
let mut overlaps: FxHashMap<_, _> = self
.windows
.keys()
.map(|k| (*k, (0., 0., 0., 0.)))
@ -1609,7 +1611,7 @@ impl App {
fn update_watcher(&mut self) -> Task<Message> {
if let Some((mut watcher, old_paths)) = self.watcher_opt.take() {
let mut new_paths = HashSet::new();
let mut new_paths = FxHashSet::default();
for entity in self.tab_model.iter() {
if let Some(tab) = self.tab_model.data::<Tab>(entity) {
if let Some(path) = tab.location.path_opt() {
@ -1978,7 +1980,7 @@ impl App {
fn get_apps_for_mime(&self, mime_type: &Mime) -> Vec<(&MimeApp, MimeAppMatch)> {
let mut results = Vec::new();
let mut dedupe = HashSet::new();
let mut dedupe = FxHashSet::default();
// start with exact matches
for mime_app in self.mime_app_cache.get(mime_type) {
@ -2160,17 +2162,17 @@ impl Application for App {
dialog_pages: DialogPages::new(),
dialog_text_input: widget::Id::new("Dialog Text Input"),
key_binds,
margin: HashMap::new(),
margin: FxHashMap::default(),
mime_app_cache: MimeAppCache::new(),
modifiers: Modifiers::empty(),
mounter_items: HashMap::new(),
mounter_items: FxHashMap::default(),
must_save_sort_names: false,
network_drive_connecting: None,
network_drive_input: String::new(),
#[cfg(feature = "notify")]
notification_opt: None,
#[cfg(all(feature = "wayland", feature = "desktop-applet"))]
overlap: HashMap::new(),
overlap: FxHashMap::default(),
pending_operation_id: 0,
pending_operations: BTreeMap::new(),
progress_operations: BTreeSet::new(),
@ -2180,12 +2182,12 @@ impl Application for App {
search_id: widget::Id::new("File Search"),
size: None,
#[cfg(all(feature = "wayland", feature = "desktop-applet"))]
surface_ids: HashMap::new(),
surface_ids: FxHashMap::default(),
#[cfg(all(feature = "wayland", feature = "desktop-applet"))]
surface_names: HashMap::new(),
surface_names: FxHashMap::default(),
toasts: widget::toaster::Toasts::new(Message::CloseToast),
watcher_opt: None,
windows: HashMap::new(),
windows: FxHashMap::default(),
nav_dnd_hover: None,
tab_dnd_hover: None,
nav_drag_id: DragId::new(),
@ -2193,7 +2195,7 @@ impl Application for App {
auto_scroll_speed: None,
file_dialog_opt: None,
#[cfg(all(feature = "wayland", feature = "desktop-applet"))]
layer_sizes: HashMap::new(),
layer_sizes: FxHashMap::default(),
};
let mut commands = vec![app.update_config()];
@ -3264,7 +3266,7 @@ impl Application for App {
Message::NotifyWatcher(mut watcher_wrapper) => match watcher_wrapper.watcher_opt.take()
{
Some(watcher) => {
self.watcher_opt = Some((watcher, HashSet::new()));
self.watcher_opt = Some((watcher, FxHashSet::default()));
return self.update_watcher();
}
None => {
@ -4744,8 +4746,8 @@ impl Application for App {
Message::SaveSortNames => {
self.must_save_sort_names = false;
if let Some(state_handler) = self.state_handler.as_ref() {
if let Err(err) =
state_handler.set::<ordermap::OrderMap<String, (HeadingOptions, bool)>>(
if let Err(err) = state_handler
.set::<FxOrderMap<String, (HeadingOptions, bool)>>(
"sort_names",
self.state.sort_names.clone(),
)

View file

@ -8,10 +8,10 @@ use cosmic::{
iced::Subscription,
theme,
};
use ordermap::OrderMap;
use serde::{Deserialize, Serialize};
use crate::{
FxOrderMap,
app::App,
tab::{HeadingOptions, Location, View},
};
@ -115,13 +115,13 @@ pub enum TypeToSearch {
#[derive(Clone, CosmicConfigEntry, Debug, Deserialize, Eq, PartialEq, Serialize)]
#[serde(default)]
pub struct State {
pub sort_names: ordermap::OrderMap<String, (HeadingOptions, bool)>,
pub sort_names: FxOrderMap<String, (HeadingOptions, bool)>,
}
impl Default for State {
fn default() -> Self {
Self {
sort_names: OrderMap::from_iter(dirs::download_dir().into_iter().map(|dir| {
sort_names: FxOrderMap::from_iter(dirs::download_dir().into_iter().map(|dir| {
(
Location::Path(dir).normalize().to_string(),
(HeadingOptions::Modified, false),

View file

@ -26,9 +26,10 @@ use notify_debouncer_full::{
notify::{self, RecommendedWatcher},
};
use recently_used_xbel::update_recently_used;
use rustc_hash::{FxHashMap, FxHashSet};
use std::{
any::TypeId,
collections::{HashMap, HashSet, VecDeque},
collections::{HashMap, VecDeque},
env, fmt, fs,
num::NonZeroU16,
path::PathBuf,
@ -513,7 +514,7 @@ struct App {
filter_selected: Option<usize>,
filename_id: widget::Id,
modifiers: Modifiers,
mounter_items: HashMap<MounterKey, MounterItems>,
mounter_items: FxHashMap<MounterKey, MounterItems>,
nav_model: segmented_button::SingleSelectModel,
result_opt: Option<DialogResult>,
search_id: widget::Id,
@ -521,7 +522,7 @@ struct App {
key_binds: HashMap<KeyBind, Action>,
watcher_opt: Option<(
Debouncer<RecommendedWatcher, RecommendedCache>,
HashSet<PathBuf>,
FxHashSet<PathBuf>,
)>,
auto_scroll_speed: Option<i16>,
}
@ -875,7 +876,7 @@ impl App {
fn update_watcher(&mut self) -> Task<Message> {
if let Some((mut watcher, old_paths)) = self.watcher_opt.take() {
let mut new_paths = HashSet::new();
let mut new_paths = FxHashSet::default();
if let Some(path) = &self.tab.location.path_opt() {
new_paths.insert(path.to_path_buf());
}
@ -985,7 +986,7 @@ impl Application for App {
filter_selected: None,
filename_id: widget::Id::new("Dialog Filename"),
modifiers: Modifiers::empty(),
mounter_items: HashMap::new(),
mounter_items: FxHashMap::default(),
nav_model: segmented_button::ModelBuilder::default().build(),
result_opt: None,
search_id: widget::Id::new("Dialog File Search"),
@ -1532,7 +1533,7 @@ impl Application for App {
Message::NotifyWatcher(mut watcher_wrapper) => match watcher_wrapper.watcher_opt.take()
{
Some(watcher) => {
self.watcher_opt = Some((watcher, HashSet::new()));
self.watcher_opt = Some((watcher, FxHashSet::default()));
return self.update_watcher();
}
None => {

View file

@ -27,6 +27,8 @@ pub mod tab;
mod thumbnail_cacher;
mod thumbnailer;
pub(crate) type FxOrderMap<K, V> = ordermap::OrderMap<K, V, rustc_hash::FxBuildHasher>;
pub(crate) fn err_str<T: ToString>(err: T) -> String {
err.to_string()
}

View file

@ -5,9 +5,9 @@
use cosmic::desktop;
use cosmic::widget;
pub use mime_guess::Mime;
use rustc_hash::FxHashMap;
use std::{
cmp::Ordering,
collections::HashMap,
env,
ffi::OsStr,
fs, io,
@ -221,8 +221,8 @@ fn filename_eq(path_opt: &Option<PathBuf>, filename: &str) -> bool {
pub struct MimeAppCache {
apps: Vec<MimeApp>,
cache: HashMap<Mime, Vec<MimeApp>>,
icons: HashMap<Mime, Vec<widget::icon::Handle>>,
cache: FxHashMap<Mime, Vec<MimeApp>>,
icons: FxHashMap<Mime, Vec<widget::icon::Handle>>,
terminals: Vec<MimeApp>,
}
@ -230,8 +230,8 @@ impl MimeAppCache {
pub fn new() -> Self {
let mut mime_app_cache = Self {
apps: Vec::new(),
cache: HashMap::new(),
icons: HashMap::new(),
cache: FxHashMap::default(),
icons: FxHashMap::default(),
terminals: Vec::new(),
};
mime_app_cache.reload();

View file

@ -2,8 +2,8 @@
use cosmic::widget::icon;
use mime_guess::Mime;
use rustc_hash::FxHashMap;
use std::{
collections::HashMap,
fs,
path::Path,
sync::{LazyLock, Mutex},
@ -18,14 +18,14 @@ struct MimeIconKey {
}
struct MimeIconCache {
cache: HashMap<MimeIconKey, Option<icon::Handle>>,
cache: FxHashMap<MimeIconKey, Option<icon::Handle>>,
shared_mime_info: xdg_mime::SharedMimeInfo,
}
impl MimeIconCache {
pub fn new() -> Self {
Self {
cache: HashMap::new(),
cache: FxHashMap::default(),
shared_mime_info: xdg_mime::SharedMimeInfo::new(),
}
}

View file

@ -49,7 +49,7 @@ use icu::{
use image::ImageDecoder;
use jxl_oxide::integration::JxlDecoder;
use mime_guess::{Mime, mime};
use ordermap::OrderMap;
use rustc_hash::FxHashMap;
use serde::{Deserialize, Serialize};
use std::{
borrow::Cow,
@ -73,6 +73,7 @@ use trash::TrashItemSize;
use walkdir::WalkDir;
use crate::{
FxOrderMap,
app::{Action, PreviewItem, PreviewKind},
clipboard::{ClipboardCopy, ClipboardKind, ClipboardPaste},
config::{DesktopConfig, ICON_SCALE_MAX, ICON_SIZE_GRID, IconSizes, TabConfig, ThumbCfg},
@ -100,9 +101,9 @@ const THUMBNAIL_SIZE: u32 = (ICON_SIZE_GRID as u32) * (ICON_SCALE_MAX as u32);
pub static THUMB_SEMAPHORE: LazyLock<tokio::sync::Semaphore> =
LazyLock::new(|| tokio::sync::Semaphore::const_new(num_cpus::get()));
pub(crate) static SORT_OPTION_FALLBACK: LazyLock<HashMap<String, (HeadingOptions, bool)>> =
pub(crate) static SORT_OPTION_FALLBACK: LazyLock<FxHashMap<String, (HeadingOptions, bool)>> =
LazyLock::new(|| {
HashMap::from_iter(dirs::download_dir().into_iter().map(|dir| {
FxHashMap::from_iter(dirs::download_dir().into_iter().map(|dir| {
(
Location::Path(dir).normalize().to_string(),
(HeadingOptions::Modified, false),
@ -131,8 +132,8 @@ static MODE_NAMES: LazyLock<Vec<String>> = LazyLock::new(|| {
]
});
static SPECIAL_DIRS: LazyLock<HashMap<PathBuf, &'static str>> = LazyLock::new(|| {
let mut special_dirs = HashMap::new();
static SPECIAL_DIRS: LazyLock<FxHashMap<PathBuf, &'static str>> = LazyLock::new(|| {
let mut special_dirs = FxHashMap::default();
if let Some(dir) = dirs::document_dir() {
special_dirs.insert(dir, "folder-documents");
}
@ -534,25 +535,17 @@ pub enum FsKind {
pub fn fs_kind(metadata: &Metadata) -> FsKind {
//TODO: method to reload remote filesystems dynamically
//TODO: fix for https://github.com/eminence/procfs/issues/262
static DEVICES: LazyLock<HashMap<u64, FsKind>> = LazyLock::new(|| {
let mut devices = HashMap::new();
static DEVICES: LazyLock<FxHashMap<u64, FsKind>> = LazyLock::new(|| {
let mut devices = FxHashMap::default();
match procfs::process::Process::myself() {
Ok(process) => match process.mountinfo() {
Ok(mount_infos) => {
for mount_info in mount_infos.iter() {
devices = FxHashMap::from_iter(mount_infos.iter().filter_map(|mount_info| {
let mut parts = mount_info.majmin.split(':');
let Some(major_str) = parts.next() else {
continue;
};
let Some(minor_str) = parts.next() else {
continue;
};
let Ok(major) = major_str.parse::<libc::c_uint>() else {
continue;
};
let Ok(minor) = minor_str.parse::<libc::c_uint>() else {
continue;
};
let major_str = parts.next()?;
let minor_str = parts.next()?;
let major = major_str.parse::<libc::c_uint>().ok()?;
let minor = minor_str.parse::<libc::c_uint>().ok()?;
let dev = libc::makedev(major, minor);
//TODO: make sure this list is exhaustive
let kind = match mount_info.fs_type.as_str() {
@ -561,8 +554,8 @@ pub fn fs_kind(metadata: &Metadata) -> FsKind {
"fuse.gvfsd-fuse" => FsKind::Gvfs,
_ => FsKind::Local,
};
devices.insert(dev, kind);
}
Some((dev, kind))
}));
}
Err(err) => {
log::warn!("failed to get mount info: {err}");
@ -2524,7 +2517,7 @@ impl Tab {
location: Location,
config: TabConfig,
thumb_config: ThumbCfg,
sorting_options: Option<&OrderMap<String, (HeadingOptions, bool)>>,
sorting_options: Option<&FxOrderMap<String, (HeadingOptions, bool)>>,
scrollable_id: widget::Id,
window_id: Option<window::Id>,
) -> Self {

View file

@ -1,7 +1,7 @@
use image::DynamicImage;
use md5::{Digest, Md5};
use rustc_hash::FxHashMap;
use std::{
collections::HashMap,
error::Error,
fs::{self, File},
io::{self, BufReader, BufWriter},
@ -143,7 +143,7 @@ impl ThumbnailCacher {
let mut reader = decoder.read_info()?;
let (width, height, color_type, bit_depth, mut text_chunks) = {
let info = reader.info();
let text_chunks: HashMap<String, String> = info
let text_chunks: FxHashMap<String, String> = info
.uncompressed_latin1_text
.clone()
.into_iter()

View file

@ -2,8 +2,8 @@
// SPDX-License-Identifier: GPL-3.0-only
use mime_guess::Mime;
use rustc_hash::FxHashMap;
use std::{
collections::HashMap,
fs,
path::Path,
process,
@ -56,13 +56,13 @@ impl Thumbnailer {
}
pub struct ThumbnailerCache {
cache: HashMap<Mime, Vec<Thumbnailer>>,
cache: FxHashMap<Mime, Vec<Thumbnailer>>,
}
impl ThumbnailerCache {
pub fn new() -> Self {
let mut thumbnailer_cache = Self {
cache: HashMap::new(),
cache: FxHashMap::default(),
};
thumbnailer_cache.reload();
thumbnailer_cache