perf(hash): swap std HashMap/HashSet for rustc-hash FxHash

Phase 4.1. Handle / u64 keys dominate the hot maps (block_defn,
hatch / image / mesh caches, draw-depth map, viewport wire caches);
the default SipHash is wasted work on integer keys. Alias the std
types to rustc_hash::FxHashMap / FxHashSet crate-wide so every map and
set — including the cross-module draw-depth map shared between scene
and the GPU pipelines — uses the faster hasher consistently.

Mechanical: no behavioural change. Constructors moved from ::new() to
::default() (Fx variants carry no RandomState). Build + 13 tests green.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Hakan Seven 2026-06-05 00:35:53 +03:00
commit 70702d063f
30 changed files with 102 additions and 98 deletions

1
Cargo.lock generated
View file

@ -28,6 +28,7 @@ dependencies = [
"printpdf", "printpdf",
"rayon", "rayon",
"rfd", "rfd",
"rustc-hash 2.1.2",
"truck-meshalgo", "truck-meshalgo",
"truck-modeling", "truck-modeling",
"truck-polymesh", "truck-polymesh",

View file

@ -28,6 +28,9 @@ rayon = "1"
ureq = { version = "3", default-features = false, features = ["rustls"] } ureq = { version = "3", default-features = false, features = ["rustls"] }
inventory = "0.3" inventory = "0.3"
truck-shapeops = "0.4" truck-shapeops = "0.4"
# Fast non-cryptographic hasher. Handle/u64 keys dominate the hot block,
# hatch, draw-depth and wire caches; the default SipHash is overkill there.
rustc-hash = "2"
[target.'cfg(target_os = "windows")'.dependencies] [target.'cfg(target_os = "windows")'.dependencies]
windows-sys = { version = "0.61", features = ["Win32_UI_Shell", "Win32_UI_WindowsAndMessaging"] } windows-sys = { version = "0.61", features = ["Win32_UI_Shell", "Win32_UI_WindowsAndMessaging"] }

View file

@ -203,7 +203,7 @@ matters.
## Phase 4 — Allocation & Memory ## Phase 4 — Allocation & Memory
### 4.1 Swap `HashMap` for `rustc-hash::FxHashMap` ### 4.1 Swap `HashMap` for `rustc-hash::FxHashMap` ✅ DONE
`Handle` is an integer wrapper; the default `SipHash` is overkill. `Handle` is an integer wrapper; the default `SipHash` is overkill.
`FxHashMap` gives 20-40 % in hash-heavy sites (block_cache, hatches / `FxHashMap` gives 20-40 % in hash-heavy sites (block_cache, hatches /

View file

@ -118,7 +118,7 @@ impl OpenCADStudio {
self.command_line.push_info(&cmd.prompt()); self.command_line.push_info(&cmd.prompt());
self.tabs[i].active_cmd = Some(Box::new(cmd)); self.tabs[i].active_cmd = Some(Box::new(cmd));
} else { } else {
let layers: std::collections::HashSet<String> = self.tabs[i] let layers: rustc_hash::FxHashSet<String> = self.tabs[i]
.scene .scene
.selected_entities() .selected_entities()
.into_iter() .into_iter()
@ -153,7 +153,7 @@ impl OpenCADStudio {
self.command_line.push_info(&cmd.prompt()); self.command_line.push_info(&cmd.prompt());
self.tabs[i].active_cmd = Some(Box::new(cmd)); self.tabs[i].active_cmd = Some(Box::new(cmd));
} else { } else {
let layers: std::collections::HashSet<String> = self.tabs[i] let layers: rustc_hash::FxHashSet<String> = self.tabs[i]
.scene .scene
.selected_entities() .selected_entities()
.into_iter() .into_iter()
@ -188,7 +188,7 @@ impl OpenCADStudio {
self.command_line.push_info(&cmd.prompt()); self.command_line.push_info(&cmd.prompt());
self.tabs[i].active_cmd = Some(Box::new(cmd)); self.tabs[i].active_cmd = Some(Box::new(cmd));
} else { } else {
let layers: std::collections::HashSet<String> = self.tabs[i] let layers: rustc_hash::FxHashSet<String> = self.tabs[i]
.scene .scene
.selected_entities() .selected_entities()
.into_iter() .into_iter()
@ -278,7 +278,7 @@ impl OpenCADStudio {
self.command_line.push_info(&cmd.prompt()); self.command_line.push_info(&cmd.prompt());
self.tabs[i].active_cmd = Some(Box::new(cmd)); self.tabs[i].active_cmd = Some(Box::new(cmd));
} else { } else {
let layers: std::collections::HashSet<String> = self.tabs[i] let layers: rustc_hash::FxHashSet<String> = self.tabs[i]
.scene .scene
.selected_entities() .selected_entities()
.into_iter() .into_iter()
@ -299,7 +299,7 @@ impl OpenCADStudio {
// LAYISO — turn off all layers except those used by selected entities // LAYISO — turn off all layers except those used by selected entities
"LAYISO" => { "LAYISO" => {
let sel_layers: std::collections::HashSet<String> = self.tabs[i] let sel_layers: rustc_hash::FxHashSet<String> = self.tabs[i]
.scene .scene
.selected_entities() .selected_entities()
.into_iter() .into_iter()
@ -3090,10 +3090,10 @@ impl OpenCADStudio {
// including ones not yet in the table, which sort by // including ones not yet in the table, which sort by
// their own handle. (min_eff, max_eff) over siblings. // their own handle. (min_eff, max_eff) over siblings.
let fb_baseline: Option<(u64, u64)> = if to_front_opt.is_some() { let fb_baseline: Option<(u64, u64)> = if to_front_opt.is_some() {
let selected_set: std::collections::HashSet<u64> = let selected_set: rustc_hash::FxHashSet<u64> =
selected.iter().map(|h| h.value()).collect(); selected.iter().map(|h| h.value()).collect();
let doc_ref = &self.tabs[i].scene.document; let doc_ref = &self.tabs[i].scene.document;
let overrides: std::collections::HashMap<u64, u64> = doc_ref let overrides: rustc_hash::FxHashMap<u64, u64> = doc_ref
.objects .objects
.values() .values()
.find_map(|obj| { .find_map(|obj| {
@ -4358,7 +4358,7 @@ impl OpenCADStudio {
let all = sub == "ALL" || sub.is_empty(); let all = sub == "ALL" || sub.is_empty();
// Collect names in use (immutable borrows — done in their own scope) // Collect names in use (immutable borrows — done in their own scope)
let used_layers: std::collections::HashSet<String> = self.tabs[i] let used_layers: rustc_hash::FxHashSet<String> = self.tabs[i]
.scene .scene
.document .document
.entities() .entities()
@ -4371,7 +4371,7 @@ impl OpenCADStudio {
} }
}) })
.collect(); .collect();
let used_text_styles: std::collections::HashSet<String> = self.tabs[i] let used_text_styles: rustc_hash::FxHashSet<String> = self.tabs[i]
.scene .scene
.document .document
.entities() .entities()
@ -4382,7 +4382,7 @@ impl OpenCADStudio {
}) })
.filter(|s| !s.is_empty()) .filter(|s| !s.is_empty())
.collect(); .collect();
let used_linetypes: std::collections::HashSet<String> = self.tabs[i] let used_linetypes: rustc_hash::FxHashSet<String> = self.tabs[i]
.scene .scene
.document .document
.entities() .entities()

View file

@ -171,7 +171,7 @@ pub(super) fn entities_centroid(wires: &[WireModel]) -> glam::Vec3 {
/// Generate the next available auto group name ("*A1", "*A2", …). /// Generate the next available auto group name ("*A1", "*A2", …).
pub(super) fn next_group_auto_name(scene: &crate::scene::Scene) -> String { pub(super) fn next_group_auto_name(scene: &crate::scene::Scene) -> String {
let existing: std::collections::HashSet<String> = let existing: rustc_hash::FxHashSet<String> =
scene.groups().map(|g| g.name.clone()).collect(); scene.groups().map(|g| g.name.clone()).collect();
for n in 1..=9999 { for n in 1..=9999 {
let name = format!("*A{n}"); let name = format!("*A{n}");

View file

@ -1,5 +1,5 @@
use super::{document::HistorySnapshot, OpenCADStudio}; use super::{document::HistorySnapshot, OpenCADStudio};
use std::collections::HashSet; use rustc_hash::FxHashSet as HashSet;
impl OpenCADStudio { impl OpenCADStudio {
pub(super) fn history_label_from_active_cmd(&self, i: usize, fallback: &'static str) -> String { pub(super) fn history_label_from_active_cmd(&self, i: usize, fallback: &'static str) -> String {

View file

@ -378,7 +378,7 @@ pub(super) struct OpenCADStudio {
// ── Keyboard Shortcut Editor ────────────────────────────────────────── // ── Keyboard Shortcut Editor ──────────────────────────────────────────
/// User-defined function-key overrides: "F3" → command string. /// User-defined function-key overrides: "F3" → command string.
shortcut_overrides: std::collections::HashMap<String, String>, shortcut_overrides: rustc_hash::FxHashMap<String, String>,
// ── Layout Manager Panel ────────────────────────────────────────────── // ── Layout Manager Panel ──────────────────────────────────────────────
layout_manager_selected: String, layout_manager_selected: String,
@ -1324,7 +1324,7 @@ impl OpenCADStudio {
// Color scheme (default: dark CAD-style) // Color scheme (default: dark CAD-style)
active_theme: Theme::Dark, active_theme: Theme::Dark,
// Keyboard shortcuts // Keyboard shortcuts
shortcut_overrides: std::collections::HashMap::new(), shortcut_overrides: rustc_hash::FxHashMap::default(),
// Layout Manager // Layout Manager
layout_manager_selected: "Model".to_string(), layout_manager_selected: "Model".to_string(),
layout_manager_rename_buf: String::new(), layout_manager_rename_buf: String::new(),

View file

@ -214,7 +214,7 @@ impl OpenCADStudio {
self.tabs[i].scene.meshes = caches.meshes; self.tabs[i].scene.meshes = caches.meshes;
// Invalidate the wire cache so the new document is tessellated. // Invalidate the wire cache so the new document is tessellated.
self.tabs[i].scene.bump_geometry(); self.tabs[i].scene.bump_geometry();
self.tabs[i].scene.selected = std::collections::HashSet::new(); self.tabs[i].scene.selected = rustc_hash::FxHashSet::default();
self.tabs[i].scene.preview_wires = vec![]; self.tabs[i].scene.preview_wires = vec![];
self.tabs[i].scene.current_layout = "Model".to_string(); self.tabs[i].scene.current_layout = "Model".to_string();
crate::linetypes::populate_document(&mut self.tabs[i].scene.document); crate::linetypes::populate_document(&mut self.tabs[i].scene.document);

View file

@ -808,7 +808,7 @@ pub trait DimensionTess {
line_weight_px: f32, line_weight_px: f32,
world_offset: [f64; 3], world_offset: [f64; 3],
anno_scale: f32, anno_scale: f32,
selected_set: &std::collections::HashSet<acadrust::Handle>, selected_set: &rustc_hash::FxHashSet<acadrust::Handle>,
active_viewport: Option<acadrust::Handle>, active_viewport: Option<acadrust::Handle>,
bg_color: [f32; 4], bg_color: [f32; 4],
view_aabb: Option<[f32; 4]>, view_aabb: Option<[f32; 4]>,
@ -826,7 +826,7 @@ impl DimensionTess for Dimension {
line_weight_px: f32, line_weight_px: f32,
world_offset: [f64; 3], world_offset: [f64; 3],
anno_scale: f32, anno_scale: f32,
selected_set: &std::collections::HashSet<acadrust::Handle>, selected_set: &rustc_hash::FxHashSet<acadrust::Handle>,
active_viewport: Option<acadrust::Handle>, active_viewport: Option<acadrust::Handle>,
bg_color: [f32; 4], bg_color: [f32; 4],
view_aabb: Option<[f32; 4]>, view_aabb: Option<[f32; 4]>,
@ -862,7 +862,7 @@ fn tessellate_dimension_inner(
// LOD hints — when present, synthesised dim text routes through the // LOD hints — when present, synthesised dim text routes through the
// top-level LOD ladder (baseline / greek / full) instead of the truck // top-level LOD ladder (baseline / greek / full) instead of the truck
// path so far-out drawings collapse to a colored rect or baseline. // path so far-out drawings collapse to a colored rect or baseline.
selected_set: &std::collections::HashSet<acadrust::Handle>, selected_set: &rustc_hash::FxHashSet<acadrust::Handle>,
active_viewport: Option<acadrust::Handle>, active_viewport: Option<acadrust::Handle>,
bg_color: [f32; 4], bg_color: [f32; 4],
view_aabb: Option<[f32; 4]>, view_aabb: Option<[f32; 4]>,

View file

@ -59,8 +59,8 @@ impl TruckConvertible for Table {
// hidden borders disappear from the grid. Cells with no style still // hidden borders disappear from the grid. Cells with no style still
// emit the standard four borders. To avoid drawing each shared edge // emit the standard four borders. To avoid drawing each shared edge
// twice we coalesce the segments by their (start, end) coordinates. // twice we coalesce the segments by their (start, end) coordinates.
use std::collections::HashSet; use rustc_hash::FxHashSet as HashSet;
let mut emitted: HashSet<(i32, i32, i32, i32)> = HashSet::new(); let mut emitted: HashSet<(i32, i32, i32, i32)> = HashSet::default();
let try_add = |a: Vec3, b: Vec3, vis: bool, emitted: &mut HashSet<(i32, i32, i32, i32)>, pts: &mut Vec<[f32; 3]>| { let try_add = |a: Vec3, b: Vec3, vis: bool, emitted: &mut HashSet<(i32, i32, i32, i32)>, pts: &mut Vec<[f32; 3]>| {
if !vis { if !vis {
return; return;
@ -334,7 +334,7 @@ pub fn tessellate_table(
use crate::scene::tess_util::aci_to_rgba; use crate::scene::tess_util::aci_to_rgba;
use crate::scene::wire_model::WireModel; use crate::scene::wire_model::WireModel;
use acadrust::types::Color; use acadrust::types::Color;
use std::collections::HashMap; use rustc_hash::FxHashMap as HashMap;
if tab.rows.is_empty() || tab.columns.is_empty() { if tab.rows.is_empty() || tab.columns.is_empty() {
return Vec::new(); return Vec::new();
@ -438,10 +438,10 @@ pub fn tessellate_table(
}; };
// Accumulators keyed by quantised colour (+ weight for borders). // Accumulators keyed by quantised colour (+ weight for borders).
let mut fills: HashMap<[u8; 4], ([f32; 4], Vec<[f32; 3]>)> = HashMap::new(); let mut fills: HashMap<[u8; 4], ([f32; 4], Vec<[f32; 3]>)> = HashMap::default();
let mut texts: HashMap<[u8; 4], ([f32; 4], Vec<[f32; 3]>)> = HashMap::new(); let mut texts: HashMap<[u8; 4], ([f32; 4], Vec<[f32; 3]>)> = HashMap::default();
let mut borders: HashMap<([u8; 4], u32), ([f32; 4], f32, Vec<[f32; 3]>)> = HashMap::new(); let mut borders: HashMap<([u8; 4], u32), ([f32; 4], f32, Vec<[f32; 3]>)> = HashMap::default();
let mut emitted: std::collections::HashSet<(i32, i32, i32, i32)> = std::collections::HashSet::new(); let mut emitted: rustc_hash::FxHashSet<(i32, i32, i32, i32)> = rustc_hash::FxHashSet::default();
let sel_col = WireModel::SELECTED; let sel_col = WireModel::SELECTED;
let mut add_edge = let mut add_edge =

View file

@ -9,7 +9,7 @@
//! STB files follow the same format but use named styles instead of //! STB files follow the same format but use named styles instead of
//! ACI indices; they are read into a `Vec<NamedPlotStyle>`. //! ACI indices; they are read into a `Vec<NamedPlotStyle>`.
use std::collections::HashMap; use rustc_hash::FxHashMap as HashMap;
use std::io::Read; use std::io::Read;
use std::path::Path; use std::path::Path;
@ -72,7 +72,7 @@ impl PlotStyleTable {
name: name.into(), name: name.into(),
is_stb: false, is_stb: false,
aci_entries: (0..=255).map(|_| PlotStyleEntry::default()).collect(), aci_entries: (0..=255).map(|_| PlotStyleEntry::default()).collect(),
named_entries: HashMap::new(), named_entries: HashMap::default(),
} }
} }
@ -202,7 +202,7 @@ fn compress_ctb(text: &[u8]) -> Result<Vec<u8>, String> {
fn parse_plot_style_text(text: &str, name: String, is_stb: bool) -> Result<PlotStyleTable, String> { fn parse_plot_style_text(text: &str, name: String, is_stb: bool) -> Result<PlotStyleTable, String> {
let mut aci_entries: Vec<PlotStyleEntry> = let mut aci_entries: Vec<PlotStyleEntry> =
(0..=255).map(|_| PlotStyleEntry::default()).collect(); (0..=255).map(|_| PlotStyleEntry::default()).collect();
let mut named_entries: HashMap<String, PlotStyleEntry> = HashMap::new(); let mut named_entries: HashMap<String, PlotStyleEntry> = HashMap::default();
let mut style_index: usize = 1; // CTB: 1-based ACI index let mut style_index: usize = 1; // CTB: 1-based ACI index
let mut current: Option<PlotStyleEntry> = None; let mut current: Option<PlotStyleEntry> = None;
let mut current_name: String = String::new(); let mut current_name: String = String::new();

View file

@ -5,7 +5,7 @@ use acadrust::entities::{Block, BlockEnd};
use acadrust::tables::TableEntry; use acadrust::tables::TableEntry;
use acadrust::types::{Handle, Vector3}; use acadrust::types::{Handle, Vector3};
use acadrust::{CadDocument, EntityType}; use acadrust::{CadDocument, EntityType};
use std::collections::HashMap; use rustc_hash::FxHashMap as HashMap;
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
/// Status of an external reference block. /// Status of an external reference block.
@ -160,7 +160,7 @@ fn merge_xref_into_block(
// Prefix every xref layer (including "0"). Entity layer references are // Prefix every xref layer (including "0"). Entity layer references are
// remapped below so the resolver finds the merged copy. Host layers // remapped below so the resolver finds the merged copy. Host layers
// (incl. its own "0") are untouched — no collisions. // (incl. its own "0") are untouched — no collisions.
let mut layer_map: HashMap<String, String> = HashMap::new(); let mut layer_map: HashMap<String, String> = HashMap::default();
for layer in xref_doc.layers.iter() { for layer in xref_doc.layers.iter() {
let old = layer.name.clone(); let old = layer.name.clone();
let new = format!("{}|{}", prefix, old); let new = format!("{}|{}", prefix, old);
@ -174,7 +174,7 @@ fn merge_xref_into_block(
// ── Linetypes ─────────────────────────────────────────────────────── // ── Linetypes ───────────────────────────────────────────────────────
// Skip the three sentinel names — "ByLayer" / "ByBlock" / "Continuous" // Skip the three sentinel names — "ByLayer" / "ByBlock" / "Continuous"
// are magic strings the resolver matches verbatim in both docs. // are magic strings the resolver matches verbatim in both docs.
let mut linetype_map: HashMap<String, String> = HashMap::new(); let mut linetype_map: HashMap<String, String> = HashMap::default();
for lt in xref_doc.line_types.iter() { for lt in xref_doc.line_types.iter() {
let old = lt.name.clone(); let old = lt.name.clone();
if is_sentinel_linetype(&old) { if is_sentinel_linetype(&old) {
@ -195,8 +195,8 @@ fn merge_xref_into_block(
// //
// Tracks (xref-doc BR handle → host BR handle) so entities owned by // Tracks (xref-doc BR handle → host BR handle) so entities owned by
// a nested xref block can be routed to the right host block_record. // a nested xref block can be routed to the right host block_record.
let mut br_handle_map: HashMap<Handle, Handle> = HashMap::new(); let mut br_handle_map: HashMap<Handle, Handle> = HashMap::default();
let mut block_name_map: HashMap<String, String> = HashMap::new(); let mut block_name_map: HashMap<String, String> = HashMap::default();
for br in xref_doc.block_records.iter() { for br in xref_doc.block_records.iter() {
// Skip layout block records (*Model_Space, *Paper_Space, *Paper_Space0…) // Skip layout block records (*Model_Space, *Paper_Space, *Paper_Space0…)
// and any further-nested xrefs (we don't recurse into xref-of-xref). // and any further-nested xrefs (we don't recurse into xref-of-xref).

View file

@ -5,7 +5,7 @@
//! //!
//! [`complex_lt`] returns the complex segment catalog for CPU-side rendering. //! [`complex_lt`] returns the complex segment catalog for CPU-side rendering.
use std::collections::HashMap; use rustc_hash::FxHashMap as HashMap;
use std::sync::OnceLock; use std::sync::OnceLock;
use acadrust::tables::linetype::{LineType, LineTypeElement}; use acadrust::tables::linetype::{LineType, LineTypeElement};
@ -217,7 +217,7 @@ fn push_element(token: &str, out: &mut Vec<LineTypeElement>) {
/// A linetype is "complex" when its A-line contains at least one `[SHAPE,...]` /// A linetype is "complex" when its A-line contains at least one `[SHAPE,...]`
/// element. Simple (dash-only) linetypes are excluded. /// element. Simple (dash-only) linetypes are excluded.
fn parse_complex(src: &str) -> HashMap<String, ComplexLt> { fn parse_complex(src: &str) -> HashMap<String, ComplexLt> {
let mut catalog: HashMap<String, ComplexLt> = HashMap::new(); let mut catalog: HashMap<String, ComplexLt> = HashMap::default();
let mut current: Option<(String, String)> = None; // (name, description) let mut current: Option<(String, String)> = None; // (name, description)
for raw in src.lines() { for raw in src.lines() {

View file

@ -15,7 +15,7 @@
// a visited set so a self-referential block produces a marker rather than // a visited set so a self-referential block produces a marker rather than
// recursing forever. // recursing forever.
use std::collections::HashMap; use rustc_hash::FxHashMap as HashMap;
use std::sync::Arc; use std::sync::Arc;
use acadrust::types::{Color as AcadColor, LineWeight, Transform, Vector3}; use acadrust::types::{Color as AcadColor, LineWeight, Transform, Vector3};
@ -206,8 +206,8 @@ impl BlockCache {
/// Walk all entities + all block_record contents collecting every distinct /// Walk all entities + all block_record contents collecting every distinct
/// `block_name` that appears in an Insert (transitively). /// `block_name` that appears in an Insert (transitively).
fn collect_referenced_blocks(doc: &CadDocument) -> Vec<String> { fn collect_referenced_blocks(doc: &CadDocument) -> Vec<String> {
use std::collections::HashSet; use rustc_hash::FxHashSet as HashSet;
let mut seen: HashSet<String> = HashSet::new(); let mut seen: HashSet<String> = HashSet::default();
let mut queue: Vec<String> = Vec::new(); let mut queue: Vec<String> = Vec::new();
for entity in doc.entities() { for entity in doc.entities() {

View file

@ -4,7 +4,7 @@
//! to 2-D pixel coordinates, then compared against the cursor or selection box. //! to 2-D pixel coordinates, then compared against the cursor or selection box.
//! This matches the visual result the user sees. //! This matches the visual result the user sees.
use std::collections::HashMap; use rustc_hash::FxHashMap as HashMap;
use acadrust::Handle; use acadrust::Handle;
use glam::{Mat4, Vec3}; use glam::{Mat4, Vec3};

View file

@ -16,7 +16,7 @@
// Cap height is 9 glyph units, matching the `height / 9.0` text scale used // Cap height is 9 glyph units, matching the `height / 9.0` text scale used
// throughout the renderer. // throughout the renderer.
use std::collections::{HashMap, HashSet}; use rustc_hash::{FxHashMap as HashMap, FxHashSet as HashSet};
use std::sync::{Mutex, OnceLock}; use std::sync::{Mutex, OnceLock};
// ── Embedded fonts ───────────────────────────────────────────────────────── // ── Embedded fonts ─────────────────────────────────────────────────────────
@ -154,7 +154,7 @@ fn warn_missing_glyph(font_name: &str, ch: char) {
if ch.is_ascii() { if ch.is_ascii() {
return; return;
} }
let set = WARNED_GLYPHS.get_or_init(|| Mutex::new(HashSet::new())); let set = WARNED_GLYPHS.get_or_init(|| Mutex::new(HashSet::default()));
if let Ok(mut guard) = set.lock() { if let Ok(mut guard) = set.lock() {
if guard.insert((font_name.to_string(), ch)) { if guard.insert((font_name.to_string(), ch)) {
eprintln!( eprintln!(
@ -167,7 +167,7 @@ fn warn_missing_glyph(font_name: &str, ch: char) {
fn fonts_map() -> &'static HashMap<String, Font> { fn fonts_map() -> &'static HashMap<String, Font> {
FONTS.get_or_init(|| { FONTS.get_or_init(|| {
let mut map = HashMap::new(); let mut map = HashMap::default();
// Register every font under its stem and its `# Name:` header. // Register every font under its stem and its `# Name:` header.
for (stem, src) in FONTS_SRC { for (stem, src) in FONTS_SRC {
let f = parse_lff(src); let f = parse_lff(src);
@ -461,12 +461,12 @@ fn parse_lff(src: &str) -> Font {
letter_spacing: 3.0, letter_spacing: 3.0,
word_spacing: 6.75, word_spacing: 6.75,
line_spacing: 1.0, line_spacing: 1.0,
glyphs: HashMap::new(), glyphs: HashMap::default(),
shapes: HashMap::new(), shapes: HashMap::default(),
}; };
let mut raw: HashMap<char, RawGlyph> = HashMap::new(); let mut raw: HashMap<char, RawGlyph> = HashMap::default();
let mut raw_shapes: HashMap<String, RawGlyph> = HashMap::new(); let mut raw_shapes: HashMap<String, RawGlyph> = HashMap::default();
let mut cur: Option<char> = None; let mut cur: Option<char> = None;
let mut cur_name: Option<String> = None; let mut cur_name: Option<String> = None;
let mut cur_glyph = RawGlyph::default(); let mut cur_glyph = RawGlyph::default();

View file

@ -62,7 +62,7 @@ use truck_modeling::{
use iced::time::Duration; use iced::time::Duration;
use std::cell::RefCell; use std::cell::RefCell;
use std::collections::{HashMap, HashSet}; use rustc_hash::{FxHashMap as HashMap, FxHashSet as HashSet};
use std::rc::Rc; use std::rc::Rc;
use std::sync::atomic::{AtomicU64, Ordering}; use std::sync::atomic::{AtomicU64, Ordering};
use std::sync::Arc; use std::sync::Arc;
@ -256,9 +256,9 @@ struct OffsetPrep {
/// `Some` when the model BlockRecord enumerates its entities; the offset /// `Some` when the model BlockRecord enumerates its entities; the offset
/// scan uses this set directly. `None` falls back to the legacy /// scan uses this set directly. `None` falls back to the legacy
/// permissive owner-based interpretation. /// permissive owner-based interpretation.
mspace_set: Option<std::collections::HashSet<Handle>>, mspace_set: Option<rustc_hash::FxHashSet<Handle>>,
any_enumerated: bool, any_enumerated: bool,
owned_by_other_block: std::collections::HashSet<Handle>, owned_by_other_block: rustc_hash::FxHashSet<Handle>,
} }
fn offset_prep(doc: &acadrust::CadDocument, model_block: Handle) -> OffsetPrep { fn offset_prep(doc: &acadrust::CadDocument, model_block: Handle) -> OffsetPrep {
@ -266,21 +266,21 @@ fn offset_prep(doc: &acadrust::CadDocument, model_block: Handle) -> OffsetPrep {
.block_records .block_records
.iter() .iter()
.find(|br| br.handle == model_block); .find(|br| br.handle == model_block);
let mspace_set: Option<std::collections::HashSet<Handle>> = model_br let mspace_set: Option<rustc_hash::FxHashSet<Handle>> = model_br
.filter(|br| !br.entity_handles.is_empty()) .filter(|br| !br.entity_handles.is_empty())
.map(|br| br.entity_handles.iter().copied().collect()); .map(|br| br.entity_handles.iter().copied().collect());
let any_enumerated = doc let any_enumerated = doc
.block_records .block_records
.iter() .iter()
.any(|br| !br.entity_handles.is_empty()); .any(|br| !br.entity_handles.is_empty());
let owned_by_other_block: std::collections::HashSet<Handle> = if mspace_set.is_none() { let owned_by_other_block: rustc_hash::FxHashSet<Handle> = if mspace_set.is_none() {
doc.block_records doc.block_records
.iter() .iter()
.filter(|br| br.handle != model_block) .filter(|br| br.handle != model_block)
.flat_map(|br| br.entity_handles.iter().copied()) .flat_map(|br| br.entity_handles.iter().copied())
.collect() .collect()
} else { } else {
std::collections::HashSet::new() rustc_hash::FxHashSet::default()
}; };
OffsetPrep { mspace_set, any_enumerated, owned_by_other_block } OffsetPrep { mspace_set, any_enumerated, owned_by_other_block }
} }
@ -718,31 +718,31 @@ impl Scene {
active_model_tile: std::cell::Cell::new(0), active_model_tile: std::cell::Cell::new(0),
selection: Rc::new(RefCell::new(SelectionState::default())), selection: Rc::new(RefCell::new(SelectionState::default())),
document: CadDocument::new(), document: CadDocument::new(),
selected: HashSet::new(), selected: HashSet::default(),
hidden: HashSet::new(), hidden: HashSet::default(),
hover_highlight: None, hover_highlight: None,
transparency_display: true, transparency_display: true,
selection_filter: HashSet::new(), selection_filter: HashSet::default(),
preview_wires: vec![], preview_wires: vec![],
interim_wire: None, interim_wire: None,
camera_generation: 0, camera_generation: 0,
geometry_epoch: GEOMETRY_EPOCH.fetch_add(1, Ordering::Relaxed), geometry_epoch: GEOMETRY_EPOCH.fetch_add(1, Ordering::Relaxed),
wire_cache: RefCell::new(None), wire_cache: RefCell::new(None),
model_tile_wire_cache: RefCell::new(HashMap::new()), model_tile_wire_cache: RefCell::new(HashMap::default()),
sort_cache: RefCell::new(None), sort_cache: RefCell::new(None),
draw_depth_cache: RefCell::new(None), draw_depth_cache: RefCell::new(None),
hatch_cache: RefCell::new(None), hatch_cache: RefCell::new(None),
wipeout_cache: RefCell::new(None), wipeout_cache: RefCell::new(None),
image_cache: RefCell::new(None), image_cache: RefCell::new(None),
mesh_cache: RefCell::new(None), mesh_cache: RefCell::new(None),
viewport_wire_cache: RefCell::new(HashMap::new()), viewport_wire_cache: RefCell::new(HashMap::default()),
paper_sheet_cache: RefCell::new(None), paper_sheet_cache: RefCell::new(None),
paper_projected_cache: RefCell::new(HashMap::new()), paper_projected_cache: RefCell::new(HashMap::default()),
current_layout: "Model".to_string(), current_layout: "Model".to_string(),
hatches: HashMap::new(), hatches: HashMap::default(),
meshes: HashMap::new(), meshes: HashMap::default(),
model_solids: HashMap::new(), model_solids: HashMap::default(),
images: HashMap::new(), images: HashMap::default(),
active_viewport: None, active_viewport: None,
bg_color: [0.11, 0.11, 0.11, 1.0], bg_color: [0.11, 0.11, 0.11, 1.0],
paper_bg_color: [1.0, 1.0, 1.0, 1.0], paper_bg_color: [1.0, 1.0, 1.0, 1.0],
@ -1455,7 +1455,7 @@ impl Scene {
// Deduplicate by name: prefer the entry with a non-null block_record (the // Deduplicate by name: prefer the entry with a non-null block_record (the
// real layout from the file) over the default placeholder created by // real layout from the file) over the default placeholder created by
// CadDocument::new(). // CadDocument::new().
let mut by_name: std::collections::HashMap<String, (i16, Handle)> = Default::default(); let mut by_name: rustc_hash::FxHashMap<String, (i16, Handle)> = Default::default();
for obj in self.document.objects.values() { for obj in self.document.objects.values() {
if let ObjectType::Layout(l) = obj { if let ObjectType::Layout(l) = obj {
if l.name == "Model" || l.name.is_empty() { if l.name == "Model" || l.name.is_empty() {
@ -1628,7 +1628,7 @@ impl Scene {
} }
use acadrust::objects::ObjectType; use acadrust::objects::ObjectType;
// Per-block SortEntitiesTable overrides: block -> (entity_val -> sort_val). // Per-block SortEntitiesTable overrides: block -> (entity_val -> sort_val).
let mut overrides: HashMap<Handle, HashMap<u64, u64>> = HashMap::new(); let mut overrides: HashMap<Handle, HashMap<u64, u64>> = HashMap::default();
for obj in self.document.objects.values() { for obj in self.document.objects.values() {
if let ObjectType::SortEntitiesTable(t) = obj { if let ObjectType::SortEntitiesTable(t) = obj {
if !t.is_empty() { if !t.is_empty() {
@ -1643,7 +1643,7 @@ impl Scene {
} }
let ms = self.model_space_block_handle(); let ms = self.model_space_block_handle();
// Group entities by owning block, carrying each entity's effective key. // Group entities by owning block, carrying each entity's effective key.
let mut by_block: HashMap<Handle, Vec<(u64, u64)>> = HashMap::new(); let mut by_block: HashMap<Handle, Vec<(u64, u64)>> = HashMap::default();
for e in self.document.entities() { for e in self.document.entities() {
let c = e.common(); let c = e.common();
// 3D meshes keep real geometric depth — exclude them from // 3D meshes keep real geometric depth — exclude them from
@ -1667,7 +1667,7 @@ impl Scene {
.unwrap_or(hv); .unwrap_or(hv);
by_block.entry(block).or_default().push((hv, eff)); by_block.entry(block).or_default().push((hv, eff));
} }
let mut depth_map: HashMap<u64, f32> = HashMap::new(); let mut depth_map: HashMap<u64, f32> = HashMap::default();
for (_block, mut v) in by_block { for (_block, mut v) in by_block {
v.sort_by_key(|(_, eff)| *eff); v.sort_by_key(|(_, eff)| *eff);
let denom = (v.len() as f32) + 1.0; let denom = (v.len() as f32) + 1.0;
@ -1928,7 +1928,7 @@ impl Scene {
.unwrap_or(true); .unwrap_or(true);
if needs_rebuild { if needs_rebuild {
let mut idx: HashMap<Handle, HashMap<u64, u64>> = HashMap::new(); let mut idx: HashMap<Handle, HashMap<u64, u64>> = HashMap::default();
for obj in self.document.objects.values() { for obj in self.document.objects.values() {
if let ObjectType::SortEntitiesTable(t) = obj { if let ObjectType::SortEntitiesTable(t) = obj {
if !t.is_empty() { if !t.is_empty() {
@ -2187,7 +2187,7 @@ impl Scene {
} }
} }
} }
let mut map: HashMap<Handle, Handle> = HashMap::new(); let mut map: HashMap<Handle, Handle> = HashMap::default();
for br in self.document.block_records.iter() { for br in self.document.block_records.iter() {
for &eh in &br.entity_handles { for &eh in &br.entity_handles {
map.insert(eh, br.handle); map.insert(eh, br.handle);
@ -4098,11 +4098,11 @@ impl Scene {
pub fn clear(&mut self) { pub fn clear(&mut self) {
self.document = CadDocument::new(); self.document = CadDocument::new();
self.selected = HashSet::new(); self.selected = HashSet::default();
self.preview_wires = vec![]; self.preview_wires = vec![];
self.current_layout = "Model".to_string(); self.current_layout = "Model".to_string();
self.hatches = HashMap::new(); self.hatches = HashMap::default();
self.meshes = HashMap::new(); self.meshes = HashMap::default();
*self.camera.borrow_mut() = Camera::default(); *self.camera.borrow_mut() = Camera::default();
self.camera_generation += 1; self.camera_generation += 1;
self.bump_geometry(); self.bump_geometry();
@ -4201,7 +4201,7 @@ impl Scene {
if self.selected.is_empty() { if self.selected.is_empty() {
return 0; return 0;
} }
let pairs: std::collections::HashSet<(&'static str, String)> = self let pairs: rustc_hash::FxHashSet<(&'static str, String)> = self
.selected .selected
.iter() .iter()
.filter_map(|h| self.document.get_entity(*h)) .filter_map(|h| self.document.get_entity(*h))
@ -6276,7 +6276,7 @@ impl Scene {
vp_handle: Handle, vp_handle: Handle,
screen_height_px: f32, screen_height_px: f32,
) -> Vec<WireModel> { ) -> Vec<WireModel> {
use std::collections::HashSet as HSet; use rustc_hash::FxHashSet as HSet;
let (frozen, vp_anno_scale, vp_aspect) = match self.document.get_entity(vp_handle) { let (frozen, vp_anno_scale, vp_aspect) = match self.document.get_entity(vp_handle) {
Some(EntityType::Viewport(vp)) => { Some(EntityType::Viewport(vp)) => {
@ -6295,7 +6295,7 @@ impl Scene {
}; };
(f, anno, aspect) (f, anno, aspect)
} }
_ => (HSet::new(), 1.0_f32, 1.0_f32), _ => (HSet::default(), 1.0_f32, 1.0_f32),
}; };
// Drive the per-viewport view_aabb / wpp from the *effective* camera // Drive the per-viewport view_aabb / wpp from the *effective* camera

View file

@ -93,7 +93,7 @@ impl Face3DGpu {
face3d_wires: &[WireModel], face3d_wires: &[WireModel],
all_wires: &[WireModel], all_wires: &[WireModel],
keep_3d_mesh_fills: bool, keep_3d_mesh_fills: bool,
depth_map: &std::collections::HashMap<u64, f32>, depth_map: &rustc_hash::FxHashMap<u64, f32>,
) -> Self { ) -> Self {
let depth_of = |w: &WireModel| -> f32 { let depth_of = |w: &WireModel| -> f32 {
w.name w.name

View file

@ -891,7 +891,7 @@ impl Pipeline {
&mut self, &mut self,
device: &wgpu::Device, device: &wgpu::Device,
wires: &[WireModel], wires: &[WireModel],
depth_map: &std::collections::HashMap<u64, f32>, depth_map: &rustc_hash::FxHashMap<u64, f32>,
) { ) {
let depth_of = |w: &WireModel| -> f32 { let depth_of = |w: &WireModel| -> f32 {
w.name w.name
@ -987,7 +987,7 @@ impl Pipeline {
face3d_wires: &[WireModel], face3d_wires: &[WireModel],
all_wires: &[WireModel], all_wires: &[WireModel],
wireframe_only: bool, wireframe_only: bool,
depth_map: &std::collections::HashMap<u64, f32>, depth_map: &rustc_hash::FxHashMap<u64, f32>,
) { ) {
// Edge buffer is always built from `face3d_wires`, so 3DFACE // Edge buffer is always built from `face3d_wires`, so 3DFACE
// outlines stay on the screen regardless of mode. // outlines stay on the screen regardless of mode.

View file

@ -267,7 +267,7 @@ fn emit_wire_instances(wire: &WireModel, color: [f32; 4], draw_depth: f32) -> Ve
/// Looks up a wire's draw-order depth from the per-entity map using the /// Looks up a wire's draw-order depth from the per-entity map using the
/// handle encoded in its `name`. Falls back to 0.0 (transient / preview /// handle encoded in its `name`. Falls back to 0.0 (transient / preview
/// wires that carry no document handle). /// wires that carry no document handle).
fn wire_draw_depth(wire: &WireModel, depth_map: &std::collections::HashMap<u64, f32>) -> f32 { fn wire_draw_depth(wire: &WireModel, depth_map: &rustc_hash::FxHashMap<u64, f32>) -> f32 {
wire wire
.name .name
.parse::<u64>() .parse::<u64>()
@ -290,7 +290,7 @@ impl WireGpu {
pub fn from_batch( pub fn from_batch(
device: &wgpu::Device, device: &wgpu::Device,
wires: &[WireModel], wires: &[WireModel],
depth_map: &std::collections::HashMap<u64, f32>, depth_map: &rustc_hash::FxHashMap<u64, f32>,
) -> Vec<Self> { ) -> Vec<Self> {
let total_segs: usize = wires.iter().map(|w| w.points.len().saturating_sub(1)).sum(); let total_segs: usize = wires.iter().map(|w| w.points.len().saturating_sub(1)).sum();
if total_segs == 0 { if total_segs == 0 {

View file

@ -57,7 +57,7 @@ pub struct QuadTree {
nodes: Vec<Node>, nodes: Vec<Node>,
/// `handle → (node_idx, item_idx_within_node)` so removal/update /// `handle → (node_idx, item_idx_within_node)` so removal/update
/// is O(1) without walking the tree. /// is O(1) without walking the tree.
locator: std::collections::HashMap<Handle, (u32, u32)>, locator: rustc_hash::FxHashMap<Handle, (u32, u32)>,
/// Items whose AABB falls outside the root bounds. Surfaced on /// Items whose AABB falls outside the root bounds. Surfaced on
/// every query — small set in practice (typically empty for /// every query — small set in practice (typically empty for
/// well-bounded drawings). /// well-bounded drawings).
@ -74,7 +74,7 @@ impl QuadTree {
}; };
Self { Self {
nodes: vec![root], nodes: vec![root],
locator: std::collections::HashMap::new(), locator: rustc_hash::FxHashMap::default(),
overflow: Vec::new(), overflow: Vec::new(),
} }
} }

View file

@ -39,7 +39,7 @@ pub struct ViewportData {
/// by the wire / face3d pipelines as a clip-z bias. WireModels carry no /// by the wire / face3d pipelines as a clip-z bias. WireModels carry no
/// depth field (84 construction sites); the bias is looked up by handle /// depth field (84 construction sites); the bias is looked up by handle
/// at GPU-upload time from this map instead. /// at GPU-upload time from this map instead.
pub(super) draw_depths: Arc<std::collections::HashMap<u64, f32>>, pub(super) draw_depths: Arc<rustc_hash::FxHashMap<u64, f32>>,
pub(super) hatches: Arc<Vec<HatchModel>>, pub(super) hatches: Arc<Vec<HatchModel>>,
/// Wipeout fills — rendered in a separate pass AFTER wires. /// Wipeout fills — rendered in a separate pass AFTER wires.
pub(super) wipeout_hatches: Arc<Vec<HatchModel>>, pub(super) wipeout_hatches: Arc<Vec<HatchModel>>,

View file

@ -10,7 +10,7 @@
// All other surface types are silently skipped; partial results are still // All other surface types are silently skipped; partial results are still
// returned so the solid renders with at least its planar faces. // returned so the solid renders with at least its planar faces.
use std::collections::HashSet; use rustc_hash::FxHashSet as HashSet;
use std::f64::consts::TAU; use std::f64::consts::TAU;
use acadrust::entities::acis::types::Sense; use acadrust::entities::acis::types::Sense;
@ -237,7 +237,7 @@ fn collect_face_polygon(sat: &SatDocument, face: &SatFace) -> Vec<[f64; 3]> {
let first_ptr = sat_loop.first_coedge(); let first_ptr = sat_loop.first_coedge();
let mut cur = first_ptr; let mut cur = first_ptr;
let mut pts: Vec<[f64; 3]> = Vec::new(); let mut pts: Vec<[f64; 3]> = Vec::new();
let mut visited: HashSet<i32> = HashSet::new(); let mut visited: HashSet<i32> = HashSet::default();
loop { loop {
if cur.is_null() { if cur.is_null() {

View file

@ -63,7 +63,7 @@ pub struct SnapResult {
// ── Snapper ─────────────────────────────────────────────────────────────── // ── Snapper ───────────────────────────────────────────────────────────────
use std::collections::HashSet; use rustc_hash::FxHashSet as HashSet;
pub struct Snapper { pub struct Snapper {
/// Global snap on/off toggle. When false, all snapping is bypassed /// Global snap on/off toggle. When false, all snapping is bypassed
@ -87,7 +87,7 @@ pub struct Snapper {
impl Default for Snapper { impl Default for Snapper {
fn default() -> Self { fn default() -> Self {
let mut enabled = HashSet::new(); let mut enabled = HashSet::default();
enabled.insert(SnapType::Endpoint); enabled.insert(SnapType::Endpoint);
enabled.insert(SnapType::Midpoint); enabled.insert(SnapType::Midpoint);
enabled.insert(SnapType::Center); enabled.insert(SnapType::Center);
@ -257,7 +257,7 @@ impl Snapper {
let tmp = Snapper { let tmp = Snapper {
snap_enabled: true, snap_enabled: true,
enabled: { enabled: {
let mut s = HashSet::new(); let mut s = HashSet::default();
s.insert(SnapType::Tangent); s.insert(SnapType::Tangent);
s s
}, },

View file

@ -7,7 +7,7 @@
//! • Linetype → read-only for now //! • Linetype → read-only for now
//! • Geometry → text_input per coordinate / dimension field //! • Geometry → text_input per coordinate / dimension field
use std::collections::HashMap; use rustc_hash::FxHashMap as HashMap;
use std::fmt; use std::fmt;
use crate::ui::ROW_H; use crate::ui::ROW_H;
@ -142,12 +142,12 @@ impl Default for PropertiesPanel {
selected_group: None, selected_group: None,
linetype_items: vec![], linetype_items: vec![],
selection_group_combo: combo_box::State::new(vec![]), selection_group_combo: combo_box::State::new(vec![]),
choice_combos: HashMap::new(), choice_combos: HashMap::default(),
layer_combo: combo_box::State::new(vec![]), layer_combo: combo_box::State::new(vec![]),
lineweight_combo: combo_box::State::new(lw_options()), lineweight_combo: combo_box::State::new(lw_options()),
linetype_combo: combo_box::State::new(vec![]), linetype_combo: combo_box::State::new(vec![]),
hatch_pattern_combo: combo_box::State::new(crate::scene::hatch_patterns::names()), hatch_pattern_combo: combo_box::State::new(crate::scene::hatch_patterns::names()),
edit_buf: HashMap::new(), edit_buf: HashMap::default(),
color_picker_open: false, color_picker_open: false,
color_palette_open: false, color_palette_open: false,
} }

View file

@ -6,7 +6,7 @@
// //
// Dropdown items within a group are collected into columns of 3 rows. // Dropdown items within a group are collected into columns of 3 rows.
use std::collections::HashMap; use rustc_hash::FxHashMap as HashMap;
use acadrust::types::{Color as AcadColor, LineWeight}; use acadrust::types::{Color as AcadColor, LineWeight};
use iced::widget::{button, column, container, mouse_area, row, scrollable, svg, text}; use iced::widget::{button, column, container, mouse_area, row, scrollable, svg, text};
@ -73,7 +73,7 @@ impl Ribbon {
wireframe: false, wireframe: false,
ortho_mode: true, ortho_mode: true,
open_dropdown: None, open_dropdown: None,
last_cmd: HashMap::new(), last_cmd: HashMap::default(),
layer_names: vec!["0".to_string()], layer_names: vec!["0".to_string()],
active_layer: "0".to_string(), active_layer: "0".to_string(),
layer_infos: vec![LayerInfo { layer_infos: vec![LayerInfo {

View file

@ -1,7 +1,7 @@
// Shared rendering helpers, button styles, colours, layout constants, and // Shared rendering helpers, button styles, colours, layout constants, and
// free functions used by the Ribbon view/overlay methods. // free functions used by the Ribbon view/overlay methods.
use std::collections::HashMap; use rustc_hash::FxHashMap as HashMap;
use std::time::Duration; use std::time::Duration;
use acadrust::types::{Color as AcadColor, LineWeight}; use acadrust::types::{Color as AcadColor, LineWeight};

View file

@ -2,7 +2,7 @@
//! A checked row means that type can be picked; unchecking it excludes the //! A checked row means that type can be picked; unchecking it excludes the
//! type from interactive selection. Opened from the FILTER status pill. //! type from interactive selection. Opened from the FILTER status pill.
use std::collections::HashSet; use rustc_hash::FxHashSet as HashSet;
use iced::widget::{button, column, container, mouse_area, row, text}; use iced::widget::{button, column, container, mouse_area, row, text};
use iced::{Background, Border, Color, Element, Fill, Length, Padding, Theme}; use iced::{Background, Border, Color, Element, Fill, Length, Padding, Theme};

View file

@ -79,7 +79,7 @@ fn section<'a>(title: &'static str) -> Element<'a, Message> {
} }
pub fn view_window<'a>( pub fn view_window<'a>(
overrides: &'a std::collections::HashMap<String, String>, overrides: &'a rustc_hash::FxHashMap<String, String>,
) -> Element<'a, Message> { ) -> Element<'a, Message> {
// ── Toolbar ─────────────────────────────────────────────────────────── // ── Toolbar ───────────────────────────────────────────────────────────
let toolbar = container( let toolbar = container(

View file

@ -5,7 +5,7 @@
//! adds or removes that pill from the bar. The choice is persisted so it //! adds or removes that pill from the bar. The choice is persisted so it
//! survives across sessions. //! survives across sessions.
use std::collections::HashSet; use rustc_hash::FxHashSet as HashSet;
use std::path::PathBuf; use std::path::PathBuf;
/// Identifies a toggleable status-bar pill. /// Identifies a toggleable status-bar pill.
@ -121,7 +121,7 @@ pub struct StatusBarConfig {
impl StatusBarConfig { impl StatusBarConfig {
/// Load the saved customization, or all-visible when none exists. /// Load the saved customization, or all-visible when none exists.
pub fn load() -> Self { pub fn load() -> Self {
let mut hidden = HashSet::new(); let mut hidden = HashSet::default();
if let Some(path) = config_path() { if let Some(path) = config_path() {
if let Ok(body) = std::fs::read_to_string(path) { if let Ok(body) = std::fs::read_to_string(path) {
for line in body.lines() { for line in body.lines() {