From 7da5fe97ccdac33f21cbcda715817f6a36425fda Mon Sep 17 00:00:00 2001 From: NotAShelf Date: Mon, 4 Aug 2025 20:49:23 +0300 Subject: [PATCH 01/48] nix: point to nixos-unstable for the nixpkgs input Oops. Signed-off-by: NotAShelf Change-Id: I6a6a6964eab43b655668d65abe532ed710538670 --- flake.lock | 7 ++++--- flake.nix | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/flake.lock b/flake.lock index d831dac..774c899 100644 --- a/flake.lock +++ b/flake.lock @@ -2,15 +2,16 @@ "nodes": { "nixpkgs": { "locked": { - "lastModified": 1743359643, - "narHash": "sha256-RkyJ9a67s0zEIz4O66TyZOIGh4TFZ4dKHKMgnxZCh2I=", + "lastModified": 1754214453, + "narHash": "sha256-Q/I2xJn/j1wpkGhWkQnm20nShYnG7TI99foDBpXm1SY=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "ca77b4bc80e558ce59f2712fdb276f90c0ee309a", + "rev": "5b09dc45f24cf32316283e62aec81ffee3c3e376", "type": "github" }, "original": { "owner": "NixOS", + "ref": "nixos-unstable", "repo": "nixpkgs", "type": "github" } diff --git a/flake.nix b/flake.nix index a481c30..ed36872 100644 --- a/flake.nix +++ b/flake.nix @@ -1,6 +1,6 @@ { description = "A microscopic fetch script in Rust, for NixOS systems"; - inputs.nixpkgs.url = "github:NixOS/nixpkgs"; + inputs.nixpkgs.url = "github:NixOS/nixpkgs?ref=nixos-unstable"; outputs = { self, From 14d8f9390d44cc8e76686a81b1f4afeb29c6cd33 Mon Sep 17 00:00:00 2001 From: NotAShelf Date: Mon, 4 Aug 2025 20:49:49 +0300 Subject: [PATCH 02/48] chore: bump criterion Signed-off-by: NotAShelf Change-Id: I6a6a6964e55c9925511d83d6be9b2ba001613983 --- Cargo.lock | 21 ++++++--------------- Cargo.toml | 2 +- 2 files changed, 7 insertions(+), 16 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c6cfc69..3b5c132 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -113,16 +113,16 @@ checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6" [[package]] name = "criterion" -version = "0.6.0" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3bf7af66b0989381bd0be551bd7cc91912a655a58c6918420c9527b1fd8b4679" +checksum = "e1c047a62b0cc3e145fa84415a3191f628e980b194c2755aa12300a4e6cbd928" dependencies = [ "anes", "cast", "ciborium", "clap", "criterion-plot", - "itertools 0.13.0", + "itertools", "num-traits", "oorandom", "plotters", @@ -136,12 +136,12 @@ dependencies = [ [[package]] name = "criterion-plot" -version = "0.5.0" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b50826342786a51a89e2da3a28f1c32b06e387201bc2d19791f622c673706b1" +checksum = "9b1bcc0dc7dfae599d84ad0b1a55f80cde8af3725da8313b528da95ef783e338" dependencies = [ "cast", - "itertools 0.10.5", + "itertools", ] [[package]] @@ -191,15 +191,6 @@ dependencies = [ "crunchy", ] -[[package]] -name = "itertools" -version = "0.10.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" -dependencies = [ - "either", -] - [[package]] name = "itertools" version = "0.13.0" diff --git a/Cargo.toml b/Cargo.toml index f20ff00..f09c8b3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,7 +16,7 @@ nix = { version = "0.30", features = ["fs", "hostname", "feature"] } libc = "0.2" [dev-dependencies] -criterion = "0.6" +criterion = "0.7" [[bench]] name = "benchmark" From 9d8905354efca612a1e9fe87845293a4460e0553 Mon Sep 17 00:00:00 2001 From: NotAShelf Date: Mon, 4 Aug 2025 21:03:48 +0300 Subject: [PATCH 03/48] expand comment strings 80 chars is good. Signed-off-by: NotAShelf Change-Id: I6a6a696460754266ff5810a73f98ce9e6eb0c35b --- src/main.rs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/main.rs b/src/main.rs index 41a1818..5dd99c1 100644 --- a/src/main.rs +++ b/src/main.rs @@ -33,10 +33,9 @@ fn main() -> Result<(), Box> { Ok(()) } -// Struct to hold all the fields we need to print -// helps avoid clippy warnings about argument count -// and makes it easier to pass around, though its -// not like we need to +// Struct to hold all the fields we need in order to print the fetch. This +// helps avoid Clippy warnings about argument count, and makes it slightly +// easier to pass data around. Though, it is not like we really need to. struct Fields { user_info: String, os_name: String, From e355ddc51758b7b8b54e3bf65bc5270a152c94ef Mon Sep 17 00:00:00 2001 From: NotAShelf Date: Sun, 17 Aug 2025 15:19:02 +0300 Subject: [PATCH 04/48] flake: bump nixpkgs Signed-off-by: NotAShelf Change-Id: Ica041259225e45e345de08325a0cc75f6a6a6964 --- flake.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/flake.lock b/flake.lock index 774c899..8fd5240 100644 --- a/flake.lock +++ b/flake.lock @@ -2,11 +2,11 @@ "nodes": { "nixpkgs": { "locked": { - "lastModified": 1754214453, - "narHash": "sha256-Q/I2xJn/j1wpkGhWkQnm20nShYnG7TI99foDBpXm1SY=", + "lastModified": 1743359643, + "narHash": "sha256-RkyJ9a67s0zEIz4O66TyZOIGh4TFZ4dKHKMgnxZCh2I=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "5b09dc45f24cf32316283e62aec81ffee3c3e376", + "rev": "ca77b4bc80e558ce59f2712fdb276f90c0ee309a", "type": "github" }, "original": { From 8800b69ef3ccbb35558d7824e2f4dabd055be826 Mon Sep 17 00:00:00 2001 From: NotAShelf Date: Sun, 17 Aug 2025 15:19:26 +0300 Subject: [PATCH 05/48] chore: bump dependencies Signed-off-by: NotAShelf Change-Id: I6a6a69647ad9cfdd52eec7fa9ae4a184b9c8ea0d --- Cargo.lock | 8 ++++---- Cargo.toml | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3b5c132..25e640c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -218,9 +218,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.171" +version = "0.2.175" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c19937216e9d3aa9956d9bb8dfc0b0c8beb6058fc4f7a4dc4d850edf86a237d6" +checksum = "6a82ae493e598baaea5209805c49bbf2ea7de956d50d7da0da1164f9c6d28543" [[package]] name = "log" @@ -245,9 +245,9 @@ dependencies = [ [[package]] name = "nix" -version = "0.30.0" +version = "0.30.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "537bc3c4a347b87fd52ac6c03a02ab1302962cfd93373c5d7a112cdc337854cc" +checksum = "74523f3a35e05aba87a1d978330aef40f67b0304ac79c1c00b294c9830543db6" dependencies = [ "bitflags", "cfg-if", diff --git a/Cargo.toml b/Cargo.toml index f09c8b3..b7d692f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,8 +12,8 @@ name = "microfetch" path = "src/main.rs" [dependencies] -nix = { version = "0.30", features = ["fs", "hostname", "feature"] } -libc = "0.2" +nix = { version = "0.30.1", default-features = false, features = ["fs", "hostname", "feature"] } +libc = "0.2.175" [dev-dependencies] criterion = "0.7" From 6150e55ba564907a8d44c53b0a15503874d5c408 Mon Sep 17 00:00:00 2001 From: NotAShelf Date: Sun, 17 Aug 2025 15:47:24 +0300 Subject: [PATCH 06/48] flake: bump nixpkgs Signed-off-by: NotAShelf Change-Id: I6ba6412f88305b295ffb571313606bd56a6a6964 --- flake.lock | 3 +++ 1 file changed, 3 insertions(+) diff --git a/flake.lock b/flake.lock index 8fd5240..452a532 100644 --- a/flake.lock +++ b/flake.lock @@ -4,9 +4,12 @@ "locked": { "lastModified": 1743359643, "narHash": "sha256-RkyJ9a67s0zEIz4O66TyZOIGh4TFZ4dKHKMgnxZCh2I=", + "lastModified": 1763381801, + "narHash": "sha256-325fR0JmHW7B74/gHPv/S9w1Rfj/M2HniwQFUwdrZ9k=", "owner": "NixOS", "repo": "nixpkgs", "rev": "ca77b4bc80e558ce59f2712fdb276f90c0ee309a", + "rev": "46931757ea8bdbba25c076697f8e73b8dc39fef5", "type": "github" }, "original": { From dab8f556af40fbbc05f6cbb2996cba454e866cd4 Mon Sep 17 00:00:00 2001 From: NotAShelf Date: Mon, 17 Nov 2025 15:42:19 +0300 Subject: [PATCH 07/48] meta: set up formatters for Rust and TOML Signed-off-by: NotAShelf Change-Id: I917d66d2af199b96f84aa50071a0ccd56a6a6964 --- .rustfmt.toml | 26 ++++++++++++++++++++++++++ .taplo.toml | 13 +++++++++++++ 2 files changed, 39 insertions(+) create mode 100644 .rustfmt.toml create mode 100644 .taplo.toml diff --git a/.rustfmt.toml b/.rustfmt.toml new file mode 100644 index 0000000..9d5c77e --- /dev/null +++ b/.rustfmt.toml @@ -0,0 +1,26 @@ +condense_wildcard_suffixes = true +doc_comment_code_block_width = 80 +edition = "2024" # Keep in sync with Cargo.toml. +enum_discrim_align_threshold = 60 +force_explicit_abi = false +force_multiline_blocks = true +format_code_in_doc_comments = true +format_macro_matchers = true +format_strings = true +group_imports = "StdExternalCrate" +hex_literal_case = "Upper" +imports_granularity = "Crate" +imports_layout = "HorizontalVertical" +inline_attribute_width = 60 +match_block_trailing_comma = true +max_width = 80 +newline_style = "Unix" +normalize_comments = true +normalize_doc_attributes = true +overflow_delimited_expr = true +struct_field_align_threshold = 60 +tab_spaces = 2 +unstable_features = true +use_field_init_shorthand = true +use_try_shorthand = true +wrap_comments = true diff --git a/.taplo.toml b/.taplo.toml new file mode 100644 index 0000000..5a6456b --- /dev/null +++ b/.taplo.toml @@ -0,0 +1,13 @@ +[formatting] +align_entries = true +column_width = 100 +compact_arrays = false +reorder_inline_tables = true +reorder_keys = true + +[[rule]] +include = [ "**/Cargo.toml" ] +keys = [ "package" ] + +[rule.formatting] +reorder_keys = false From 1d69d3107c6aa63568cf5287476ae0b63f96ea44 Mon Sep 17 00:00:00 2001 From: NotAShelf Date: Mon, 17 Nov 2025 15:48:02 +0300 Subject: [PATCH 08/48] microfetch: trim fetch screen by one space Signed-off-by: NotAShelf Change-Id: I200c72b5a8249ed3d23754aa3f01aea86a6a6964 --- src/main.rs | 130 ++++++++++++++++++++++++++++------------------------ 1 file changed, 70 insertions(+), 60 deletions(-) diff --git a/src/main.rs b/src/main.rs index 5dd99c1..2138a44 100644 --- a/src/main.rs +++ b/src/main.rs @@ -4,78 +4,88 @@ mod release; mod system; mod uptime; -use crate::colors::print_dots; -use crate::desktop::get_desktop_info; -use crate::release::{get_os_pretty_name, get_system_info}; -use crate::system::{get_memory_usage, get_root_disk_usage, get_shell, get_username_and_hostname}; -use crate::uptime::get_current; use std::io::{Write, stdout}; -fn main() -> Result<(), Box> { - if Some("--version") == std::env::args().nth(1).as_deref() { - println!("Microfetch {}", env!("CARGO_PKG_VERSION")); - } else { - let utsname = nix::sys::utsname::uname()?; - let fields = Fields { - user_info: get_username_and_hostname(&utsname), - os_name: get_os_pretty_name()?, - kernel_version: get_system_info(&utsname), - shell: get_shell(), - desktop: get_desktop_info(), - uptime: get_current()?, - memory_usage: get_memory_usage()?, - storage: get_root_disk_usage()?, - colors: print_dots(), - }; - print_system_info(&fields)?; - } +use crate::{ + colors::print_dots, + desktop::get_desktop_info, + release::{get_os_pretty_name, get_system_info}, + system::{ + get_memory_usage, + get_root_disk_usage, + get_shell, + get_username_and_hostname, + }, + uptime::get_current, +}; - Ok(()) +fn main() -> Result<(), Box> { + if Some("--version") == std::env::args().nth(1).as_deref() { + println!("Microfetch {}", env!("CARGO_PKG_VERSION")); + } else { + let utsname = nix::sys::utsname::uname()?; + let fields = Fields { + user_info: get_username_and_hostname(&utsname), + os_name: get_os_pretty_name()?, + kernel_version: get_system_info(&utsname), + shell: get_shell(), + desktop: get_desktop_info(), + uptime: get_current()?, + memory_usage: get_memory_usage()?, + storage: get_root_disk_usage()?, + colors: print_dots(), + }; + print_system_info(&fields)?; + } + + Ok(()) } // Struct to hold all the fields we need in order to print the fetch. This // helps avoid Clippy warnings about argument count, and makes it slightly // easier to pass data around. Though, it is not like we really need to. struct Fields { - user_info: String, - os_name: String, - kernel_version: String, - shell: String, - uptime: String, - desktop: String, - memory_usage: String, - storage: String, - colors: String, + user_info: String, + os_name: String, + kernel_version: String, + shell: String, + uptime: String, + desktop: String, + memory_usage: String, + storage: String, + colors: String, } -fn print_system_info(fields: &Fields) -> Result<(), Box> { - use crate::colors::COLORS; +fn print_system_info( + fields: &Fields, +) -> Result<(), Box> { + use crate::colors::COLORS; - let Fields { - user_info, - os_name, - kernel_version, - shell, - uptime, - desktop, - memory_usage, - storage, - colors, - } = fields; + let Fields { + user_info, + os_name, + kernel_version, + shell, + uptime, + desktop, + memory_usage, + storage, + colors, + } = fields; - let cyan = COLORS.cyan; - let blue = COLORS.blue; - let reset = COLORS.reset; - let system_info = format!(" - {cyan} ▟█▖ {blue}▝█▙ ▗█▛ {user_info} ~{reset} - {cyan} ▗▄▄▟██▄▄▄▄▄{blue}▝█▙█▛ {cyan}▖ {cyan} {blue}System{reset}  {os_name} - {cyan} ▀▀▀▀▀▀▀▀▀▀▀▘{blue}▝██ {cyan}▟█▖ {cyan} {blue}Kernel{reset}  {kernel_version} - {blue} ▟█▛ {blue}▝█▘{cyan}▟█▛ {cyan} {blue}Shell{reset}  {shell} - {blue}▟█████▛ {cyan}▟█████▛ {cyan} {blue}Uptime{reset}  {uptime} - {blue} ▟█▛{cyan}▗█▖ {cyan}▟█▛ {cyan} {blue}Desktop{reset}  {desktop} - {blue} ▝█▛ {cyan}██▖{blue}▗▄▄▄▄▄▄▄▄▄▄▄ {cyan} {blue}Memory{reset}  {memory_usage} - {blue} ▝ {cyan}▟█▜█▖{blue}▀▀▀▀▀██▛▀▀▘ {cyan}󱥎 {blue}Storage (/){reset}  {storage} - {cyan} ▟█▘ ▜█▖ {blue}▝█▛ {cyan} {blue}Colors{reset}  {colors}\n"); + let cyan = COLORS.cyan; + let blue = COLORS.blue; + let reset = COLORS.reset; + let system_info = format!(" + {cyan} ▟█▖ {blue}▝█▙ ▗█▛ {user_info} ~{reset} + {cyan} ▗▄▄▟██▄▄▄▄▄{blue}▝█▙█▛ {cyan}▖ {cyan} {blue}System{reset}  {os_name} + {cyan} ▀▀▀▀▀▀▀▀▀▀▀▘{blue}▝██ {cyan}▟█▖ {cyan} {blue}Kernel{reset}  {kernel_version} + {blue} ▟█▛ {blue}▝█▘{cyan}▟█▛ {cyan} {blue}Shell{reset}  {shell} + {blue}▟█████▛ {cyan}▟█████▛ {cyan} {blue}Uptime{reset}  {uptime} + {blue} ▟█▛{cyan}▗█▖ {cyan}▟█▛ {cyan} {blue}Desktop{reset}  {desktop} + {blue} ▝█▛ {cyan}██▖{blue}▗▄▄▄▄▄▄▄▄▄▄▄ {cyan} {blue}Memory{reset}  {memory_usage} + {blue} ▝ {cyan}▟█▜█▖{blue}▀▀▀▀▀██▛▀▀▘ {cyan}󱥎 {blue}Storage (/){reset}  {storage} + {cyan} ▟█▘ ▜█▖ {blue}▝█▛ {cyan} {blue}Colors{reset}  {colors}\n"); - Ok(stdout().write_all(system_info.as_bytes())?) + Ok(stdout().write_all(system_info.as_bytes())?) } From d43880073812e1d842115c91ae1771a5faaff88a Mon Sep 17 00:00:00 2001 From: NotAShelf Date: Mon, 17 Nov 2025 15:51:56 +0300 Subject: [PATCH 09/48] nix: streamline source filter; move RUSTFLAGS to env attrs Signed-off-by: NotAShelf Change-Id: I9761b908d05efd7dc10d53c54ca80fb26a6a6964 --- nix/package.nix | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/nix/package.nix b/nix/package.nix index 506aea3..5bb387c 100644 --- a/nix/package.nix +++ b/nix/package.nix @@ -9,23 +9,29 @@ inherit (toml) version; in rustPlatform.buildRustPackage.override {stdenv = stdenvAdapters.useMoldLinker llvm.stdenv;} { - RUSTFLAGS = "-C link-arg=-fuse-ld=mold"; - inherit pname version; - - src = builtins.path { - name = "${pname}-${version}"; - path = lib.sources.cleanSource ../.; - }; + src = let + fs = lib.fileset; + s = ../.; + in + fs.toSource { + root = s; + fileset = fs.unions [ + (fs.fileFilter (file: builtins.any file.hasExt ["rs"]) (s + /src)) + (s + /Cargo.lock) + (s + /Cargo.toml) + ]; + }; cargoLock.lockFile = ../Cargo.lock; enableParallelBuilding = true; + env.RUSTFLAGS = "-C link-arg=-fuse-ld=mold"; meta = { - description = "A microscopic fetch script in Rust, for NixOS systems"; + description = "Microscopic fetch script in Rust, for NixOS systems"; homepage = "https://github.com/NotAShelf/microfetch"; license = lib.licenses.gpl3Only; - maintainers = with lib.maintainers; [NotAShelf]; + maintainers = [lib.maintainers.NotAShelf]; mainProgram = "microfetch"; }; } From 4c22cf5d2abcb54923009a1d50828325c028596c Mon Sep 17 00:00:00 2001 From: NotAShelf Date: Mon, 17 Nov 2025 15:59:39 +0300 Subject: [PATCH 10/48] nix: use nightly rustfmt; add taplo Signed-off-by: NotAShelf Change-Id: Ie4a1b5a29166931aac006a44874374346a6a6964 --- nix/shell.nix | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/nix/shell.nix b/nix/shell.nix index 0577a0a..111b803 100644 --- a/nix/shell.nix +++ b/nix/shell.nix @@ -4,6 +4,7 @@ rustfmt, clippy, cargo, + taplo, rustc, rustPlatform, gnuplot, @@ -16,8 +17,9 @@ mkShell { rustc rust-analyzer-unwrapped - rustfmt + (rustfmt.override {asNightly = true;}) clippy + taplo gnuplot # For Criterion.rs plots ]; From af8031f9ec869d8c4ccf867453be9d2c000e259f Mon Sep 17 00:00:00 2001 From: NotAShelf Date: Mon, 17 Nov 2025 15:59:56 +0300 Subject: [PATCH 11/48] meta: selectively enable Clippy lint-groups projectwide Signed-off-by: NotAShelf Change-Id: Ic6eedd28e1ad48a67c988607dd45d7686a6a6964 --- Cargo.toml | 46 +++++++++++++++++++++++++++++++++++----------- 1 file changed, 35 insertions(+), 11 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index b7d692f..5ecade2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "microfetch" +name = "microfetch" version = "0.4.9" edition = "2024" @@ -12,28 +12,52 @@ name = "microfetch" path = "src/main.rs" [dependencies] -nix = { version = "0.30.1", default-features = false, features = ["fs", "hostname", "feature"] } libc = "0.2.175" +nix = { default-features = false, features = [ "fs", "hostname", "feature" ], version = "0.30.1" } [dev-dependencies] criterion = "0.7" [[bench]] -name = "benchmark" harness = false +name = "benchmark" [profile.dev] -opt-level = 3 +opt-level = 1 [profile.release] -strip = true -opt-level = "s" -lto = true codegen-units = 1 -panic = "abort" +lto = true +opt-level = "s" +panic = "abort" +strip = true [profile.profiler] -inherits = "release" -debug = true +debug = true +inherits = "release" split-debuginfo = "unpacked" -strip = "none" +strip = "none" + +[lints.clippy] +complexity = { level = "warn", priority = -1 } +nursery = { level = "warn", priority = -1 } +pedantic = { level = "warn", priority = -1 } +perf = { level = "warn", priority = -1 } +style = { level = "warn", priority = -1 } + +# The lint groups above enable some less-than-desirable rules, we should manually +# enable those to keep our sanity. +absolute_paths = "allow" +arbitrary_source_item_ordering = "allow" +implicit_return = "allow" +missing_docs_in_private_items = "allow" +non_ascii_literal = "allow" +pattern_type_mismatch = "allow" +print_stdout = "allow" +question_mark_used = "allow" +similar_names = "allow" +single_call_fn = "allow" +std_instead_of_core = "allow" +too_long_first_doc_paragraph = "allow" +too_many_lines = "allow" +unused_trait_names = "allow" From 9bd4c9a70aeb1865ab92da897c6303398816f761 Mon Sep 17 00:00:00 2001 From: NotAShelf Date: Mon, 17 Nov 2025 16:10:31 +0300 Subject: [PATCH 12/48] treewide: format with nightly rustfmt rules Signed-off-by: NotAShelf Change-Id: Ib8502372dafe2e970024f606b44825af6a6a6964 --- benches/benchmark.rs | 41 +++++++------ src/colors.rs | 88 +++++++++++++-------------- src/desktop.rs | 45 +++++++------- src/release.rs | 48 ++++++++------- src/system.rs | 139 +++++++++++++++++++++++-------------------- src/uptime.rs | 62 +++++++++---------- 6 files changed, 220 insertions(+), 203 deletions(-) diff --git a/benches/benchmark.rs b/benches/benchmark.rs index 03e49f4..33dc1b9 100644 --- a/benches/benchmark.rs +++ b/benches/benchmark.rs @@ -1,26 +1,31 @@ use criterion::{Criterion, criterion_group, criterion_main}; -use microfetch_lib::colors::print_dots; -use microfetch_lib::desktop::get_desktop_info; -use microfetch_lib::release::{get_os_pretty_name, get_system_info}; -use microfetch_lib::system::{ - get_memory_usage, get_root_disk_usage, get_shell, get_username_and_hostname, +use microfetch_lib::{ + colors::print_dots, + desktop::get_desktop_info, + release::{get_os_pretty_name, get_system_info}, + system::{ + get_memory_usage, + get_root_disk_usage, + get_shell, + get_username_and_hostname, + }, + uptime::get_current, }; -use microfetch_lib::uptime::get_current; fn main_benchmark(c: &mut Criterion) { - let utsname = nix::sys::utsname::uname().expect("lol"); - c.bench_function("user_info", |b| { - b.iter(|| get_username_and_hostname(&utsname)); - }); - c.bench_function("os_name", |b| b.iter(get_os_pretty_name)); - c.bench_function("kernel_version", |b| b.iter(|| get_system_info(&utsname))); - c.bench_function("shell", |b| b.iter(get_shell)); + let utsname = nix::sys::utsname::uname().expect("lol"); + c.bench_function("user_info", |b| { + b.iter(|| get_username_and_hostname(&utsname)); + }); + c.bench_function("os_name", |b| b.iter(get_os_pretty_name)); + c.bench_function("kernel_version", |b| b.iter(|| get_system_info(&utsname))); + c.bench_function("shell", |b| b.iter(get_shell)); - c.bench_function("desktop", |b| b.iter(get_desktop_info)); - c.bench_function("uptime", |b| b.iter(get_current)); - c.bench_function("memory_usage", |b| b.iter(get_memory_usage)); - c.bench_function("storage", |b| b.iter(get_root_disk_usage)); - c.bench_function("colors", |b| b.iter(print_dots)); + c.bench_function("desktop", |b| b.iter(get_desktop_info)); + c.bench_function("uptime", |b| b.iter(get_current)); + c.bench_function("memory_usage", |b| b.iter(get_memory_usage)); + c.bench_function("storage", |b| b.iter(get_root_disk_usage)); + c.bench_function("colors", |b| b.iter(print_dots)); } criterion_group!(benches, main_benchmark); diff --git a/src/colors.rs b/src/colors.rs index da613a5..733ec53 100644 --- a/src/colors.rs +++ b/src/colors.rs @@ -1,57 +1,57 @@ -use std::env; -use std::sync::LazyLock; +use std::{env, sync::LazyLock}; pub struct Colors { - pub reset: &'static str, - pub blue: &'static str, - pub cyan: &'static str, - pub green: &'static str, - pub yellow: &'static str, - pub red: &'static str, - pub magenta: &'static str, + pub reset: &'static str, + pub blue: &'static str, + pub cyan: &'static str, + pub green: &'static str, + pub yellow: &'static str, + pub red: &'static str, + pub magenta: &'static str, } impl Colors { - const fn new(is_no_color: bool) -> Self { - if is_no_color { - Self { - reset: "", - blue: "", - cyan: "", - green: "", - yellow: "", - red: "", - magenta: "", - } - } else { - Self { - reset: "\x1b[0m", - blue: "\x1b[34m", - cyan: "\x1b[36m", - green: "\x1b[32m", - yellow: "\x1b[33m", - red: "\x1b[31m", - magenta: "\x1b[35m", - } - } + const fn new(is_no_color: bool) -> Self { + if is_no_color { + Self { + reset: "", + blue: "", + cyan: "", + green: "", + yellow: "", + red: "", + magenta: "", + } + } else { + Self { + reset: "\x1b[0m", + blue: "\x1b[34m", + cyan: "\x1b[36m", + green: "\x1b[32m", + yellow: "\x1b[33m", + red: "\x1b[31m", + magenta: "\x1b[35m", + } } + } } pub static COLORS: LazyLock = LazyLock::new(|| { - // check for NO_COLOR once at startup - let is_no_color = env::var("NO_COLOR").is_ok(); - Colors::new(is_no_color) + // check for NO_COLOR once at startup + let is_no_color = env::var("NO_COLOR").is_ok(); + Colors::new(is_no_color) }); +#[must_use] pub fn print_dots() -> String { - format!( - "{} {} {} {} {} {} {}", - COLORS.blue, - COLORS.cyan, - COLORS.green, - COLORS.yellow, - COLORS.red, - COLORS.magenta, - COLORS.reset, - ) + format!( + "{} {} {} {} {} {} {}", + COLORS.blue, + COLORS.cyan, + COLORS.green, + COLORS.yellow, + COLORS.red, + COLORS.magenta, + COLORS.reset, + ) } diff --git a/src/desktop.rs b/src/desktop.rs index f35cd2a..e5e6039 100644 --- a/src/desktop.rs +++ b/src/desktop.rs @@ -1,28 +1,29 @@ +#[must_use] pub fn get_desktop_info() -> String { - // Retrieve the environment variables and handle Result types - let desktop_env = std::env::var("XDG_CURRENT_DESKTOP"); - let display_backend_result = std::env::var("XDG_SESSION_TYPE"); + // Retrieve the environment variables and handle Result types + let desktop_env = std::env::var("XDG_CURRENT_DESKTOP"); + let display_backend_result = std::env::var("XDG_SESSION_TYPE"); - // Capitalize the first letter of the display backend value - let mut display_backend = display_backend_result.unwrap_or_default(); - if let Some(c) = display_backend.as_mut_str().get_mut(0..1) { - c.make_ascii_uppercase(); - } + // Capitalize the first letter of the display backend value + let mut display_backend = display_backend_result.unwrap_or_default(); + if let Some(c) = display_backend.as_mut_str().get_mut(0..1) { + c.make_ascii_uppercase(); + } - // Trim "none+" from the start of desktop_env if present - // Use "Unknown" if desktop_env is empty or has an error - let desktop_env = match desktop_env { - Err(_) => "Unknown".to_string(), - Ok(s) => s.trim_start_matches("none+").to_string(), - }; + // Trim "none+" from the start of desktop_env if present + // Use "Unknown" if desktop_env is empty or has an error + let desktop_env = match desktop_env { + Err(_) => "Unknown".to_owned(), + Ok(s) => s.trim_start_matches("none+").to_owned(), + }; - // Handle the case where display_backend might be empty after capitalization - let display_backend = if display_backend.is_empty() { - "Unknown" - } else { - &display_backend - } - .to_string(); + // Handle the case where display_backend might be empty after capitalization + let display_backend = if display_backend.is_empty() { + "Unknown" + } else { + &display_backend + } + .to_owned(); - format!("{desktop_env} ({display_backend})") + format!("{desktop_env} ({display_backend})") } diff --git a/src/release.rs b/src/release.rs index 6d1b20f..20a9b81 100644 --- a/src/release.rs +++ b/src/release.rs @@ -1,33 +1,35 @@ -use nix::sys::utsname::UtsName; use std::{ - fs::File, - io::{self, BufRead, BufReader}, + fs::File, + io::{self, BufRead, BufReader}, }; +use nix::sys::utsname::UtsName; + +#[must_use] pub fn get_system_info(utsname: &UtsName) -> String { - format!( - "{} {} ({})", - utsname.sysname().to_str().unwrap_or("Unknown"), - utsname.release().to_str().unwrap_or("Unknown"), - utsname.machine().to_str().unwrap_or("Unknown") - ) + format!( + "{} {} ({})", + utsname.sysname().to_str().unwrap_or("Unknown"), + utsname.release().to_str().unwrap_or("Unknown"), + utsname.machine().to_str().unwrap_or("Unknown") + ) } pub fn get_os_pretty_name() -> Result { - let file = File::open("/etc/os-release")?; - let reader = BufReader::new(file); + let file = File::open("/etc/os-release")?; + let reader = BufReader::new(file); - for line in reader.lines() { - let line = line?; - if let Some(pretty_name) = line.strip_prefix("PRETTY_NAME=") { - if let Some(trimmed) = pretty_name - .strip_prefix('"') - .and_then(|s| s.strip_suffix('"')) - { - return Ok(trimmed.to_string()); - } - return Ok(pretty_name.to_string()); - } + for line in reader.lines() { + let line = line?; + if let Some(pretty_name) = line.strip_prefix("PRETTY_NAME=") { + if let Some(trimmed) = pretty_name + .strip_prefix('"') + .and_then(|s| s.strip_suffix('"')) + { + return Ok(trimmed.to_owned()); + } + return Ok(pretty_name.to_owned()); } - Ok("Unknown".to_string()) + } + Ok("Unknown".to_owned()) } diff --git a/src/system.rs b/src/system.rs index aed8752..1cfd12c 100644 --- a/src/system.rs +++ b/src/system.rs @@ -1,87 +1,96 @@ -use crate::colors::COLORS; -use nix::sys::{statvfs::statvfs, utsname::UtsName}; use std::{ - env, - fs::File, - io::{self, Read}, + env, + fs::File, + io::{self, Read}, }; +use nix::sys::{statvfs::statvfs, utsname::UtsName}; + +use crate::colors::COLORS; + +#[must_use] pub fn get_username_and_hostname(utsname: &UtsName) -> String { - let username = env::var("USER").unwrap_or_else(|_| "unknown_user".to_string()); - let hostname = utsname - .nodename() - .to_str() - .unwrap_or("unknown_host") - .to_string(); - format!( - "{yellow}{username}{red}@{green}{hostname}{reset}", - yellow = COLORS.yellow, - red = COLORS.red, - green = COLORS.green, - reset = COLORS.reset, - ) + let username = env::var("USER").unwrap_or_else(|_| "unknown_user".to_owned()); + let hostname = utsname + .nodename() + .to_str() + .unwrap_or("unknown_host") + .to_owned(); + format!( + "{yellow}{username}{red}@{green}{hostname}{reset}", + yellow = COLORS.yellow, + red = COLORS.red, + green = COLORS.green, + reset = COLORS.reset, + ) } +#[must_use] pub fn get_shell() -> String { - let shell_path = env::var("SHELL").unwrap_or_else(|_| "unknown_shell".to_string()); - let shell_name = shell_path.rsplit('/').next().unwrap_or("unknown_shell"); - shell_name.to_string() + let shell_path = + env::var("SHELL").unwrap_or_else(|_| "unknown_shell".to_owned()); + let shell_name = shell_path.rsplit('/').next().unwrap_or("unknown_shell"); + shell_name.to_owned() } pub fn get_root_disk_usage() -> Result { - let vfs = statvfs("/")?; - let block_size = vfs.block_size() as u64; - let total_blocks = vfs.blocks(); - let available_blocks = vfs.blocks_available(); + let vfs = statvfs("/")?; + let block_size = vfs.block_size() as u64; + let total_blocks = vfs.blocks(); + let available_blocks = vfs.blocks_available(); - let total_size = block_size * total_blocks; - let used_size = total_size - (block_size * available_blocks); + let total_size = block_size * total_blocks; + let used_size = total_size - (block_size * available_blocks); - let total_size = total_size as f64 / (1024.0 * 1024.0 * 1024.0); - let used_size = used_size as f64 / (1024.0 * 1024.0 * 1024.0); - let usage = (used_size / total_size) * 100.0; + let total_size = total_size as f64 / (1024.0 * 1024.0 * 1024.0); + let used_size = used_size as f64 / (1024.0 * 1024.0 * 1024.0); + let usage = (used_size / total_size) * 100.0; - Ok(format!( - "{used_size:.2} GiB / {total_size:.2} GiB ({cyan}{usage:.0}%{reset})", - cyan = COLORS.cyan, - reset = COLORS.reset, - )) + Ok(format!( + "{used_size:.2} GiB / {total_size:.2} GiB ({cyan}{usage:.0}%{reset})", + cyan = COLORS.cyan, + reset = COLORS.reset, + )) } pub fn get_memory_usage() -> Result { - fn parse_memory_info() -> Result<(f64, f64), io::Error> { - let mut total_memory_kb = 0.0; - let mut available_memory_kb = 0.0; - let mut meminfo = String::with_capacity(2048); + fn parse_memory_info() -> Result<(f64, f64), io::Error> { + let mut total_memory_kb = 0.0; + let mut available_memory_kb = 0.0; + let mut meminfo = String::with_capacity(2048); - File::open("/proc/meminfo")?.read_to_string(&mut meminfo)?; + File::open("/proc/meminfo")?.read_to_string(&mut meminfo)?; - for line in meminfo.lines() { - let mut split = line.split_whitespace(); - match split.next().unwrap_or_default() { - "MemTotal:" => total_memory_kb = split.next().unwrap_or("0").parse().unwrap_or(0.0), - "MemAvailable:" => { - available_memory_kb = split.next().unwrap_or("0").parse().unwrap_or(0.0); - // MemTotal comes before MemAvailable, stop parsing - break; - } - _ => (), - } - } - - let total_memory_gb = total_memory_kb / 1024.0 / 1024.0; - let available_memory_gb = available_memory_kb / 1024.0 / 1024.0; - let used_memory_gb = total_memory_gb - available_memory_gb; - - Ok((used_memory_gb, total_memory_gb)) + for line in meminfo.lines() { + let mut split = line.split_whitespace(); + match split.next().unwrap_or_default() { + "MemTotal:" => { + total_memory_kb = split.next().unwrap_or("0").parse().unwrap_or(0.0) + }, + "MemAvailable:" => { + available_memory_kb = + split.next().unwrap_or("0").parse().unwrap_or(0.0); + // MemTotal comes before MemAvailable, stop parsing + break; + }, + _ => (), + } } - let (used_memory, total_memory) = parse_memory_info()?; - let percentage_used = (used_memory / total_memory * 100.0).round() as u64; + let total_memory_gb = total_memory_kb / 1024.0 / 1024.0; + let available_memory_gb = available_memory_kb / 1024.0 / 1024.0; + let used_memory_gb = total_memory_gb - available_memory_gb; - Ok(format!( - "{used_memory:.2} GiB / {total_memory:.2} GiB ({cyan}{percentage_used}%{reset})", - cyan = COLORS.cyan, - reset = COLORS.reset, - )) + Ok((used_memory_gb, total_memory_gb)) + } + + let (used_memory, total_memory) = parse_memory_info()?; + let percentage_used = (used_memory / total_memory * 100.0).round() as u64; + + Ok(format!( + "{used_memory:.2} GiB / {total_memory:.2} GiB \ + ({cyan}{percentage_used}%{reset})", + cyan = COLORS.cyan, + reset = COLORS.reset, + )) } diff --git a/src/uptime.rs b/src/uptime.rs index 496052a..87b95cf 100644 --- a/src/uptime.rs +++ b/src/uptime.rs @@ -1,40 +1,40 @@ use std::{io, mem::MaybeUninit}; pub fn get_current() -> Result { - let uptime_seconds = { - let mut info = MaybeUninit::uninit(); - if unsafe { libc::sysinfo(info.as_mut_ptr()) } != 0 { - return Err(io::Error::last_os_error()); - } - unsafe { info.assume_init().uptime as u64 } - }; + let uptime_seconds = { + let mut info = MaybeUninit::uninit(); + if unsafe { libc::sysinfo(info.as_mut_ptr()) } != 0 { + return Err(io::Error::last_os_error()); + } + unsafe { info.assume_init().uptime as u64 } + }; - let days = uptime_seconds / 86400; - let hours = (uptime_seconds / 3600) % 24; - let minutes = (uptime_seconds / 60) % 60; + let days = uptime_seconds / 86400; + let hours = (uptime_seconds / 3600) % 24; + let minutes = (uptime_seconds / 60) % 60; - let mut result = String::with_capacity(32); - if days > 0 { - result.push_str(&days.to_string()); - result.push_str(if days == 1 { " day" } else { " days" }); + let mut result = String::with_capacity(32); + if days > 0 { + result.push_str(&days.to_string()); + result.push_str(if days == 1 { " day" } else { " days" }); + } + if hours > 0 { + if !result.is_empty() { + result.push_str(", "); } - if hours > 0 { - if !result.is_empty() { - result.push_str(", "); - } - result.push_str(&hours.to_string()); - result.push_str(if hours == 1 { " hour" } else { " hours" }); - } - if minutes > 0 { - if !result.is_empty() { - result.push_str(", "); - } - result.push_str(&minutes.to_string()); - result.push_str(if minutes == 1 { " minute" } else { " minutes" }); - } - if result.is_empty() { - result.push_str("less than a minute"); + result.push_str(&hours.to_string()); + result.push_str(if hours == 1 { " hour" } else { " hours" }); + } + if minutes > 0 { + if !result.is_empty() { + result.push_str(", "); } + result.push_str(&minutes.to_string()); + result.push_str(if minutes == 1 { " minute" } else { " minutes" }); + } + if result.is_empty() { + result.push_str("less than a minute"); + } - Ok(result) + Ok(result) } From 2a6fe2a3f1444fd1eba285bbefe0439e74dd033e Mon Sep 17 00:00:00 2001 From: NotAShelf Date: Mon, 17 Nov 2025 17:39:21 +0300 Subject: [PATCH 13/48] treewide: set up Hotpath for benchmarking individual allocations Signed-off-by: NotAShelf Change-Id: I0351e5753996e6d0391fc9e2f329878a6a6a6964 --- Cargo.lock | 1055 +++++++++++++++++++++++++++++++++++++++++++++++- Cargo.toml | 11 +- src/colors.rs | 1 + src/desktop.rs | 1 + src/main.rs | 2 + src/release.rs | 2 + src/system.rs | 5 + src/uptime.rs | 1 + 8 files changed, 1075 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 25e640c..40f74fc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,12 @@ # It is not intended for manual editing. version = 4 +[[package]] +name = "adler2" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" + [[package]] name = "aho-corasick" version = "1.1.3" @@ -17,18 +23,80 @@ version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" +[[package]] +name = "anstream" +version = "0.6.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43d5b281e737544384e969a5ccad3f1cdd24b48086a0fc1b2a5262a26b8f4f4a" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + [[package]] name = "anstyle" version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9" +[[package]] +name = "anstyle-parse" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc" +dependencies = [ + "windows-sys 0.61.2", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d" +dependencies = [ + "anstyle", + "once_cell_polyfill", + "windows-sys 0.61.2", +] + +[[package]] +name = "arc-swap" +version = "1.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69f7f8c3906b62b754cd5326047894316021dcfe5a194c8ea52bdd94934a3457" + +[[package]] +name = "ascii" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d92bec98840b8f03a5ff5413de5293bfcd8bf96467cf5452609f939ec6f5de16" + [[package]] name = "autocfg" version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + [[package]] name = "bitflags" version = "2.9.0" @@ -41,12 +109,34 @@ version = "3.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf" +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "bytes" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b35204fbdc0b3f4446b89fc1ac2cf84a8a68971995d0bf2e925ec7cd960f9cb3" + [[package]] name = "cast" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" +[[package]] +name = "cc" +version = "1.2.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b97463e1064cb1b1c1384ad0a0b9c8abd0988e2a91f52606c80ef14aadb63e36" +dependencies = [ + "find-msvc-tools", + "shlex", +] + [[package]] name = "cfg-if" version = "1.0.0" @@ -59,6 +149,12 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" +[[package]] +name = "chunked_transfer" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e4de3bc4ea267985becf712dc6d9eed8b04c953b3fcfb339ebc87acd9804901" + [[package]] name = "ciborium" version = "0.2.2" @@ -93,6 +189,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d8aa86934b44c19c50f87cc2790e19f54f7a67aedb64101c2e1a2e5ecfb73944" dependencies = [ "clap_builder", + "clap_derive", ] [[package]] @@ -101,8 +198,22 @@ version = "4.5.35" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2414dbb2dd0695280da6ea9261e327479e9d37b0630f6b53ba2a11c60c679fd9" dependencies = [ + "anstream", "anstyle", "clap_lex", + "strsim", +] + +[[package]] +name = "clap_derive" +version = "4.5.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09176aae279615badda0765c0c0b3f6ed53f4709118af73cf4655d85d1530cd7" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn", ] [[package]] @@ -111,6 +222,59 @@ version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6" +[[package]] +name = "colorchoice" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" + +[[package]] +name = "colored" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fde0e0ec90c9dfb3b4b1a0891a7dcd0e2bffde2f7efed5fe7c9bb00e5bfb915e" +dependencies = [ + "windows-sys 0.59.0", +] + +[[package]] +name = "cookie" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ddef33a339a91ea89fb53151bd0a4689cfce27055c291dfa69945475d22c747" +dependencies = [ + "percent-encoding", + "time", + "version_check", +] + +[[package]] +name = "cookie_store" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fc4bff745c9b4c7fb1e97b25d13153da2bc7796260141df62378998d070207f" +dependencies = [ + "cookie", + "document-features", + "idna", + "indexmap", + "log", + "serde", + "serde_derive", + "serde_json", + "time", + "url", +] + +[[package]] +name = "crc32fast" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511" +dependencies = [ + "cfg-if", +] + [[package]] name = "criterion" version = "0.7.0" @@ -144,6 +308,15 @@ dependencies = [ "itertools", ] +[[package]] +name = "crossbeam-channel" +version = "0.5.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82b8f8f868b36967f9606790d1903570de9ceaf870a7bf9fbbd3016d636a2cb2" +dependencies = [ + "crossbeam-utils", +] + [[package]] name = "crossbeam-deque" version = "0.8.6" @@ -175,12 +348,126 @@ version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "43da5946c66ffcc7745f48db692ffbb10a83bfe0afd96235c5c2a4fb23994929" +[[package]] +name = "deranged" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ececcb659e7ba858fb4f10388c250a7252eb0a27373f1a72b8748afdd248e587" +dependencies = [ + "powerfmt", +] + +[[package]] +name = "dirs-next" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b98cf8ebf19c3d1b223e151f99a4f9f0690dca41414773390fc824184ac833e1" +dependencies = [ + "cfg-if", + "dirs-sys-next", +] + +[[package]] +name = "dirs-sys-next" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d" +dependencies = [ + "libc", + "redox_users", + "winapi", +] + +[[package]] +name = "displaydoc" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "document-features" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4b8a88685455ed29a21542a33abd9cb6510b6b129abadabdcef0f4c55bc8f61" +dependencies = [ + "litrs", +] + [[package]] name = "either" version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" +[[package]] +name = "encode_unicode" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34aa73646ffb006b8f5147f3dc182bd4bcb190227ce861fc4a4844bf8e3cb2c0" + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "eyre" +version = "0.6.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cd915d99f24784cdc19fd37ef22b97e3ff0ae756c7e492e9fbfe897d61e2aec" +dependencies = [ + "indenter", + "once_cell", +] + +[[package]] +name = "find-msvc-tools" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a3076410a55c90011c298b04d0cfa770b00fa04e1e3c97d3f6c9de105a03844" + +[[package]] +name = "flate2" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfe33edd8e85a12a67454e37f8c75e730830d83e313556ab9ebf9ee7fbeb3bfb" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "form_urlencoded" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "getrandom" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + [[package]] name = "half" version = "2.6.0" @@ -191,6 +478,227 @@ dependencies = [ "crunchy", ] +[[package]] +name = "hashbrown" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5419bdc4f6a9207fbeba6d11b604d481addf78ecd10c11ad51e76c2f6482748d" + +[[package]] +name = "hdrhistogram" +version = "7.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "765c9198f173dd59ce26ff9f95ef0aafd0a0fe01fb9d72841bc5066a4c06511d" +dependencies = [ + "byteorder", + "num-traits", +] + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "hermit-abi" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c" + +[[package]] +name = "hotpath" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef44238d7007bbd3c41ce7dbf3d7a4fb224bbf70d6cf3162479138dda0cd6a9e" +dependencies = [ + "arc-swap", + "base64", + "cfg-if", + "clap", + "colored", + "crossbeam-channel", + "eyre", + "hdrhistogram", + "hotpath-macros", + "prettytable-rs", + "quanta", + "serde", + "serde_json", + "tiny_http", + "tokio", + "ureq", +] + +[[package]] +name = "hotpath-macros" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e7e3443c6f4e5cc69e6b701f7bfca8a5502f0080e94629413346e7b4518d730" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "http" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4a85d31aea989eead29a3aaf9e1115a180df8282431156e533de47660892565" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "httparse" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" + +[[package]] +name = "httpdate" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" + +[[package]] +name = "icu_collections" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c6b649701667bbe825c3b7e6388cb521c23d88644678e83c0c4d0a621a34b43" +dependencies = [ + "displaydoc", + "potential_utf", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_locale_core" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edba7861004dd3714265b4db54a3c390e880ab658fec5f7db895fae2046b5bb6" +dependencies = [ + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_normalizer" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f6c8828b67bf8908d82127b2054ea1b4427ff0230ee9141c54251934ab1b599" +dependencies = [ + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7aedcccd01fc5fe81e6b489c15b247b8b0690feb23304303a9e560f37efc560a" + +[[package]] +name = "icu_properties" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e93fcd3157766c0c8da2f8cff6ce651a31f0810eaa1c51ec363ef790bbb5fb99" +dependencies = [ + "icu_collections", + "icu_locale_core", + "icu_properties_data", + "icu_provider", + "zerotrie", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02845b3647bb045f1100ecd6480ff52f34c35f82d9880e029d329c21d1054899" + +[[package]] +name = "icu_provider" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85962cf0ce02e1e0a629cc34e7ca3e373ce20dda4c4d7294bbd0bf1fdb59e614" +dependencies = [ + "displaydoc", + "icu_locale_core", + "writeable", + "yoke", + "zerofrom", + "zerotrie", + "zerovec", +] + +[[package]] +name = "idna" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de" +dependencies = [ + "idna_adapter", + "smallvec", + "utf8_iter", +] + +[[package]] +name = "idna_adapter" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344" +dependencies = [ + "icu_normalizer", + "icu_properties", +] + +[[package]] +name = "indenter" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "964de6e86d545b246d84badc0fef527924ace5134f30641c203ef52ba83f58d5" + +[[package]] +name = "indexmap" +version = "2.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6717a8d2a5a929a1a2eb43a12812498ed141a0bcfb7e8f7844fbdbe4303bba9f" +dependencies = [ + "equivalent", + "hashbrown", +] + +[[package]] +name = "is-terminal" +version = "0.4.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3640c1c38b8e4e43584d8df18be5fc6b0aa314ce6ebf51b53313d4306cca8e46" +dependencies = [ + "hermit-abi", + "libc", + "windows-sys 0.59.0", +] + +[[package]] +name = "is_terminal_polyfill" +version = "1.70.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695" + [[package]] name = "itertools" version = "0.13.0" @@ -216,12 +724,40 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + [[package]] name = "libc" version = "0.2.175" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a82ae493e598baaea5209805c49bbf2ea7de956d50d7da0da1164f9c6d28543" +[[package]] +name = "libredox" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "416f7e718bdb06000964960ffa43b4335ad4012ae8b99060261aa4a8088d5ccb" +dependencies = [ + "bitflags", + "libc", +] + +[[package]] +name = "litemap" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6373607a59f0be73a39b6fe456b8192fcc3585f602af20751600e974dd455e77" + +[[package]] +name = "litrs" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11d3d7f243d5c5a8b9bb5d6dd2b1602c0cb0b9db1621bafc7ed66e35ff9fe092" + [[package]] name = "log" version = "0.4.27" @@ -239,10 +775,21 @@ name = "microfetch" version = "0.4.9" dependencies = [ "criterion", + "hotpath", "libc", "nix", ] +[[package]] +name = "miniz_oxide" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" +dependencies = [ + "adler2", + "simd-adler32", +] + [[package]] name = "nix" version = "0.30.1" @@ -255,6 +802,12 @@ dependencies = [ "libc", ] +[[package]] +name = "num-conv" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" + [[package]] name = "num-traits" version = "0.2.19" @@ -270,12 +823,30 @@ version = "1.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" +[[package]] +name = "once_cell_polyfill" +version = "1.70.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe" + [[package]] name = "oorandom" version = "11.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d6790f58c7ff633d8771f42965289203411a5e5c68388703c06e14f24770b41e" +[[package]] +name = "percent-encoding" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" + +[[package]] +name = "pin-project-lite" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" + [[package]] name = "plotters" version = "0.3.7" @@ -304,6 +875,34 @@ dependencies = [ "plotters-backend", ] +[[package]] +name = "potential_utf" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b73949432f5e2a09657003c25bca5e19a0e9c84f8058ca374f49e0ebe605af77" +dependencies = [ + "zerovec", +] + +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + +[[package]] +name = "prettytable-rs" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eea25e07510aa6ab6547308ebe3c036016d162b8da920dbb079e3ba8acf3d95a" +dependencies = [ + "encode_unicode", + "is-terminal", + "lazy_static", + "term", + "unicode-width", +] + [[package]] name = "proc-macro2" version = "1.0.94" @@ -313,6 +912,21 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "quanta" +version = "0.12.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3ab5a9d756f0d97bdc89019bd2e4ea098cf9cde50ee7564dde6b81ccc8f06c7" +dependencies = [ + "crossbeam-utils", + "libc", + "once_cell", + "raw-cpuid", + "wasi", + "web-sys", + "winapi", +] + [[package]] name = "quote" version = "1.0.40" @@ -322,6 +936,15 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "raw-cpuid" +version = "11.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "498cd0dc59d73224351ee52a95fee0f1a617a2eae0e7d9d720cc622c73a54186" +dependencies = [ + "bitflags", +] + [[package]] name = "rayon" version = "1.10.0" @@ -342,6 +965,17 @@ dependencies = [ "crossbeam-utils", ] +[[package]] +name = "redox_users" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43" +dependencies = [ + "getrandom", + "libredox", + "thiserror", +] + [[package]] name = "regex" version = "1.11.1" @@ -371,6 +1005,55 @@ version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" +[[package]] +name = "ring" +version = "0.17.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" +dependencies = [ + "cc", + "cfg-if", + "getrandom", + "libc", + "untrusted", + "windows-sys 0.52.0", +] + +[[package]] +name = "rustls" +version = "0.23.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "533f54bc6a7d4f647e46ad909549eda97bf5afc1585190ef692b4286b198bd8f" +dependencies = [ + "log", + "once_cell", + "ring", + "rustls-pki-types", + "rustls-webpki", + "subtle", + "zeroize", +] + +[[package]] +name = "rustls-pki-types" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94182ad936a0c91c324cd46c6511b9510ed16af436d7b5bab34beab0afd55f7a" +dependencies = [ + "zeroize", +] + +[[package]] +name = "rustls-webpki" +version = "0.103.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ffdfa2f5286e2247234e03f680868ac2815974dc39e00ea15adc445d0aafe52" +dependencies = [ + "ring", + "rustls-pki-types", + "untrusted", +] + [[package]] name = "rustversion" version = "1.0.20" @@ -424,6 +1107,42 @@ dependencies = [ "serde", ] +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "simd-adler32" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" + +[[package]] +name = "smallvec" +version = "1.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" + +[[package]] +name = "stable_deref_trait" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" + +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + [[package]] name = "syn" version = "2.0.100" @@ -435,6 +1154,101 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "synstructure" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "term" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c59df8ac95d96ff9bede18eb7300b0fda5e5d8d90960e76f8e14ae765eedbf1f" +dependencies = [ + "dirs-next", + "rustversion", + "winapi", +] + +[[package]] +name = "thiserror" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "time" +version = "0.3.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e7d9e3bb61134e77bde20dd4825b97c010155709965fedf0f49bb138e52a9d" +dependencies = [ + "deranged", + "itoa", + "num-conv", + "powerfmt", + "serde", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40868e7c1d2f0b8d73e4a8c7f0ff63af4f6d19be117e90bd73eb1d62cf831c6b" + +[[package]] +name = "time-macros" +version = "0.2.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30cfb0125f12d9c277f35663a0a33f8c30190f4e4574868a330595412d34ebf3" +dependencies = [ + "num-conv", + "time-core", +] + +[[package]] +name = "tiny_http" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "389915df6413a2e74fb181895f933386023c71110878cd0825588928e64cdc82" +dependencies = [ + "ascii", + "chunked_transfer", + "httpdate", + "log", +] + +[[package]] +name = "tinystr" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42d3e9c45c09de15d06dd8acf5f4e0e399e85927b7f00711024eb7ae10fa4869" +dependencies = [ + "displaydoc", + "zerovec", +] + [[package]] name = "tinytemplate" version = "1.2.1" @@ -445,12 +1259,101 @@ dependencies = [ "serde_json", ] +[[package]] +name = "tokio" +version = "1.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff360e02eab121e0bc37a2d3b4d4dc622e6eda3a8e5253d5435ecf5bd4c68408" +dependencies = [ + "pin-project-lite", +] + [[package]] name = "unicode-ident" version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" +[[package]] +name = "unicode-width" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" + +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + +[[package]] +name = "ureq" +version = "3.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d39cb1dbab692d82a977c0392ffac19e188bd9186a9f32806f0aaa859d75585a" +dependencies = [ + "base64", + "cookie_store", + "flate2", + "log", + "percent-encoding", + "rustls", + "rustls-pki-types", + "serde", + "serde_json", + "ureq-proto", + "utf-8", + "webpki-roots", +] + +[[package]] +name = "ureq-proto" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60b4531c118335662134346048ddb0e54cc86bd7e81866757873055f0e38f5d2" +dependencies = [ + "base64", + "http", + "httparse", + "log", +] + +[[package]] +name = "url" +version = "2.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08bc136a29a3d1758e07a9cca267be308aeebf5cfd5a10f3f67ab2097683ef5b" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", + "serde", +] + +[[package]] +name = "utf-8" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" + +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + +[[package]] +name = "utf8parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + [[package]] name = "walkdir" version = "2.5.0" @@ -461,6 +1364,12 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "wasi" +version = "0.11.1+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" + [[package]] name = "wasm-bindgen" version = "0.2.100" @@ -529,13 +1438,59 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "webpki-roots" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2878ef029c47c6e8cf779119f20fcf52bde7ad42a731b2a304bc221df17571e" +dependencies = [ + "rustls-pki-types", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + [[package]] name = "winapi-util" version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" dependencies = [ - "windows-sys", + "windows-sys 0.59.0", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets", ] [[package]] @@ -547,6 +1502,15 @@ dependencies = [ "windows-targets", ] +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link", +] + [[package]] name = "windows-targets" version = "0.52.6" @@ -610,3 +1574,92 @@ name = "windows_x86_64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "writeable" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9edde0db4769d2dc68579893f2306b26c6ecfbe0ef499b013d731b7b9247e0b9" + +[[package]] +name = "yoke" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72d6e5c6afb84d73944e5cedb052c4680d5657337201555f9f2a16b7406d4954" +dependencies = [ + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b659052874eb698efe5b9e8cf382204678a0086ebf46982b79d6ca3182927e5d" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] +name = "zerofrom" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] +name = "zeroize" +version = "1.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0" + +[[package]] +name = "zerotrie" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a59c17a5562d507e4b54960e8569ebee33bee890c70aa3fe7b97e85a9fd7851" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", +] + +[[package]] +name = "zerovec" +version = "0.11.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c28719294829477f525be0186d13efa9a3c602f7ec202ca9e353d310fb9a002" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eadce39539ca5cb3985590102671f2567e659fca9666581ad3411d59207951f3" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] diff --git a/Cargo.toml b/Cargo.toml index 5ecade2..6af4917 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,12 +12,19 @@ name = "microfetch" path = "src/main.rs" [dependencies] -libc = "0.2.175" -nix = { default-features = false, features = [ "fs", "hostname", "feature" ], version = "0.30.1" } +hotpath = { optional = true, version = "0.6" } +libc = "0.2.175" +nix = { default-features = false, features = [ "fs", "hostname", "feature" ], version = "0.30.1" } [dev-dependencies] criterion = "0.7" +[features] +hotpath = [ "dep:hotpath", "hotpath/hotpath" ] +hotpath-alloc-bytes-total = [ "hotpath/hotpath-alloc-bytes-total" ] +hotpath-alloc-count-total = [ "hotpath/hotpath-alloc-count-total" ] +hotpath-off = [ "hotpath/hotpath-off" ] + [[bench]] harness = false name = "benchmark" diff --git a/src/colors.rs b/src/colors.rs index 733ec53..53b12f6 100644 --- a/src/colors.rs +++ b/src/colors.rs @@ -43,6 +43,7 @@ pub static COLORS: LazyLock = LazyLock::new(|| { }); #[must_use] +#[cfg_attr(feature = "hotpath", hotpath::measure)] pub fn print_dots() -> String { format!( "{} {} {} {} {} {} {}", diff --git a/src/desktop.rs b/src/desktop.rs index e5e6039..7d3733a 100644 --- a/src/desktop.rs +++ b/src/desktop.rs @@ -1,4 +1,5 @@ #[must_use] +#[cfg_attr(feature = "hotpath", hotpath::measure)] pub fn get_desktop_info() -> String { // Retrieve the environment variables and handle Result types let desktop_env = std::env::var("XDG_CURRENT_DESKTOP"); diff --git a/src/main.rs b/src/main.rs index 2138a44..5ba7810 100644 --- a/src/main.rs +++ b/src/main.rs @@ -19,6 +19,7 @@ use crate::{ uptime::get_current, }; +#[cfg_attr(feature = "hotpath", hotpath::main)] fn main() -> Result<(), Box> { if Some("--version") == std::env::args().nth(1).as_deref() { println!("Microfetch {}", env!("CARGO_PKG_VERSION")); @@ -56,6 +57,7 @@ struct Fields { colors: String, } +#[cfg_attr(feature = "hotpath", hotpath::measure)] fn print_system_info( fields: &Fields, ) -> Result<(), Box> { diff --git a/src/release.rs b/src/release.rs index 20a9b81..2f1338c 100644 --- a/src/release.rs +++ b/src/release.rs @@ -6,6 +6,7 @@ use std::{ use nix::sys::utsname::UtsName; #[must_use] +#[cfg_attr(feature = "hotpath", hotpath::measure)] pub fn get_system_info(utsname: &UtsName) -> String { format!( "{} {} ({})", @@ -15,6 +16,7 @@ pub fn get_system_info(utsname: &UtsName) -> String { ) } +#[cfg_attr(feature = "hotpath", hotpath::measure)] pub fn get_os_pretty_name() -> Result { let file = File::open("/etc/os-release")?; let reader = BufReader::new(file); diff --git a/src/system.rs b/src/system.rs index 1cfd12c..f90c912 100644 --- a/src/system.rs +++ b/src/system.rs @@ -9,6 +9,7 @@ use nix::sys::{statvfs::statvfs, utsname::UtsName}; use crate::colors::COLORS; #[must_use] +#[cfg_attr(feature = "hotpath", hotpath::measure)] pub fn get_username_and_hostname(utsname: &UtsName) -> String { let username = env::var("USER").unwrap_or_else(|_| "unknown_user".to_owned()); let hostname = utsname @@ -26,6 +27,7 @@ pub fn get_username_and_hostname(utsname: &UtsName) -> String { } #[must_use] +#[cfg_attr(feature = "hotpath", hotpath::measure)] pub fn get_shell() -> String { let shell_path = env::var("SHELL").unwrap_or_else(|_| "unknown_shell".to_owned()); @@ -33,6 +35,7 @@ pub fn get_shell() -> String { shell_name.to_owned() } +#[cfg_attr(feature = "hotpath", hotpath::measure)] pub fn get_root_disk_usage() -> Result { let vfs = statvfs("/")?; let block_size = vfs.block_size() as u64; @@ -53,7 +56,9 @@ pub fn get_root_disk_usage() -> Result { )) } +#[cfg_attr(feature = "hotpath", hotpath::measure)] pub fn get_memory_usage() -> Result { + #[cfg_attr(feature = "hotpath", hotpath::measure)] fn parse_memory_info() -> Result<(f64, f64), io::Error> { let mut total_memory_kb = 0.0; let mut available_memory_kb = 0.0; diff --git a/src/uptime.rs b/src/uptime.rs index 87b95cf..d5253d6 100644 --- a/src/uptime.rs +++ b/src/uptime.rs @@ -1,5 +1,6 @@ use std::{io, mem::MaybeUninit}; +#[cfg_attr(feature = "hotpath", hotpath::measure)] pub fn get_current() -> Result { let uptime_seconds = { let mut info = MaybeUninit::uninit(); From 325ec690249438ead86fb2a01253b4761527ccae Mon Sep 17 00:00:00 2001 From: NotAShelf Date: Mon, 17 Nov 2025 17:40:59 +0300 Subject: [PATCH 14/48] chore: tag 0.4.10 Signed-off-by: NotAShelf Change-Id: I6a2158a305d5f249b52c8b21dc5aaca86a6a6964 --- Cargo.lock | 2 +- Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 40f74fc..c7bf6b7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -772,7 +772,7 @@ checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" [[package]] name = "microfetch" -version = "0.4.9" +version = "0.4.10" dependencies = [ "criterion", "hotpath", diff --git a/Cargo.toml b/Cargo.toml index 6af4917..c97b58b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "microfetch" -version = "0.4.9" +version = "0.4.10" edition = "2024" [lib] From 2ad765ef988051fd7fb6272c595baee1ed87a00c Mon Sep 17 00:00:00 2001 From: NotAShelf Date: Mon, 17 Nov 2025 17:55:10 +0300 Subject: [PATCH 15/48] various: reduce allocations where available Signed-off-by: NotAShelf Change-Id: I517d855b14c015569a325deb64948f3b6a6a6964 --- src/colors.rs | 45 +++++++++++++++++++++++++++--------- src/desktop.rs | 47 ++++++++++++++++++++++---------------- src/release.rs | 28 ++++++++++++++--------- src/system.rs | 62 ++++++++++++++++++++++++++++++++++---------------- src/uptime.rs | 8 +++---- 5 files changed, 125 insertions(+), 65 deletions(-) diff --git a/src/colors.rs b/src/colors.rs index 53b12f6..07ab9bd 100644 --- a/src/colors.rs +++ b/src/colors.rs @@ -37,7 +37,7 @@ impl Colors { } pub static COLORS: LazyLock = LazyLock::new(|| { - // check for NO_COLOR once at startup + // Check for NO_COLOR once at startup let is_no_color = env::var("NO_COLOR").is_ok(); Colors::new(is_no_color) }); @@ -45,14 +45,37 @@ pub static COLORS: LazyLock = LazyLock::new(|| { #[must_use] #[cfg_attr(feature = "hotpath", hotpath::measure)] pub fn print_dots() -> String { - format!( - "{} {} {} {} {} {} {}", - COLORS.blue, - COLORS.cyan, - COLORS.green, - COLORS.yellow, - COLORS.red, - COLORS.magenta, - COLORS.reset, - ) + // Pre-calculate capacity: 6 color codes + " " (glyph + 2 spaces) per color + const GLYPH: &str = ""; + let capacity = COLORS.blue.len() + + COLORS.cyan.len() + + COLORS.green.len() + + COLORS.yellow.len() + + COLORS.red.len() + + COLORS.magenta.len() + + COLORS.reset.len() + + (GLYPH.len() + 2) * 6; + + let mut result = String::with_capacity(capacity); + result.push_str(COLORS.blue); + result.push_str(GLYPH); + result.push_str(" "); + result.push_str(COLORS.cyan); + result.push_str(GLYPH); + result.push_str(" "); + result.push_str(COLORS.green); + result.push_str(GLYPH); + result.push_str(" "); + result.push_str(COLORS.yellow); + result.push_str(GLYPH); + result.push_str(" "); + result.push_str(COLORS.red); + result.push_str(GLYPH); + result.push_str(" "); + result.push_str(COLORS.magenta); + result.push_str(GLYPH); + result.push_str(" "); + result.push_str(COLORS.reset); + + result } diff --git a/src/desktop.rs b/src/desktop.rs index 7d3733a..561be03 100644 --- a/src/desktop.rs +++ b/src/desktop.rs @@ -1,30 +1,37 @@ +use std::fmt::Write; + #[must_use] #[cfg_attr(feature = "hotpath", hotpath::measure)] pub fn get_desktop_info() -> String { // Retrieve the environment variables and handle Result types let desktop_env = std::env::var("XDG_CURRENT_DESKTOP"); - let display_backend_result = std::env::var("XDG_SESSION_TYPE"); + let display_backend = std::env::var("XDG_SESSION_TYPE"); - // Capitalize the first letter of the display backend value - let mut display_backend = display_backend_result.unwrap_or_default(); - if let Some(c) = display_backend.as_mut_str().get_mut(0..1) { - c.make_ascii_uppercase(); - } - - // Trim "none+" from the start of desktop_env if present - // Use "Unknown" if desktop_env is empty or has an error - let desktop_env = match desktop_env { - Err(_) => "Unknown".to_owned(), - Ok(s) => s.trim_start_matches("none+").to_owned(), + let desktop_str = match desktop_env { + Err(_) => "Unknown", + Ok(ref s) if s.starts_with("none+") => &s[5..], + Ok(ref s) => s.as_str(), }; - // Handle the case where display_backend might be empty after capitalization - let display_backend = if display_backend.is_empty() { - "Unknown" - } else { - &display_backend - } - .to_owned(); + let backend_str = match display_backend { + Err(_) => "Unknown", + Ok(ref s) if s.is_empty() => "Unknown", + Ok(ref s) => s.as_str(), + }; - format!("{desktop_env} ({display_backend})") + // Pre-calculate capacity: desktop_len + " (" + backend_len + ")" + // Capitalize first char needs temporary allocation only if backend exists + let mut result = + String::with_capacity(desktop_str.len() + backend_str.len() + 3); + result.push_str(desktop_str); + result.push_str(" ("); + + // Capitalize first character of backend + if let Some(first_char) = backend_str.chars().next() { + let _ = write!(result, "{}", first_char.to_ascii_uppercase()); + result.push_str(&backend_str[first_char.len_utf8()..]); + } + + result.push(')'); + result } diff --git a/src/release.rs b/src/release.rs index 2f1338c..d9ec4c9 100644 --- a/src/release.rs +++ b/src/release.rs @@ -1,6 +1,7 @@ use std::{ + fmt::Write as _, fs::File, - io::{self, BufRead, BufReader}, + io::{self, Read}, }; use nix::sys::utsname::UtsName; @@ -8,21 +9,26 @@ use nix::sys::utsname::UtsName; #[must_use] #[cfg_attr(feature = "hotpath", hotpath::measure)] pub fn get_system_info(utsname: &UtsName) -> String { - format!( - "{} {} ({})", - utsname.sysname().to_str().unwrap_or("Unknown"), - utsname.release().to_str().unwrap_or("Unknown"), - utsname.machine().to_str().unwrap_or("Unknown") - ) + let sysname = utsname.sysname().to_str().unwrap_or("Unknown"); + let release = utsname.release().to_str().unwrap_or("Unknown"); + let machine = utsname.machine().to_str().unwrap_or("Unknown"); + + // Pre-allocate capacity: sysname + " " + release + " (" + machine + ")" + let capacity = sysname.len() + 1 + release.len() + 2 + machine.len() + 1; + let mut result = String::with_capacity(capacity); + + write!(result, "{sysname} {release} ({machine})").unwrap(); + result } #[cfg_attr(feature = "hotpath", hotpath::measure)] pub fn get_os_pretty_name() -> Result { - let file = File::open("/etc/os-release")?; - let reader = BufReader::new(file); + // We use a stack-allocated buffer here, which seems to perform MUCH better + // than `BufReader`. In hindsight, I should've seen this coming. + let mut buffer = String::with_capacity(1024); + File::open("/etc/os-release")?.read_to_string(&mut buffer)?; - for line in reader.lines() { - let line = line?; + for line in buffer.lines() { if let Some(pretty_name) = line.strip_prefix("PRETTY_NAME=") { if let Some(trimmed) = pretty_name .strip_prefix('"') diff --git a/src/system.rs b/src/system.rs index f90c912..21b2b2f 100644 --- a/src/system.rs +++ b/src/system.rs @@ -1,5 +1,6 @@ use std::{ env, + fmt::Write as _, fs::File, io::{self, Read}, }; @@ -12,18 +13,26 @@ use crate::colors::COLORS; #[cfg_attr(feature = "hotpath", hotpath::measure)] pub fn get_username_and_hostname(utsname: &UtsName) -> String { let username = env::var("USER").unwrap_or_else(|_| "unknown_user".to_owned()); - let hostname = utsname - .nodename() - .to_str() - .unwrap_or("unknown_host") - .to_owned(); - format!( - "{yellow}{username}{red}@{green}{hostname}{reset}", - yellow = COLORS.yellow, - red = COLORS.red, - green = COLORS.green, - reset = COLORS.reset, - ) + let hostname = utsname.nodename().to_str().unwrap_or("unknown_host"); + + let capacity = COLORS.yellow.len() + + username.len() + + COLORS.red.len() + + 1 + + COLORS.green.len() + + hostname.len() + + COLORS.reset.len(); + let mut result = String::with_capacity(capacity); + + result.push_str(COLORS.yellow); + result.push_str(&username); + result.push_str(COLORS.red); + result.push('@'); + result.push_str(COLORS.green); + result.push_str(hostname); + result.push_str(COLORS.reset); + + result } #[must_use] @@ -31,8 +40,13 @@ pub fn get_username_and_hostname(utsname: &UtsName) -> String { pub fn get_shell() -> String { let shell_path = env::var("SHELL").unwrap_or_else(|_| "unknown_shell".to_owned()); - let shell_name = shell_path.rsplit('/').next().unwrap_or("unknown_shell"); - shell_name.to_owned() + + // Find last '/' and get the part after it, avoiding allocation + shell_path + .rsplit('/') + .next() + .unwrap_or("unknown_shell") + .to_owned() } #[cfg_attr(feature = "hotpath", hotpath::measure)] @@ -49,11 +63,16 @@ pub fn get_root_disk_usage() -> Result { let used_size = used_size as f64 / (1024.0 * 1024.0 * 1024.0); let usage = (used_size / total_size) * 100.0; - Ok(format!( + let mut result = String::with_capacity(64); + write!( + result, "{used_size:.2} GiB / {total_size:.2} GiB ({cyan}{usage:.0}%{reset})", cyan = COLORS.cyan, reset = COLORS.reset, - )) + ) + .unwrap(); + + Ok(result) } #[cfg_attr(feature = "hotpath", hotpath::measure)] @@ -70,7 +89,7 @@ pub fn get_memory_usage() -> Result { let mut split = line.split_whitespace(); match split.next().unwrap_or_default() { "MemTotal:" => { - total_memory_kb = split.next().unwrap_or("0").parse().unwrap_or(0.0) + total_memory_kb = split.next().unwrap_or("0").parse().unwrap_or(0.0); }, "MemAvailable:" => { available_memory_kb = @@ -92,10 +111,15 @@ pub fn get_memory_usage() -> Result { let (used_memory, total_memory) = parse_memory_info()?; let percentage_used = (used_memory / total_memory * 100.0).round() as u64; - Ok(format!( + let mut result = String::with_capacity(64); + write!( + result, "{used_memory:.2} GiB / {total_memory:.2} GiB \ ({cyan}{percentage_used}%{reset})", cyan = COLORS.cyan, reset = COLORS.reset, - )) + ) + .unwrap(); + + Ok(result) } diff --git a/src/uptime.rs b/src/uptime.rs index d5253d6..bd2cc39 100644 --- a/src/uptime.rs +++ b/src/uptime.rs @@ -1,4 +1,4 @@ -use std::{io, mem::MaybeUninit}; +use std::{fmt::Write, io, mem::MaybeUninit}; #[cfg_attr(feature = "hotpath", hotpath::measure)] pub fn get_current() -> Result { @@ -16,21 +16,21 @@ pub fn get_current() -> Result { let mut result = String::with_capacity(32); if days > 0 { - result.push_str(&days.to_string()); + let _ = write!(result, "{days}"); result.push_str(if days == 1 { " day" } else { " days" }); } if hours > 0 { if !result.is_empty() { result.push_str(", "); } - result.push_str(&hours.to_string()); + let _ = write!(result, "{hours}"); result.push_str(if hours == 1 { " hour" } else { " hours" }); } if minutes > 0 { if !result.is_empty() { result.push_str(", "); } - result.push_str(&minutes.to_string()); + let _ = write!(result, "{minutes}"); result.push_str(if minutes == 1 { " minute" } else { " minutes" }); } if result.is_empty() { From f4f3385ff760f6570ae7af93774748e977da30e4 Mon Sep 17 00:00:00 2001 From: NotAShelf Date: Mon, 17 Nov 2025 18:17:59 +0300 Subject: [PATCH 16/48] various: fix clippy warnings - Adds proper documentation comments with `# Errors` sections for all functions returning `Result` - `cast_precision_loss` on `u64` -> `f64` for disk sizes is acceptable since disk sizes won't exceed f64's precision limit in practice. Thus, we can suppress those. - `cast_sign_loss` and `cast_possible_truncation` on the percentage calculation is safe since percentages are always 0-100. Once again, it's safe to suppress. Signed-off-by: NotAShelf Change-Id: Id4dd7ebc9674407d2be4f38ff4de24bc6a6a6964 --- src/release.rs | 5 +++++ src/system.rs | 12 ++++++++++++ src/uptime.rs | 6 ++++++ 3 files changed, 23 insertions(+) diff --git a/src/release.rs b/src/release.rs index d9ec4c9..1b820b8 100644 --- a/src/release.rs +++ b/src/release.rs @@ -21,6 +21,11 @@ pub fn get_system_info(utsname: &UtsName) -> String { result } +/// Gets the pretty name of the OS from `/etc/os-release`. +/// +/// # Errors +/// +/// Returns an error if `/etc/os-release` cannot be read. #[cfg_attr(feature = "hotpath", hotpath::measure)] pub fn get_os_pretty_name() -> Result { // We use a stack-allocated buffer here, which seems to perform MUCH better diff --git a/src/system.rs b/src/system.rs index 21b2b2f..ba90b27 100644 --- a/src/system.rs +++ b/src/system.rs @@ -49,7 +49,13 @@ pub fn get_shell() -> String { .to_owned() } +/// Gets the root disk usage information. +/// +/// # Errors +/// +/// Returns an error if the filesystem information cannot be retrieved. #[cfg_attr(feature = "hotpath", hotpath::measure)] +#[allow(clippy::cast_precision_loss)] pub fn get_root_disk_usage() -> Result { let vfs = statvfs("/")?; let block_size = vfs.block_size() as u64; @@ -75,6 +81,11 @@ pub fn get_root_disk_usage() -> Result { Ok(result) } +/// Gets the system memory usage information. +/// +/// # Errors +/// +/// Returns an error if `/proc/meminfo` cannot be read. #[cfg_attr(feature = "hotpath", hotpath::measure)] pub fn get_memory_usage() -> Result { #[cfg_attr(feature = "hotpath", hotpath::measure)] @@ -109,6 +120,7 @@ pub fn get_memory_usage() -> Result { } let (used_memory, total_memory) = parse_memory_info()?; + #[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)] let percentage_used = (used_memory / total_memory * 100.0).round() as u64; let mut result = String::with_capacity(64); diff --git a/src/uptime.rs b/src/uptime.rs index bd2cc39..72af399 100644 --- a/src/uptime.rs +++ b/src/uptime.rs @@ -1,5 +1,10 @@ use std::{fmt::Write, io, mem::MaybeUninit}; +/// Gets the current system uptime. +/// +/// # Errors +/// +/// Returns an error if the system uptime cannot be retrieved. #[cfg_attr(feature = "hotpath", hotpath::measure)] pub fn get_current() -> Result { let uptime_seconds = { @@ -7,6 +12,7 @@ pub fn get_current() -> Result { if unsafe { libc::sysinfo(info.as_mut_ptr()) } != 0 { return Err(io::Error::last_os_error()); } + #[allow(clippy::cast_sign_loss)] unsafe { info.assume_init().uptime as u64 } }; From 789ece866b0480f57a365f6c1509a51c8b90b005 Mon Sep 17 00:00:00 2001 From: NotAShelf Date: Mon, 17 Nov 2025 18:30:06 +0300 Subject: [PATCH 17/48] ci: initial benchmarking workflows Signed-off-by: NotAShelf Change-Id: I367444097eafbd1020c02707c42351bf6a6a6964 --- .github/workflows/hotpath-comment.yml | 57 ++++++++++++++++++++++++ .github/workflows/hotpath-profile.yml | 63 +++++++++++++++++++++++++++ 2 files changed, 120 insertions(+) create mode 100644 .github/workflows/hotpath-comment.yml create mode 100644 .github/workflows/hotpath-profile.yml diff --git a/.github/workflows/hotpath-comment.yml b/.github/workflows/hotpath-comment.yml new file mode 100644 index 0000000..395a533 --- /dev/null +++ b/.github/workflows/hotpath-comment.yml @@ -0,0 +1,57 @@ +name: Hotpath Comment + +on: + workflow_run: + workflows: ["Hotpath Profile"] + types: + - completed + +permissions: + pull-requests: write + +jobs: + comment: + runs-on: ubuntu-latest + if: ${{ github.event.workflow_run.conclusion == 'success' }} + + steps: + - name: Download profiling results + uses: actions/download-artifact@v4 + with: + name: hotpath-results + github-token: ${{ secrets.GITHUB_TOKEN }} + run-id: ${{ github.event.workflow_run.id }} + + - name: Read PR number + id: pr + run: echo "number=$(cat pr_number.txt)" >> $GITHUB_OUTPUT + + - name: Setup Rust + uses: actions-rust-lang/setup-rust-toolchain@v1 + + - name: Install hotpath CLI + run: cargo install hotpath + + - name: Post timing comparison comment + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + hotpath profile-pr \ + --repo ${{ github.repository }} \ + --pr-number ${{ steps.pr.outputs.number }} \ + --head-json head-timing.json \ + --base-json base-timing.json \ + --mode timing \ + --title "⏱️ Hotpath Timing Profile" + + - name: Post allocation comparison comment + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + hotpath profile-pr \ + --repo ${{ github.repository }} \ + --pr-number ${{ steps.pr.outputs.number }} \ + --head-json head-alloc.json \ + --base-json base-alloc.json \ + --mode alloc \ + --title "📊 Hotpath Allocation Profile" diff --git a/.github/workflows/hotpath-profile.yml b/.github/workflows/hotpath-profile.yml new file mode 100644 index 0000000..b367ca2 --- /dev/null +++ b/.github/workflows/hotpath-profile.yml @@ -0,0 +1,63 @@ +name: Hotpath Profile + +on: + pull_request: + branches: [ "main" ] + +env: + CARGO_TERM_COLOR: always + +jobs: + profile: + runs-on: ubuntu-latest + + steps: + - name: Checkout PR HEAD + uses: actions/checkout@v4 + + - name: Setup Rust + uses: actions-rust-lang/setup-rust-toolchain@v1 + + - name: Run timing profiling on HEAD + env: + HOTPATH_JSON: "true" + run: | + cargo run --features='hotpath' 2>&1 | grep '^{"hotpath_profiling_mode"' > head-timing.json + + - name: Run allocation profiling on HEAD + env: + HOTPATH_JSON: "true" + run: | + cargo run --features='hotpath,hotpath-alloc-count-total' 2>&1 | grep '^{"hotpath_profiling_mode"' > head-alloc.json + + - name: Checkout base branch + uses: actions/checkout@v4 + with: + ref: ${{ github.event.pull_request.base.sha }} + + - name: Run timing profiling on base + env: + HOTPATH_JSON: "true" + run: | + cargo run --features='hotpath' 2>&1 | grep '^{"hotpath_profiling_mode"' > base-timing.json + + - name: Run allocation profiling on base + env: + HOTPATH_JSON: "true" + run: | + cargo run --features='hotpath,hotpath-alloc-count-total' 2>&1 | grep '^{"hotpath_profiling_mode"' > base-alloc.json + + - name: Save PR number + run: echo "${{ github.event.number }}" > pr_number.txt + + - name: Upload profiling results + uses: actions/upload-artifact@v4 + with: + name: hotpath-results + path: | + head-timing.json + head-alloc.json + base-timing.json + base-alloc.json + pr_number.txt + retention-days: 1 From 00159d64545b6afab624c1905b66421f32a0eca1 Mon Sep 17 00:00:00 2001 From: NotAShelf Date: Mon, 17 Nov 2025 19:17:44 +0300 Subject: [PATCH 18/48] docs: README makeover; update benchmarks Signed-off-by: NotAShelf Change-Id: Ic1ca95ab7e0b2ff78ed7967c604739546a6a6964 --- README.md | 141 ++++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 105 insertions(+), 36 deletions(-) diff --git a/README.md b/README.md index 28d6338..5ae0f72 100644 --- a/README.md +++ b/README.md @@ -1,17 +1,34 @@ + +
https://deps.rs/repo/github/notashelf/microfetch - - + stars
-

Microfetch

+
+

+ Microfetch +

+

+ Microscopic fetch tool in Rust, for NixOS systems, with special emphasis on speed +

+
+ Synopsis
+ Features | Motivation
+ Installation +
+
-Stupidly simple, laughably fast fetch tool. Written in Rust for speed and ease -of maintainability. Runs in a _fraction of a millisecond_ and displays _most_ of -the nonsense you'd see posted on r/unixporn or other internet communities. Aims -to replace [fastfetch](https://github.com/fastfetch-cli/fastfetch) on my -personal system, but [probably not yours](#customizing). Though, you are more -than welcome to use it on your system: it's pretty [fast](#benchmarks)... +## Synopsis + +[fastfetch]: https://github.com/fastfetch-cli/fastfetch + +Stupidly small and simple, laughably fast and pretty fetch tool. Written in Rust +for speed and ease of maintainability. Runs in a _fraction of a millisecond_ and +displays _most_ of the nonsense you'd see posted on r/unixporn or other internet +communities. Aims to replace [fastfetch] on my personal system, but +[probably not yours](#customizing). Though, you are more than welcome to use it +on your system: it is pretty _[fast](#benchmarks)_...

[!NOTE] From ca76a6e1bd532f6c9424f0530ad194c32f37c744 Mon Sep 17 00:00:00 2001 From: NotAShelf Date: Mon, 17 Nov 2025 19:17:53 +0300 Subject: [PATCH 19/48] chore: format with rustfmt nightly Signed-off-by: NotAShelf Change-Id: I9c9e4e010b09d37f0e0994a5407f3ce56a6a6964 --- src/uptime.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/uptime.rs b/src/uptime.rs index 72af399..52188c2 100644 --- a/src/uptime.rs +++ b/src/uptime.rs @@ -13,7 +13,9 @@ pub fn get_current() -> Result { return Err(io::Error::last_os_error()); } #[allow(clippy::cast_sign_loss)] - unsafe { info.assume_init().uptime as u64 } + unsafe { + info.assume_init().uptime as u64 + } }; let days = uptime_seconds / 86400; From b24e720dd81d52ef4e71ede470d7b611d5474a6d Mon Sep 17 00:00:00 2001 From: NotAShelf Date: Mon, 17 Nov 2025 19:18:35 +0300 Subject: [PATCH 20/48] chore: tag 0.4.11 Signed-off-by: NotAShelf Change-Id: I42b59d68cdac17ff60d52a4c25bef4686a6a6964 --- Cargo.lock | 8 ++++---- Cargo.toml | 6 +++--- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c7bf6b7..c9ed3f9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -690,7 +690,7 @@ checksum = "3640c1c38b8e4e43584d8df18be5fc6b0aa314ce6ebf51b53313d4306cca8e46" dependencies = [ "hermit-abi", "libc", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -732,9 +732,9 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "libc" -version = "0.2.175" +version = "0.2.177" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a82ae493e598baaea5209805c49bbf2ea7de956d50d7da0da1164f9c6d28543" +checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976" [[package]] name = "libredox" @@ -772,7 +772,7 @@ checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" [[package]] name = "microfetch" -version = "0.4.10" +version = "0.4.11" dependencies = [ "criterion", "hotpath", diff --git a/Cargo.toml b/Cargo.toml index c97b58b..7e83f9f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "microfetch" -version = "0.4.10" +version = "0.4.11" edition = "2024" [lib] @@ -12,8 +12,8 @@ name = "microfetch" path = "src/main.rs" [dependencies] -hotpath = { optional = true, version = "0.6" } -libc = "0.2.175" +hotpath = { optional = true, version = "0.6.0" } +libc = "0.2.177" nix = { default-features = false, features = [ "fs", "hostname", "feature" ], version = "0.30.1" } [dev-dependencies] From f8a0070986dda8c7554dcf769fda99cd129e039f Mon Sep 17 00:00:00 2001 From: NotAShelf Date: Mon, 17 Nov 2025 19:19:28 +0300 Subject: [PATCH 21/48] uptime: optimize uptime calculation w/ inline assembly and custom itoa lol, lmao even. Signed-off-by: NotAShelf Change-Id: Ie22fbd2e9c2be5740b493bdc81caafb36a6a6964 --- src/uptime.rs | 74 +++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 69 insertions(+), 5 deletions(-) diff --git a/src/uptime.rs b/src/uptime.rs index 52188c2..98c9207 100644 --- a/src/uptime.rs +++ b/src/uptime.rs @@ -1,4 +1,66 @@ -use std::{fmt::Write, io, mem::MaybeUninit}; +use std::{io, mem::MaybeUninit}; + +/// Fast integer to string conversion (no formatting overhead) +#[inline] +fn itoa(mut n: u64, buf: &mut [u8]) -> &str { + if n == 0 { + return "0"; + } + + let mut i = buf.len(); + while n > 0 { + i -= 1; + buf[i] = b'0' + (n % 10) as u8; + n /= 10; + } + + unsafe { std::str::from_utf8_unchecked(&buf[i..]) } +} + +/// Direct sysinfo syscall using inline assembly +/// +/// # Safety +/// This function uses inline assembly to make a direct syscall. +/// The caller must ensure the sysinfo pointer is valid. +#[inline] +unsafe fn sys_sysinfo(info: *mut libc::sysinfo) -> i64 { + #[cfg(target_arch = "x86_64")] + { + let ret: i64; + unsafe { + std::arch::asm!( + "syscall", + in("rax") 99_i64, // __NR_sysinfo + in("rdi") info, + out("rcx") _, + out("r11") _, + lateout("rax") ret, + options(nostack) + ); + } + ret + } + + #[cfg(target_arch = "aarch64")] + { + let ret: i64; + unsafe { + std::arch::asm!( + "svc #0", + in("x8") 179_i64, // __NR_sysinfo + in("x0") info, + lateout("x0") ret, + options(nostack) + ); + } + ret + } + + #[cfg(not(any(target_arch = "x86_64", target_arch = "aarch64")))] + { + unsafe { libc::sysinfo(info) as i64 } + } +} /// Gets the current system uptime. /// @@ -9,7 +71,7 @@ use std::{fmt::Write, io, mem::MaybeUninit}; pub fn get_current() -> Result { let uptime_seconds = { let mut info = MaybeUninit::uninit(); - if unsafe { libc::sysinfo(info.as_mut_ptr()) } != 0 { + if unsafe { sys_sysinfo(info.as_mut_ptr()) } != 0 { return Err(io::Error::last_os_error()); } #[allow(clippy::cast_sign_loss)] @@ -23,22 +85,24 @@ pub fn get_current() -> Result { let minutes = (uptime_seconds / 60) % 60; let mut result = String::with_capacity(32); + let mut buf = [0u8; 20]; // Enough for u64::MAX + if days > 0 { - let _ = write!(result, "{days}"); + result.push_str(itoa(days, &mut buf)); result.push_str(if days == 1 { " day" } else { " days" }); } if hours > 0 { if !result.is_empty() { result.push_str(", "); } - let _ = write!(result, "{hours}"); + result.push_str(itoa(hours, &mut buf)); result.push_str(if hours == 1 { " hour" } else { " hours" }); } if minutes > 0 { if !result.is_empty() { result.push_str(", "); } - let _ = write!(result, "{minutes}"); + result.push_str(itoa(minutes, &mut buf)); result.push_str(if minutes == 1 { " minute" } else { " minutes" }); } if result.is_empty() { From 75132ff17239b2778ee1e36308a6b011579400e6 Mon Sep 17 00:00:00 2001 From: NotAShelf Date: Mon, 17 Nov 2025 21:27:28 +0300 Subject: [PATCH 22/48] treewide: remove nix dependency, add custom syscall wrappers & `UtsName` Signed-off-by: NotAShelf Change-Id: Ib880f4bafe9d3bbc944af4b9125256366a6a6964 --- Cargo.lock | 19 ---- Cargo.toml | 1 - benches/benchmark.rs | 3 +- src/lib.rs | 40 +++++++++ src/main.rs | 5 +- src/release.rs | 57 ++++++++---- src/syscall.rs | 203 +++++++++++++++++++++++++++++++++++++++++++ src/system.rs | 102 +++++++++++++++------- 8 files changed, 358 insertions(+), 72 deletions(-) create mode 100644 src/syscall.rs diff --git a/Cargo.lock b/Cargo.lock index c9ed3f9..0147746 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -143,12 +143,6 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" -[[package]] -name = "cfg_aliases" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" - [[package]] name = "chunked_transfer" version = "1.5.0" @@ -777,7 +771,6 @@ dependencies = [ "criterion", "hotpath", "libc", - "nix", ] [[package]] @@ -790,18 +783,6 @@ dependencies = [ "simd-adler32", ] -[[package]] -name = "nix" -version = "0.30.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74523f3a35e05aba87a1d978330aef40f67b0304ac79c1c00b294c9830543db6" -dependencies = [ - "bitflags", - "cfg-if", - "cfg_aliases", - "libc", -] - [[package]] name = "num-conv" version = "0.1.0" diff --git a/Cargo.toml b/Cargo.toml index 7e83f9f..e95ef5c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,7 +14,6 @@ path = "src/main.rs" [dependencies] hotpath = { optional = true, version = "0.6.0" } libc = "0.2.177" -nix = { default-features = false, features = [ "fs", "hostname", "feature" ], version = "0.30.1" } [dev-dependencies] criterion = "0.7" diff --git a/benches/benchmark.rs b/benches/benchmark.rs index 33dc1b9..8d62082 100644 --- a/benches/benchmark.rs +++ b/benches/benchmark.rs @@ -1,5 +1,6 @@ use criterion::{Criterion, criterion_group, criterion_main}; use microfetch_lib::{ + UtsName, colors::print_dots, desktop::get_desktop_info, release::{get_os_pretty_name, get_system_info}, @@ -13,7 +14,7 @@ use microfetch_lib::{ }; fn main_benchmark(c: &mut Criterion) { - let utsname = nix::sys::utsname::uname().expect("lol"); + let utsname = UtsName::uname().expect("Failed to get uname"); c.bench_function("user_info", |b| { b.iter(|| get_username_and_hostname(&utsname)); }); diff --git a/src/lib.rs b/src/lib.rs index 4ba33f8..8c8a6ba 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,5 +1,45 @@ pub mod colors; pub mod desktop; pub mod release; +pub mod syscall; pub mod system; pub mod uptime; + +use std::mem::MaybeUninit; + +/// Wrapper for `libc::utsname` with safe accessor methods +pub struct UtsName(libc::utsname); + +impl UtsName { + /// Calls `uname` syscall and returns a `UtsName` wrapper + /// + /// # Errors + /// Returns an error if the uname syscall fails + pub fn uname() -> Result { + let mut uts = MaybeUninit::uninit(); + if unsafe { libc::uname(uts.as_mut_ptr()) } != 0 { + return Err(std::io::Error::last_os_error()); + } + Ok(Self(unsafe { uts.assume_init() })) + } + + #[must_use] + pub const fn nodename(&self) -> &std::ffi::CStr { + unsafe { std::ffi::CStr::from_ptr(self.0.nodename.as_ptr()) } + } + + #[must_use] + pub const fn sysname(&self) -> &std::ffi::CStr { + unsafe { std::ffi::CStr::from_ptr(self.0.sysname.as_ptr()) } + } + + #[must_use] + pub const fn release(&self) -> &std::ffi::CStr { + unsafe { std::ffi::CStr::from_ptr(self.0.release.as_ptr()) } + } + + #[must_use] + pub const fn machine(&self) -> &std::ffi::CStr { + unsafe { std::ffi::CStr::from_ptr(self.0.machine.as_ptr()) } + } +} diff --git a/src/main.rs b/src/main.rs index 5ba7810..4e664bb 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,11 +1,14 @@ mod colors; mod desktop; mod release; +mod syscall; mod system; mod uptime; use std::io::{Write, stdout}; +pub use microfetch_lib::UtsName; + use crate::{ colors::print_dots, desktop::get_desktop_info, @@ -24,7 +27,7 @@ fn main() -> Result<(), Box> { if Some("--version") == std::env::args().nth(1).as_deref() { println!("Microfetch {}", env!("CARGO_PKG_VERSION")); } else { - let utsname = nix::sys::utsname::uname()?; + let utsname = UtsName::uname()?; let fields = Fields { user_info: get_username_and_hostname(&utsname), os_name: get_os_pretty_name()?, diff --git a/src/release.rs b/src/release.rs index 1b820b8..8d3232e 100644 --- a/src/release.rs +++ b/src/release.rs @@ -1,10 +1,6 @@ -use std::{ - fmt::Write as _, - fs::File, - io::{self, Read}, -}; +use std::{fmt::Write as _, io}; -use nix::sys::utsname::UtsName; +use crate::{UtsName, syscall::read_file_fast}; #[must_use] #[cfg_attr(feature = "hotpath", hotpath::measure)] @@ -28,21 +24,46 @@ pub fn get_system_info(utsname: &UtsName) -> String { /// Returns an error if `/etc/os-release` cannot be read. #[cfg_attr(feature = "hotpath", hotpath::measure)] pub fn get_os_pretty_name() -> Result { - // We use a stack-allocated buffer here, which seems to perform MUCH better - // than `BufReader`. In hindsight, I should've seen this coming. - let mut buffer = String::with_capacity(1024); - File::open("/etc/os-release")?.read_to_string(&mut buffer)?; + // Fast byte-level scanning for PRETTY_NAME= + const PREFIX: &[u8] = b"PRETTY_NAME="; - for line in buffer.lines() { - if let Some(pretty_name) = line.strip_prefix("PRETTY_NAME=") { - if let Some(trimmed) = pretty_name - .strip_prefix('"') - .and_then(|s| s.strip_suffix('"')) + let mut buffer = [0u8; 1024]; + + // Use fast syscall-based file reading + let bytes_read = read_file_fast("/etc/os-release", &mut buffer)?; + let content = &buffer[..bytes_read]; + + let mut offset = 0; + + while offset < content.len() { + let remaining = &content[offset..]; + + // Find newline or end + let line_end = remaining + .iter() + .position(|&b| b == b'\n') + .unwrap_or(remaining.len()); + let line = &remaining[..line_end]; + + if line.starts_with(PREFIX) { + let value = &line[PREFIX.len()..]; + + // Strip quotes if present + let trimmed = if value.len() >= 2 + && value[0] == b'"' + && value[value.len() - 1] == b'"' { - return Ok(trimmed.to_owned()); - } - return Ok(pretty_name.to_owned()); + &value[1..value.len() - 1] + } else { + value + }; + + // Convert to String - should be valid UTF-8 + return Ok(String::from_utf8_lossy(trimmed).into_owned()); } + + offset += line_end + 1; } + Ok("Unknown".to_owned()) } diff --git a/src/syscall.rs b/src/syscall.rs new file mode 100644 index 0000000..2776fe7 --- /dev/null +++ b/src/syscall.rs @@ -0,0 +1,203 @@ +//! Incredibly fast syscall wrappers for using inline assembly. Serves the +//! purposes of completely bypassing Rust's standard library in favor of +//! handwritten Assembly. Is this a good idea? No. Is it fast? Yeah, but only +//! marginally. Either way it serves a purpose and I will NOT accept criticism. +//! What do you mean I wasted two whole hours to make the program only 100µs +//! faster? +//! +//! Supports `x86_64` and `aarch64` architectures. Riscv support will be +//! implemented when and ONLY WHEN I can be bothered to work on it. + +use std::io; + +/// Direct syscall to open a file +/// Returns file descriptor or -1 on error +/// +/// # Safety +/// +/// The caller must ensure: +/// - `path` points to a valid null-terminated C string +/// - The pointer remains valid for the duration of the syscall +#[inline] +#[must_use] +pub unsafe fn sys_open(path: *const u8, flags: i32) -> i32 { + #[cfg(target_arch = "x86_64")] + unsafe { + let fd: i64; + std::arch::asm!( + "syscall", + in("rax") 2i64, // SYS_open + in("rdi") path, + in("rsi") flags, + in("rdx") 0i32, // mode (not used for reading) + lateout("rax") fd, + lateout("rcx") _, + lateout("r11") _, + options(nostack) + ); + #[allow(clippy::cast_possible_truncation)] + { + fd as i32 + } + } + #[cfg(target_arch = "aarch64")] + unsafe { + let fd: i64; + std::arch::asm!( + "svc #0", + in("x8") 56i64, // SYS_openat + in("x0") -100i32, // AT_FDCWD + in("x1") path, + in("x2") flags, + in("x3") 0i32, // mode + lateout("x0") fd, + options(nostack) + ); + #[allow(clippy::cast_possible_truncation)] + { + fd as i32 + } + } + #[cfg(not(any(target_arch = "x86_64", target_arch = "aarch64")))] + { + compile_error!("Unsupported architecture for inline assembly syscalls"); + } +} + +/// Direct syscall to read from a file descriptor +/// Returns number of bytes read or -1 on error +/// +/// # Safety +/// +/// The caller must ensure: +/// - `buf` points to a valid writable buffer of at least `count` bytes +/// - `fd` is a valid open file descriptor +#[inline] +pub unsafe fn sys_read(fd: i32, buf: *mut u8, count: usize) -> isize { + #[cfg(target_arch = "x86_64")] + unsafe { + let ret: i64; + std::arch::asm!( + "syscall", + in("rax") 0i64, // SYS_read + in("rdi") fd, + in("rsi") buf, + in("rdx") count, + lateout("rax") ret, + lateout("rcx") _, + lateout("r11") _, + options(nostack) + ); + #[allow(clippy::cast_possible_truncation)] + { + ret as isize + } + } + #[cfg(target_arch = "aarch64")] + unsafe { + let ret: i64; + std::arch::asm!( + "svc #0", + in("x8") 63i64, // SYS_read + in("x0") fd, + in("x1") buf, + in("x2") count, + lateout("x0") ret, + options(nostack) + ); + #[allow(clippy::cast_possible_truncation)] + { + ret as isize + } + } + #[cfg(not(any(target_arch = "x86_64", target_arch = "aarch64")))] + { + compile_error!("Unsupported architecture for inline assembly syscalls"); + } +} + +/// Direct syscall to close a file descriptor +/// +/// # Safety +/// +/// The caller must ensure `fd` is a valid open file descriptor +#[inline] +#[must_use] +pub unsafe fn sys_close(fd: i32) -> i32 { + #[cfg(target_arch = "x86_64")] + unsafe { + let ret: i64; + std::arch::asm!( + "syscall", + in("rax") 3i64, // SYS_close + in("rdi") fd, + lateout("rax") ret, + lateout("rcx") _, + lateout("r11") _, + options(nostack) + ); + #[allow(clippy::cast_possible_truncation)] + { + ret as i32 + } + } + #[cfg(target_arch = "aarch64")] + unsafe { + let ret: i64; + std::arch::asm!( + "svc #0", + in("x8") 57i64, // SYS_close + in("x0") fd, + lateout("x0") ret, + options(nostack) + ); + #[allow(clippy::cast_possible_truncation)] + { + ret as i32 + } + } + #[cfg(not(any(target_arch = "x86_64", target_arch = "aarch64")))] + { + compile_error!("Unsupported architecture for inline assembly syscalls"); + } +} + +/// Read entire file using direct syscalls. This avoids libc overhead and can be +/// significantly faster for small files. +/// +/// # Errors +/// +/// Returns an error if the file cannot be opened or read +#[inline] +pub fn read_file_fast(path: &str, buffer: &mut [u8]) -> io::Result { + const O_RDONLY: i32 = 0; + + // Use stack-allocated buffer for null-terminated path (max 256 bytes) + let path_bytes = path.as_bytes(); + if path_bytes.len() >= 256 { + return Err(io::Error::new(io::ErrorKind::InvalidInput, "Path too long")); + } + + let mut path_buf = [0u8; 256]; + path_buf[..path_bytes.len()].copy_from_slice(path_bytes); + // XXX: Already zero-terminated since array is initialized to zeros + + unsafe { + let fd = sys_open(path_buf.as_ptr(), O_RDONLY); + if fd < 0 { + return Err(io::Error::last_os_error()); + } + + let bytes_read = sys_read(fd, buffer.as_mut_ptr(), buffer.len()); + let _ = sys_close(fd); + + if bytes_read < 0 { + return Err(io::Error::last_os_error()); + } + + #[allow(clippy::cast_sign_loss)] + { + Ok(bytes_read as usize) + } + } +} diff --git a/src/system.rs b/src/system.rs index ba90b27..24406ec 100644 --- a/src/system.rs +++ b/src/system.rs @@ -1,13 +1,6 @@ -use std::{ - env, - fmt::Write as _, - fs::File, - io::{self, Read}, -}; +use std::{env, fmt::Write as _, io, mem::MaybeUninit}; -use nix::sys::{statvfs::statvfs, utsname::UtsName}; - -use crate::colors::COLORS; +use crate::{UtsName, colors::COLORS, syscall::read_file_fast}; #[must_use] #[cfg_attr(feature = "hotpath", hotpath::measure)] @@ -57,10 +50,17 @@ pub fn get_shell() -> String { #[cfg_attr(feature = "hotpath", hotpath::measure)] #[allow(clippy::cast_precision_loss)] pub fn get_root_disk_usage() -> Result { - let vfs = statvfs("/")?; - let block_size = vfs.block_size() as u64; - let total_blocks = vfs.blocks(); - let available_blocks = vfs.blocks_available(); + let mut vfs = MaybeUninit::uninit(); + let path = b"/\0"; + + if unsafe { libc::statvfs(path.as_ptr().cast(), vfs.as_mut_ptr()) } != 0 { + return Err(io::Error::last_os_error()); + } + + let vfs = unsafe { vfs.assume_init() }; + let block_size = vfs.f_bsize; + let total_blocks = vfs.f_blocks; + let available_blocks = vfs.f_bavail; let total_size = block_size * total_blocks; let used_size = total_size - (block_size * available_blocks); @@ -81,6 +81,20 @@ pub fn get_root_disk_usage() -> Result { Ok(result) } +/// Fast integer parsing without stdlib overhead +#[inline] +fn parse_u64_fast(s: &[u8]) -> u64 { + let mut result = 0u64; + for &byte in s { + if byte.is_ascii_digit() { + result = result * 10 + u64::from(byte - b'0'); + } else { + break; + } + } + result +} + /// Gets the system memory usage information. /// /// # Errors @@ -90,30 +104,54 @@ pub fn get_root_disk_usage() -> Result { pub fn get_memory_usage() -> Result { #[cfg_attr(feature = "hotpath", hotpath::measure)] fn parse_memory_info() -> Result<(f64, f64), io::Error> { - let mut total_memory_kb = 0.0; - let mut available_memory_kb = 0.0; - let mut meminfo = String::with_capacity(2048); + let mut total_memory_kb = 0u64; + let mut available_memory_kb = 0u64; + let mut buffer = [0u8; 2048]; - File::open("/proc/meminfo")?.read_to_string(&mut meminfo)?; + // Use fast syscall-based file reading + let bytes_read = read_file_fast("/proc/meminfo", &mut buffer)?; + let meminfo = &buffer[..bytes_read]; - for line in meminfo.lines() { - let mut split = line.split_whitespace(); - match split.next().unwrap_or_default() { - "MemTotal:" => { - total_memory_kb = split.next().unwrap_or("0").parse().unwrap_or(0.0); - }, - "MemAvailable:" => { - available_memory_kb = - split.next().unwrap_or("0").parse().unwrap_or(0.0); - // MemTotal comes before MemAvailable, stop parsing - break; - }, - _ => (), + // Fast scanning for MemTotal and MemAvailable + let mut offset = 0; + let mut found_total = false; + let mut found_available = false; + + while offset < meminfo.len() && (!found_total || !found_available) { + let remaining = &meminfo[offset..]; + + // Find newline or end + let line_end = remaining + .iter() + .position(|&b| b == b'\n') + .unwrap_or(remaining.len()); + let line = &remaining[..line_end]; + + if line.starts_with(b"MemTotal:") { + // Skip "MemTotal:" and whitespace + let mut pos = 9; + while pos < line.len() && line[pos].is_ascii_whitespace() { + pos += 1; + } + total_memory_kb = parse_u64_fast(&line[pos..]); + found_total = true; + } else if line.starts_with(b"MemAvailable:") { + // Skip "MemAvailable:" and whitespace + let mut pos = 13; + while pos < line.len() && line[pos].is_ascii_whitespace() { + pos += 1; + } + available_memory_kb = parse_u64_fast(&line[pos..]); + found_available = true; } + + offset += line_end + 1; } - let total_memory_gb = total_memory_kb / 1024.0 / 1024.0; - let available_memory_gb = available_memory_kb / 1024.0 / 1024.0; + #[allow(clippy::cast_precision_loss)] + let total_memory_gb = total_memory_kb as f64 / 1024.0 / 1024.0; + #[allow(clippy::cast_precision_loss)] + let available_memory_gb = available_memory_kb as f64 / 1024.0 / 1024.0; let used_memory_gb = total_memory_gb - available_memory_gb; Ok((used_memory_gb, total_memory_gb)) From 07afedd0cc4a49630b23ea830fe56c847eff06da Mon Sep 17 00:00:00 2001 From: NotAShelf Date: Mon, 17 Nov 2025 21:42:38 +0300 Subject: [PATCH 23/48] docs: update binary size Signed-off-by: NotAShelf Change-Id: I1a9189f90666d7efc010a7255c287bd86a6a6964 --- README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 5ae0f72..a40e444 100644 --- a/README.md +++ b/README.md @@ -43,7 +43,7 @@ on your system: it is pretty _[fast](#benchmarks)_... - Fast - Really fast - Minimal dependencies -- Tiny binary (~410kb) +- Tiny binary (~370kb) - Actually really fast - Cool NixOS logo (other, inferior, distros are not supported) - Reliable detection of following info: @@ -160,7 +160,8 @@ performance regressions. > [!NOTE] > You will need a Nerdfonts patched font installed, and for your terminal -> emulator to support said font. Microfetch uses nerdfonts glyphs by default. +> emulator to support said font. Microfetch uses nerdfonts glyphs by default, +> but this can be changed by [patching the program](#customizing). Microfetch is packaged in [nixpkgs](https://github.com/nixos/nixpkgs). It can be installed by adding `pkgs.microfetch` to your `environment.systemPackages`. From 6f8d1ffa83fec677001e48de51c3a930885d647d Mon Sep 17 00:00:00 2001 From: NotAShelf Date: Mon, 17 Nov 2025 21:48:15 +0300 Subject: [PATCH 24/48] nix: include benches in source filter; fix build Signed-off-by: NotAShelf Change-Id: Id78ee5f62a5168feef09b5f8713b107c6a6a6964 --- nix/package.nix | 1 + 1 file changed, 1 insertion(+) diff --git a/nix/package.nix b/nix/package.nix index 5bb387c..dca3cc2 100644 --- a/nix/package.nix +++ b/nix/package.nix @@ -20,6 +20,7 @@ in (fs.fileFilter (file: builtins.any file.hasExt ["rs"]) (s + /src)) (s + /Cargo.lock) (s + /Cargo.toml) + (s + /benches) ]; }; From 353b78e688ddffd86540bf336b52568499f980ec Mon Sep 17 00:00:00 2001 From: NotAShelf Date: Tue, 18 Nov 2025 00:07:26 +0300 Subject: [PATCH 25/48] ci: fix hotpath-comment usage Signed-off-by: NotAShelf Change-Id: Idc163948f012efb07fc7a6a952af54b36a6a6964 --- .github/workflows/hotpath-comment.yml | 46 +++++++++++---------------- 1 file changed, 19 insertions(+), 27 deletions(-) diff --git a/.github/workflows/hotpath-comment.yml b/.github/workflows/hotpath-comment.yml index 395a533..8f83d00 100644 --- a/.github/workflows/hotpath-comment.yml +++ b/.github/workflows/hotpath-comment.yml @@ -13,7 +13,7 @@ jobs: comment: runs-on: ubuntu-latest if: ${{ github.event.workflow_run.conclusion == 'success' }} - + steps: - name: Download profiling results uses: actions/download-artifact@v4 @@ -21,37 +21,29 @@ jobs: name: hotpath-results github-token: ${{ secrets.GITHUB_TOKEN }} run-id: ${{ github.event.workflow_run.id }} - + - name: Read PR number id: pr run: echo "number=$(cat pr_number.txt)" >> $GITHUB_OUTPUT - + - name: Setup Rust uses: actions-rust-lang/setup-rust-toolchain@v1 - + - name: Install hotpath CLI run: cargo install hotpath - + - name: Post timing comparison comment - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: | - hotpath profile-pr \ - --repo ${{ github.repository }} \ - --pr-number ${{ steps.pr.outputs.number }} \ - --head-json head-timing.json \ - --base-json base-timing.json \ - --mode timing \ - --title "⏱️ Hotpath Timing Profile" - - - name: Post allocation comparison comment - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: | - hotpath profile-pr \ - --repo ${{ github.repository }} \ - --pr-number ${{ steps.pr.outputs.number }} \ - --head-json head-alloc.json \ - --base-json base-alloc.json \ - --mode alloc \ - --title "📊 Hotpath Allocation Profile" + run: | + hotpath profile-pr \ + --head-metrics head-timing.json \ + --base-metrics base-timing.json \ + --github-token ${{ secrets.GITHUB_TOKEN }} \ + --pr-number ${{ steps.pr.outputs.number }} + + - name: Post allocation comparison comment + run: | + hotpath profile-pr \ + --head-metrics head-alloc.json \ + --base-metrics base-alloc.json \ + --github-token ${{ secrets.GITHUB_TOKEN }} \ + --pr-number ${{ steps.pr.outputs.number }} From 6d6ee838bfb514920a71c0e09f9eb524935eb616 Mon Sep 17 00:00:00 2001 From: NotAShelf Date: Sun, 30 Nov 2025 15:04:34 +0300 Subject: [PATCH 26/48] chore: bump criterion Signed-off-by: NotAShelf Change-Id: I5623347364c5ed71c99676029f4261026a6a6964 --- Cargo.lock | 29 +++++++++++++++++++++++++---- Cargo.toml | 2 +- 2 files changed, 26 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0147746..423e558 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -17,6 +17,15 @@ dependencies = [ "memchr", ] +[[package]] +name = "alloca" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5a7d05ea6aea7e9e64d25b9156ba2fee3fdd659e34e41063cd2fc7cd020d7f4" +dependencies = [ + "cc", +] + [[package]] name = "anes" version = "0.1.6" @@ -271,10 +280,11 @@ dependencies = [ [[package]] name = "criterion" -version = "0.7.0" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1c047a62b0cc3e145fa84415a3191f628e980b194c2755aa12300a4e6cbd928" +checksum = "a0dfe5e9e71bdcf4e4954f7d14da74d1cdb92a3a07686452d1509652684b1aab" dependencies = [ + "alloca", "anes", "cast", "ciborium", @@ -283,6 +293,7 @@ dependencies = [ "itertools", "num-traits", "oorandom", + "page_size", "plotters", "rayon", "regex", @@ -294,9 +305,9 @@ dependencies = [ [[package]] name = "criterion-plot" -version = "0.6.0" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b1bcc0dc7dfae599d84ad0b1a55f80cde8af3725da8313b528da95ef783e338" +checksum = "5de36c2bee19fba779808f92bf5d9b0fa5a40095c277aba10c458a12b35d21d6" dependencies = [ "cast", "itertools", @@ -816,6 +827,16 @@ version = "11.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d6790f58c7ff633d8771f42965289203411a5e5c68388703c06e14f24770b41e" +[[package]] +name = "page_size" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30d5b2194ed13191c1999ae0704b7839fb18384fa22e49b57eeaa97d79ce40da" +dependencies = [ + "libc", + "winapi", +] + [[package]] name = "percent-encoding" version = "2.3.2" diff --git a/Cargo.toml b/Cargo.toml index e95ef5c..076441c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,7 +16,7 @@ hotpath = { optional = true, version = "0.6.0" } libc = "0.2.177" [dev-dependencies] -criterion = "0.7" +criterion = "0.8.0" [features] hotpath = [ "dep:hotpath", "hotpath/hotpath" ] From c3a6d1c93cfa22f21a45fc085eb589576e1fdafd Mon Sep 17 00:00:00 2001 From: NotAShelf Date: Sun, 30 Nov 2025 15:06:33 +0300 Subject: [PATCH 27/48] treewide: fix commonmark formatting in Rustdoc Signed-off-by: NotAShelf Change-Id: Ie1a13221aded56f903156fdb35abe2ac6a6a6964 --- src/lib.rs | 3 ++- src/syscall.rs | 11 +++++++++-- src/uptime.rs | 5 +++-- 3 files changed, 14 insertions(+), 5 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 8c8a6ba..1e0f9f3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -14,7 +14,8 @@ impl UtsName { /// Calls `uname` syscall and returns a `UtsName` wrapper /// /// # Errors - /// Returns an error if the uname syscall fails + /// + /// Returns an error if the `uname` syscall fails pub fn uname() -> Result { let mut uts = MaybeUninit::uninit(); if unsafe { libc::uname(uts.as_mut_ptr()) } != 0 { diff --git a/src/syscall.rs b/src/syscall.rs index 2776fe7..0c8634b 100644 --- a/src/syscall.rs +++ b/src/syscall.rs @@ -11,11 +11,15 @@ use std::io; /// Direct syscall to open a file -/// Returns file descriptor or -1 on error +/// +/// # Returns +/// +/// File descriptor or -1 on error /// /// # Safety /// /// The caller must ensure: +/// /// - `path` points to a valid null-terminated C string /// - The pointer remains valid for the duration of the syscall #[inline] @@ -65,7 +69,10 @@ pub unsafe fn sys_open(path: *const u8, flags: i32) -> i32 { } /// Direct syscall to read from a file descriptor -/// Returns number of bytes read or -1 on error +/// +/// # Returns n +/// +/// Number of bytes read or -1 on error /// /// # Safety /// diff --git a/src/uptime.rs b/src/uptime.rs index 98c9207..095af7d 100644 --- a/src/uptime.rs +++ b/src/uptime.rs @@ -1,6 +1,6 @@ use std::{io, mem::MaybeUninit}; -/// Fast integer to string conversion (no formatting overhead) +/// Faster integer to string conversion without the formatting overhead. #[inline] fn itoa(mut n: u64, buf: &mut [u8]) -> &str { if n == 0 { @@ -17,9 +17,10 @@ fn itoa(mut n: u64, buf: &mut [u8]) -> &str { unsafe { std::str::from_utf8_unchecked(&buf[i..]) } } -/// Direct sysinfo syscall using inline assembly +/// Direct `sysinfo` syscall using inline assembly /// /// # Safety +/// /// This function uses inline assembly to make a direct syscall. /// The caller must ensure the sysinfo pointer is valid. #[inline] From c1a4bc24f46420336d3aabf7edd2d56d1f6fc143 Mon Sep 17 00:00:00 2001 From: NotAShelf Date: Sun, 30 Nov 2025 15:06:48 +0300 Subject: [PATCH 28/48] docs: add note about handwritten assembly Signed-off-by: NotAShelf Change-Id: I6920a416e6169a84469514bae0f207426a6a6964 --- README.md | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index a40e444..1bd7124 100644 --- a/README.md +++ b/README.md @@ -82,11 +82,14 @@ Nixpkgs, you are recommended to use it to utilize the binary cache properly. The usage of Rust _is_ nice, however, since it provides us with incredible tooling and a very powerful language that allows for Microfetch to be as fast as possible. Sure C could've been used here as well, but do you think I hate -myself? [^1] +myself? -[^1]: Okay, maybe a little bit. One of the future goals of Microfetch is to - defer to inline Assembly for the costliest functions, but that's for a - future date and until I do that I can pretend to be sane. +> [!IMPORTANT] +> **Update as of November 30th, 2025**: +> +> Microfetch now inlines handwritten assembly for even better performance. I +> know I previously said I do not hate myself but I'm beginning to suspect this +> is no longer the case. Enjoy the performance benefits! ## Benchmarks From 09da1c27d21cea01bca93224726ddb3ed95d2441 Mon Sep 17 00:00:00 2001 From: NotAShelf Date: Sun, 30 Nov 2025 15:13:14 +0300 Subject: [PATCH 29/48] chore: bump hotpath Signed-off-by: NotAShelf Change-Id: I1ae1d9b3d25ff220bee51660efc598b76a6a6964 --- Cargo.lock | 75 +++++++++++++++++++++++++++++++++++++++++++++++++++--- Cargo.toml | 9 +++---- 2 files changed, 75 insertions(+), 9 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 423e558..88deaeb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -462,6 +462,50 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "futures-core" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" + +[[package]] +name = "futures-macro" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "futures-sink" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" + +[[package]] +name = "futures-task" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" + +[[package]] +name = "futures-util" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" +dependencies = [ + "futures-core", + "futures-macro", + "futures-sink", + "futures-task", + "pin-project-lite", + "pin-utils", + "slab", +] + [[package]] name = "getrandom" version = "0.2.16" @@ -513,9 +557,9 @@ checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c" [[package]] name = "hotpath" -version = "0.6.0" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef44238d7007bbd3c41ce7dbf3d7a4fb224bbf70d6cf3162479138dda0cd6a9e" +checksum = "08382b985a19a79d95d35e2e201b02cc4b99efe2f47d82f3fd4301bb0005bb68" dependencies = [ "arc-swap", "base64", @@ -524,10 +568,15 @@ dependencies = [ "colored", "crossbeam-channel", "eyre", + "futures-util", "hdrhistogram", "hotpath-macros", + "libc", + "mach2", + "pin-project-lite", "prettytable-rs", "quanta", + "regex", "serde", "serde_json", "tiny_http", @@ -537,9 +586,9 @@ dependencies = [ [[package]] name = "hotpath-macros" -version = "0.6.0" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e7e3443c6f4e5cc69e6b701f7bfca8a5502f0080e94629413346e7b4518d730" +checksum = "7d618063f89423ebe079a69f5435a13d4909219d4e359757118b75fd05ae65d0" dependencies = [ "proc-macro2", "quote", @@ -769,6 +818,12 @@ version = "0.4.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" +[[package]] +name = "mach2" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dae608c151f68243f2b000364e1f7b186d9c29845f7d2d85bd31b9ad77ad552b" + [[package]] name = "memchr" version = "2.7.4" @@ -849,6 +904,12 @@ version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + [[package]] name = "plotters" version = "0.3.7" @@ -1121,6 +1182,12 @@ version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" +[[package]] +name = "slab" +version = "0.4.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a2ae44ef20feb57a68b23d846850f861394c2e02dc425a50098ae8c90267589" + [[package]] name = "smallvec" version = "1.15.1" diff --git a/Cargo.toml b/Cargo.toml index 076441c..f7a2f81 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,17 +12,16 @@ name = "microfetch" path = "src/main.rs" [dependencies] -hotpath = { optional = true, version = "0.6.0" } +hotpath = { optional = true, version = "0.7.5" } libc = "0.2.177" [dev-dependencies] criterion = "0.8.0" [features] -hotpath = [ "dep:hotpath", "hotpath/hotpath" ] -hotpath-alloc-bytes-total = [ "hotpath/hotpath-alloc-bytes-total" ] -hotpath-alloc-count-total = [ "hotpath/hotpath-alloc-count-total" ] -hotpath-off = [ "hotpath/hotpath-off" ] +hotpath = [ "dep:hotpath", "hotpath/hotpath" ] +hotpath-alloc = [ "hotpath/hotpath-alloc" ] +hotpath-off = [ "hotpath/hotpath-off" ] [[bench]] harness = false From 48c3807148892b5888b342cc4a64601c8cf15800 Mon Sep 17 00:00:00 2001 From: NotAShelf Date: Sun, 30 Nov 2025 15:14:53 +0300 Subject: [PATCH 30/48] docs: update hotpath usage instructions Signed-off-by: NotAShelf Change-Id: I8a056b6814bef36e74b4aca56d4aa2686a6a6964 --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 1bd7124..aa299ce 100644 --- a/README.md +++ b/README.md @@ -151,7 +151,7 @@ HOTPATH_JSON=true cargo run --features=hotpath To profile allocations: ```bash -HOTPATH_JSON=true cargo run --features=hotpath,hotpath-alloc-count-total +HOTPATH_JSON=true cargo run --features=hotpath,hotpath-alloc ``` The JSON output can be analyzed with the `hotpath` CLI tool for detailed From 8d97a9e8ec543611c4fde53c688a13ebd3b9a552 Mon Sep 17 00:00:00 2001 From: NotAShelf Date: Sun, 30 Nov 2025 15:32:40 +0300 Subject: [PATCH 31/48] ci: streamline workflows; remove tagged release workflow Signed-off-by: NotAShelf Change-Id: I1432980533dee11b64a53d2ad2f2094d6a6a6964 --- .github/workflows/hotpath-comment.yml | 51 +++++++++++++++++---------- .github/workflows/hotpath-profile.yml | 30 ++++++++-------- 2 files changed, 48 insertions(+), 33 deletions(-) diff --git a/.github/workflows/hotpath-comment.yml b/.github/workflows/hotpath-comment.yml index 8f83d00..4e072fe 100644 --- a/.github/workflows/hotpath-comment.yml +++ b/.github/workflows/hotpath-comment.yml @@ -1,12 +1,13 @@ -name: Hotpath Comment +name: hotpath-comment on: workflow_run: - workflows: ["Hotpath Profile"] + workflows: ["hotpath-profile"] types: - completed permissions: + contents: read pull-requests: write jobs: @@ -15,17 +16,17 @@ jobs: if: ${{ github.event.workflow_run.conclusion == 'success' }} steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Download profiling results uses: actions/download-artifact@v4 with: name: hotpath-results + path: /tmp/ github-token: ${{ secrets.GITHUB_TOKEN }} run-id: ${{ github.event.workflow_run.id }} - - name: Read PR number - id: pr - run: echo "number=$(cat pr_number.txt)" >> $GITHUB_OUTPUT - - name: Setup Rust uses: actions-rust-lang/setup-rust-toolchain@v1 @@ -33,17 +34,29 @@ jobs: run: cargo install hotpath - name: Post timing comparison comment - run: | - hotpath profile-pr \ - --head-metrics head-timing.json \ - --base-metrics base-timing.json \ - --github-token ${{ secrets.GITHUB_TOKEN }} \ - --pr-number ${{ steps.pr.outputs.number }} + env: + GH_TOKEN: ${{ github.token }} + run: | + set -euo pipefail + HEAD_METRICS=$(cat /tmp/head_timing.json) + BASE_METRICS=$(cat /tmp/base_timing.json) + PR_NUMBER=$(cat /tmp/pr_number.txt) + hotpath profile-pr \ + --head-metrics "$HEAD_METRICS" \ + --base-metrics "$BASE_METRICS" \ + --github-token "$GH_TOKEN" \ + --pr-number "$PR_NUMBER" - - name: Post allocation comparison comment - run: | - hotpath profile-pr \ - --head-metrics head-alloc.json \ - --base-metrics base-alloc.json \ - --github-token ${{ secrets.GITHUB_TOKEN }} \ - --pr-number ${{ steps.pr.outputs.number }} + - name: Post allocation comparison comment + env: + GH_TOKEN: ${{ github.token }} + run: | + set -euo pipefail + HEAD_METRICS=$(cat /tmp/head_alloc.json) + BASE_METRICS=$(cat /tmp/base_alloc.json) + PR_NUMBER=$(cat /tmp/pr_number.txt) + hotpath profile-pr \ + --head-metrics "$HEAD_METRICS" \ + --base-metrics "$BASE_METRICS" \ + --github-token "$GH_TOKEN" \ + --pr-number "$PR_NUMBER" diff --git a/.github/workflows/hotpath-profile.yml b/.github/workflows/hotpath-profile.yml index b367ca2..1b4ce26 100644 --- a/.github/workflows/hotpath-profile.yml +++ b/.github/workflows/hotpath-profile.yml @@ -1,4 +1,4 @@ -name: Hotpath Profile +name: hotpath-profile on: pull_request: @@ -14,6 +14,8 @@ jobs: steps: - name: Checkout PR HEAD uses: actions/checkout@v4 + with: + fetch-depth: 0 - name: Setup Rust uses: actions-rust-lang/setup-rust-toolchain@v1 @@ -22,42 +24,42 @@ jobs: env: HOTPATH_JSON: "true" run: | - cargo run --features='hotpath' 2>&1 | grep '^{"hotpath_profiling_mode"' > head-timing.json + cargo run --features='hotpath' 2>&1 | grep '^{"hotpath_profiling_mode"' > /tmp/head_timing.json - name: Run allocation profiling on HEAD env: HOTPATH_JSON: "true" run: | - cargo run --features='hotpath,hotpath-alloc-count-total' 2>&1 | grep '^{"hotpath_profiling_mode"' > head-alloc.json + cargo run --features='hotpath,hotpath-alloc' 2>&1 | grep '^{"hotpath_profiling_mode"' > /tmp/head_alloc.json - name: Checkout base branch - uses: actions/checkout@v4 - with: - ref: ${{ github.event.pull_request.base.sha }} + run: | + git checkout ${{ github.event.pull_request.base.sha }} - name: Run timing profiling on base env: HOTPATH_JSON: "true" run: | - cargo run --features='hotpath' 2>&1 | grep '^{"hotpath_profiling_mode"' > base-timing.json + cargo run --features='hotpath' 2>&1 | grep '^{"hotpath_profiling_mode"' > /tmp/base_timing.json || echo '{}' > /tmp/base_timing.json - name: Run allocation profiling on base env: HOTPATH_JSON: "true" run: | - cargo run --features='hotpath,hotpath-alloc-count-total' 2>&1 | grep '^{"hotpath_profiling_mode"' > base-alloc.json + cargo run --features='hotpath,hotpath-alloc' 2>&1 | grep '^{"hotpath_profiling_mode"' > /tmp/base_alloc.json || echo '{}' > /tmp/base_alloc.json - name: Save PR number - run: echo "${{ github.event.number }}" > pr_number.txt + run: | + echo '${{ github.event.pull_request.number }}' > /tmp/pr_number.txt - name: Upload profiling results uses: actions/upload-artifact@v4 with: name: hotpath-results path: | - head-timing.json - head-alloc.json - base-timing.json - base-alloc.json - pr_number.txt + /tmp/head_timing.json + /tmp/head_alloc.json + /tmp/base_timing.json + /tmp/base_alloc.json + /tmp/pr_number.txt retention-days: 1 From 16a1d5fe3f8e2d2740f3eae326664621533edcfb Mon Sep 17 00:00:00 2001 From: NotAShelf Date: Sun, 30 Nov 2025 15:38:32 +0300 Subject: [PATCH 32/48] ci: set up dependabot for GH actions Signed-off-by: NotAShelf Change-Id: I890272e62db5824a3d866748375d1a9f6a6a6964 --- .github/dependabot.yml | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 .github/dependabot.yml diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..a1067c4 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,14 @@ +version: 2 +updates: + # Update Cargo deps + - package-ecosystem: cargo + directory: "/" + schedule: + interval: "weekly" + + # Update used workflows + - package-ecosystem: github-actions + directory: "/" + schedule: + interval: daily + From fa8b6b9d689d8bd7bef306b07de03b555d573018 Mon Sep 17 00:00:00 2001 From: NotAShelf Date: Sun, 30 Nov 2025 16:54:35 +0300 Subject: [PATCH 33/48] ci: set up cross comp for Rust builds Signed-off-by: NotAShelf Change-Id: I154d2afb05088e22882985bcd4536f026a6a6964 --- .github/workflows/rust.yml | 43 +++++++++++++++++++++++++++++++++----- 1 file changed, 38 insertions(+), 5 deletions(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index e87e1da..b742a24 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -10,12 +10,45 @@ env: CARGO_TERM_COLOR: always jobs: - build: - - runs-on: ubuntu-latest + test: + name: Test on ${{ matrix.target }} + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + include: + - os: ubuntu-latest + target: x86_64-unknown-linux-gnu + - os: ubuntu-latest + target: aarch64-unknown-linux-gnu steps: - name: "Checkout" uses: actions/checkout@v4 - - name: "Build with Cargo" - run: cargo build --verbose + + - name: "Setup Rust toolchain" + uses: actions-rust-lang/setup-rust-toolchain@v1 + with: + target: ${{ matrix.target }} + + - name: "Install cross-compilation tools" + if: matrix.target == 'aarch64-unknown-linux-gnu' + run: | + sudo apt-get update + sudo apt-get install -y gcc-aarch64-linux-gnu + + - name: "Configure linker for aarch64" + if: matrix.target == 'aarch64-unknown-linux-gnu' + run: | + mkdir -p .cargo + cat >> .cargo/config.toml << EOF + [target.aarch64-unknown-linux-gnu] + linker = "aarch64-linux-gnu-gcc" + EOF + + - name: "Build" + run: cargo build --verbose --target ${{ matrix.target }} + + - name: "Run tests" + if: matrix.target == 'x86_64-unknown-linux-gnu' + run: cargo test --verbose --target ${{ matrix.target }} From 5196ced4874a6ee4e104ca9ab8d9d4ec34d8dbb4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 30 Nov 2025 14:01:24 +0000 Subject: [PATCH 34/48] chore(deps): bump actions/download-artifact from 4 to 6 Bumps [actions/download-artifact](https://github.com/actions/download-artifact) from 4 to 6. - [Release notes](https://github.com/actions/download-artifact/releases) - [Commits](https://github.com/actions/download-artifact/compare/v4...v6) --- updated-dependencies: - dependency-name: actions/download-artifact dependency-version: '6' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/hotpath-comment.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/hotpath-comment.yml b/.github/workflows/hotpath-comment.yml index 4e072fe..cdaa27d 100644 --- a/.github/workflows/hotpath-comment.yml +++ b/.github/workflows/hotpath-comment.yml @@ -20,7 +20,7 @@ jobs: uses: actions/checkout@v4 - name: Download profiling results - uses: actions/download-artifact@v4 + uses: actions/download-artifact@v6 with: name: hotpath-results path: /tmp/ From 5c23cb266527b07f06acfaad36f7b13dc46c9219 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 30 Nov 2025 14:01:27 +0000 Subject: [PATCH 35/48] chore(deps): bump actions/upload-artifact from 4 to 5 Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 4 to 5. - [Release notes](https://github.com/actions/upload-artifact/releases) - [Commits](https://github.com/actions/upload-artifact/compare/v4...v5) --- updated-dependencies: - dependency-name: actions/upload-artifact dependency-version: '5' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/hotpath-profile.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/hotpath-profile.yml b/.github/workflows/hotpath-profile.yml index 1b4ce26..84f0e27 100644 --- a/.github/workflows/hotpath-profile.yml +++ b/.github/workflows/hotpath-profile.yml @@ -53,7 +53,7 @@ jobs: echo '${{ github.event.pull_request.number }}' > /tmp/pr_number.txt - name: Upload profiling results - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v5 with: name: hotpath-results path: | From d2a981b0705efdf05b69b45cc88e1f290ff36e2f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 30 Nov 2025 14:01:31 +0000 Subject: [PATCH 36/48] chore(deps): bump actions/checkout from 4 to 6 Bumps [actions/checkout](https://github.com/actions/checkout) from 4 to 6. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/v4...v6) --- updated-dependencies: - dependency-name: actions/checkout dependency-version: '6' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/hotpath-comment.yml | 2 +- .github/workflows/hotpath-profile.yml | 2 +- .github/workflows/rust.yml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/hotpath-comment.yml b/.github/workflows/hotpath-comment.yml index 4e072fe..b22b541 100644 --- a/.github/workflows/hotpath-comment.yml +++ b/.github/workflows/hotpath-comment.yml @@ -17,7 +17,7 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: Download profiling results uses: actions/download-artifact@v4 diff --git a/.github/workflows/hotpath-profile.yml b/.github/workflows/hotpath-profile.yml index 1b4ce26..4fd42a9 100644 --- a/.github/workflows/hotpath-profile.yml +++ b/.github/workflows/hotpath-profile.yml @@ -13,7 +13,7 @@ jobs: steps: - name: Checkout PR HEAD - uses: actions/checkout@v4 + uses: actions/checkout@v6 with: fetch-depth: 0 diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index b742a24..5f435fb 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -24,7 +24,7 @@ jobs: steps: - name: "Checkout" - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: "Setup Rust toolchain" uses: actions-rust-lang/setup-rust-toolchain@v1 From ff8af87747f67e9cdd17bd0dfb7dcdd987836381 Mon Sep 17 00:00:00 2001 From: NotAShelf Date: Sun, 30 Nov 2025 17:01:51 +0300 Subject: [PATCH 37/48] chore: tag 0.4.12 Signed-off-by: NotAShelf Change-Id: I54a5f2181efdd42af2fda7cebb88484f6a6a6964 --- Cargo.lock | 2 +- Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 88deaeb..3a23f75 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -832,7 +832,7 @@ checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" [[package]] name = "microfetch" -version = "0.4.11" +version = "0.4.12" dependencies = [ "criterion", "hotpath", diff --git a/Cargo.toml b/Cargo.toml index f7a2f81..c245c00 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "microfetch" -version = "0.4.11" +version = "0.4.12" edition = "2024" [lib] From 3ad14a95a650f94b9fb4621b6c19d5ffd572cfc5 Mon Sep 17 00:00:00 2001 From: NotAShelf Date: Sun, 30 Nov 2025 17:23:57 +0300 Subject: [PATCH 38/48] meta: build with Mold linker on x86_64 Linux Signed-off-by: NotAShelf Change-Id: Id51e62dda1ec2ba895ffdbbd25c2f1256a6a6964 --- .cargo/config.toml | 4 ++++ nix/shell.nix | 12 ++++++++---- 2 files changed, 12 insertions(+), 4 deletions(-) create mode 100644 .cargo/config.toml diff --git a/.cargo/config.toml b/.cargo/config.toml new file mode 100644 index 0000000..b7efcee --- /dev/null +++ b/.cargo/config.toml @@ -0,0 +1,4 @@ +# https://github.com/rui314/mold?tab=readme-ov-file#how-to-use +[target.'cfg(target_os = "linux")'] +linker = "clang" +rustflags = ["-C", "link-arg=-fuse-ld=mold"] diff --git a/nix/shell.nix b/nix/shell.nix index 111b803..ae65e10 100644 --- a/nix/shell.nix +++ b/nix/shell.nix @@ -1,27 +1,31 @@ { mkShell, + cargo, + rustc, + mold, + clang, rust-analyzer-unwrapped, rustfmt, clippy, - cargo, taplo, - rustc, rustPlatform, gnuplot, }: mkShell { + name = "microfetch"; strictDeps = true; - nativeBuildInputs = [ cargo rustc + mold + clang rust-analyzer-unwrapped (rustfmt.override {asNightly = true;}) clippy taplo - gnuplot # For Criterion.rs plots + gnuplot # for Criterion.rs plots ]; env.RUST_SRC_PATH = "${rustPlatform.rustLibSrc}"; From 8c32f5f4089c862eb1dea153ac41682e33a30bea Mon Sep 17 00:00:00 2001 From: NotAShelf Date: Sun, 30 Nov 2025 17:24:19 +0300 Subject: [PATCH 39/48] nix: build with Mold linker on x86_64-linux Signed-off-by: NotAShelf Change-Id: I771c36297577aba189058a2183ec2b4a6a6a6964 --- README.md | 5 ++++- flake.nix | 7 +++++-- nix/package.nix | 21 ++++++++++++++++++--- 3 files changed, 27 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index aa299ce..f148a61 100644 --- a/README.md +++ b/README.md @@ -43,7 +43,7 @@ on your system: it is pretty _[fast](#benchmarks)_... - Fast - Really fast - Minimal dependencies -- Tiny binary (~370kb) +- Tiny binary (~370kb [^1]) - Actually really fast - Cool NixOS logo (other, inferior, distros are not supported) - Reliable detection of following info: @@ -60,6 +60,9 @@ on your system: it is pretty _[fast](#benchmarks)_... - Did I mention fast? - Respects [`NO_COLOR` spec](https://no-color.org/) +[^1]: With the Mold linker, which is enabled by default in the Flake package, + the binary size is roughly 350kb. That's nearly 20kb reduction in size :) + ## Motivation Fastfetch, as its name probably hinted, is a very fast fetch tool written in C. diff --git a/flake.nix b/flake.nix index ed36872..90978a2 100644 --- a/flake.nix +++ b/flake.nix @@ -10,9 +10,12 @@ forEachSystem = nixpkgs.lib.genAttrs systems; pkgsForEach = nixpkgs.legacyPackages; in { - packages = forEachSystem (system: { + packages = forEachSystem (system: let + pkgs = pkgsForEach.${system}; + in { default = self.packages.${system}.microfetch; - microfetch = pkgsForEach.${system}.callPackage ./nix/package.nix {}; + microfetch = pkgs.callPackage ./nix/package.nix {}; + microfetch-mold = pkgs.callPackage ./nix/package.nix {useMold = true;}; }); devShells = forEachSystem (system: { diff --git a/nix/package.nix b/nix/package.nix index dca3cc2..052f576 100644 --- a/nix/package.nix +++ b/nix/package.nix @@ -1,14 +1,22 @@ { lib, - rustPlatform, + stdenv, stdenvAdapters, + rustPlatform, llvm, + useMold ? stdenv.isLinux && !stdenv.hostPlatform.isAarch, }: let toml = (lib.importTOML ../Cargo.toml).package; pname = toml.name; inherit (toml) version; + + # Select stdenv based on useMold flag + stdenv = + if useMold + then stdenvAdapters.useMoldLinker llvm.stdenv + else llvm.stdenv; in - rustPlatform.buildRustPackage.override {stdenv = stdenvAdapters.useMoldLinker llvm.stdenv;} { + rustPlatform.buildRustPackage.override {inherit stdenv;} { inherit pname version; src = let fs = lib.fileset; @@ -26,7 +34,14 @@ in cargoLock.lockFile = ../Cargo.lock; enableParallelBuilding = true; - env.RUSTFLAGS = "-C link-arg=-fuse-ld=mold"; + buildNoDefaultFeatures = true; + doCheck = false; + + # Only set RUSTFLAGS for mold if useMold is enabled + env = lib.optionalAttrs useMold { + CARGO_LINKER = "clang"; + RUSTFLAGS = "-C link-arg=-fuse-ld=mold"; + }; meta = { description = "Microscopic fetch script in Rust, for NixOS systems"; From 8376e9d32341c44f5bbf07e5020340326093f4b1 Mon Sep 17 00:00:00 2001 From: Uzair Aftab Date: Sun, 30 Nov 2025 18:00:55 +0100 Subject: [PATCH 40/48] perf: use libc to fetch env vars --- src/colors.rs | 6 +++--- src/desktop.rs | 29 +++++++++++++++++------------ src/system.rs | 33 +++++++++++++++++++++------------ 3 files changed, 41 insertions(+), 27 deletions(-) diff --git a/src/colors.rs b/src/colors.rs index 07ab9bd..7c65944 100644 --- a/src/colors.rs +++ b/src/colors.rs @@ -1,4 +1,4 @@ -use std::{env, sync::LazyLock}; +use std::sync::LazyLock; pub struct Colors { pub reset: &'static str, @@ -37,8 +37,8 @@ impl Colors { } pub static COLORS: LazyLock = LazyLock::new(|| { - // Check for NO_COLOR once at startup - let is_no_color = env::var("NO_COLOR").is_ok(); + const NO_COLOR: *const libc::c_char = c"NO_COLOR".as_ptr(); + let is_no_color = unsafe { !libc::getenv(NO_COLOR).is_null() }; Colors::new(is_no_color) }); diff --git a/src/desktop.rs b/src/desktop.rs index 561be03..501e967 100644 --- a/src/desktop.rs +++ b/src/desktop.rs @@ -1,22 +1,27 @@ -use std::fmt::Write; +use std::{ffi::CStr, fmt::Write}; #[must_use] #[cfg_attr(feature = "hotpath", hotpath::measure)] pub fn get_desktop_info() -> String { // Retrieve the environment variables and handle Result types - let desktop_env = std::env::var("XDG_CURRENT_DESKTOP"); - let display_backend = std::env::var("XDG_SESSION_TYPE"); - - let desktop_str = match desktop_env { - Err(_) => "Unknown", - Ok(ref s) if s.starts_with("none+") => &s[5..], - Ok(ref s) => s.as_str(), + let desktop_str = unsafe { + let ptr = libc::getenv(c"XDG_CURRENT_DESKTOP".as_ptr()); + if ptr.is_null() { + "Unknown" + } else { + let s = CStr::from_ptr(ptr).to_str().unwrap_or("Unknown"); + s.strip_prefix("none+").unwrap_or(s) + } }; - let backend_str = match display_backend { - Err(_) => "Unknown", - Ok(ref s) if s.is_empty() => "Unknown", - Ok(ref s) => s.as_str(), + let backend_str = unsafe { + let ptr = libc::getenv(c"XDG_SESSION_TYPE".as_ptr()); + if ptr.is_null() { + "Unknown" + } else { + let s = CStr::from_ptr(ptr).to_str().unwrap_or("Unknown"); + if s.is_empty() { "Unknown" } else { s } + } }; // Pre-calculate capacity: desktop_len + " (" + backend_len + ")" diff --git a/src/system.rs b/src/system.rs index 24406ec..ba8fe79 100644 --- a/src/system.rs +++ b/src/system.rs @@ -1,11 +1,18 @@ -use std::{env, fmt::Write as _, io, mem::MaybeUninit}; +use std::{ffi::CStr, fmt::Write as _, io, mem::MaybeUninit}; use crate::{UtsName, colors::COLORS, syscall::read_file_fast}; #[must_use] #[cfg_attr(feature = "hotpath", hotpath::measure)] pub fn get_username_and_hostname(utsname: &UtsName) -> String { - let username = env::var("USER").unwrap_or_else(|_| "unknown_user".to_owned()); + let username = unsafe { + let ptr = libc::getenv(c"USER".as_ptr()); + if ptr.is_null() { + "unknown_user" + } else { + CStr::from_ptr(ptr).to_str().unwrap_or("unknown_user") + } + }; let hostname = utsname.nodename().to_str().unwrap_or("unknown_host"); let capacity = COLORS.yellow.len() @@ -18,7 +25,7 @@ pub fn get_username_and_hostname(utsname: &UtsName) -> String { let mut result = String::with_capacity(capacity); result.push_str(COLORS.yellow); - result.push_str(&username); + result.push_str(username); result.push_str(COLORS.red); result.push('@'); result.push_str(COLORS.green); @@ -31,15 +38,17 @@ pub fn get_username_and_hostname(utsname: &UtsName) -> String { #[must_use] #[cfg_attr(feature = "hotpath", hotpath::measure)] pub fn get_shell() -> String { - let shell_path = - env::var("SHELL").unwrap_or_else(|_| "unknown_shell".to_owned()); + unsafe { + let ptr = libc::getenv(c"SHELL".as_ptr()); + if ptr.is_null() { + return "unknown_shell".into(); + } - // Find last '/' and get the part after it, avoiding allocation - shell_path - .rsplit('/') - .next() - .unwrap_or("unknown_shell") - .to_owned() + let bytes = CStr::from_ptr(ptr).to_bytes(); + let start = bytes.iter().rposition(|&b| b == b'/').map_or(0, |i| i + 1); + let name = std::str::from_utf8_unchecked(&bytes[start..]); + name.into() + } } /// Gets the root disk usage information. @@ -106,7 +115,7 @@ pub fn get_memory_usage() -> Result { fn parse_memory_info() -> Result<(f64, f64), io::Error> { let mut total_memory_kb = 0u64; let mut available_memory_kb = 0u64; - let mut buffer = [0u8; 2048]; + let mut buffer = [0u8; 1024]; // Use fast syscall-based file reading let bytes_read = read_file_fast("/proc/meminfo", &mut buffer)?; From 9da87b933dd3a5b4d92860aef958e8660d8d2529 Mon Sep 17 00:00:00 2001 From: Uzair Aftab Date: Sun, 30 Nov 2025 19:05:05 +0100 Subject: [PATCH 41/48] perf: use stack buffer and direct write syscall in print_system_info Eliminates ~1KB stdout buffering allocation by using Cursor<&mut [u8]> and libc::write instead of format!() + stdout().write_all() --- src/main.rs | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/src/main.rs b/src/main.rs index 4e664bb..d02f16e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -5,7 +5,7 @@ mod syscall; mod system; mod uptime; -use std::io::{Write, stdout}; +use std::io::{self, Cursor, Write}; pub use microfetch_lib::UtsName; @@ -81,7 +81,13 @@ fn print_system_info( let cyan = COLORS.cyan; let blue = COLORS.blue; let reset = COLORS.reset; - let system_info = format!(" + + let mut buf = [0u8; 2048]; + let mut cursor = Cursor::new(&mut buf[..]); + + write!( + cursor, + " {cyan} ▟█▖ {blue}▝█▙ ▗█▛ {user_info} ~{reset} {cyan} ▗▄▄▟██▄▄▄▄▄{blue}▝█▙█▛ {cyan}▖ {cyan} {blue}System{reset}  {os_name} {cyan} ▀▀▀▀▀▀▀▀▀▀▀▘{blue}▝██ {cyan}▟█▖ {cyan} {blue}Kernel{reset}  {kernel_version} @@ -90,7 +96,16 @@ fn print_system_info( {blue} ▟█▛{cyan}▗█▖ {cyan}▟█▛ {cyan} {blue}Desktop{reset}  {desktop} {blue} ▝█▛ {cyan}██▖{blue}▗▄▄▄▄▄▄▄▄▄▄▄ {cyan} {blue}Memory{reset}  {memory_usage} {blue} ▝ {cyan}▟█▜█▖{blue}▀▀▀▀▀██▛▀▀▘ {cyan}󱥎 {blue}Storage (/){reset}  {storage} - {cyan} ▟█▘ ▜█▖ {blue}▝█▛ {cyan} {blue}Colors{reset}  {colors}\n"); + {cyan} ▟█▘ ▜█▖ {blue}▝█▛ {cyan} {blue}Colors{reset}  {colors}\n" + )?; - Ok(stdout().write_all(system_info.as_bytes())?) + #[allow(clippy::cast_possible_truncation)] + let len = cursor.position() as usize; + + // Direct syscall to avoid stdout buffering allocation + let written = unsafe { libc::write(libc::STDOUT_FILENO, buf.as_ptr().cast(), len) }; + if written < 0 { + return Err(io::Error::last_os_error().into()); + } + Ok(()) } From 80e64a1c70202cbe1110ebd43a43562d43bd468a Mon Sep 17 00:00:00 2001 From: poz Date: Mon, 1 Dec 2025 20:36:31 +0100 Subject: [PATCH 42/48] add .direnv to gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 8ea0ee8..8799178 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ /target result* +/.direnv From e4880661ae5453b61ca40ebff90aed8affc24c31 Mon Sep 17 00:00:00 2001 From: poz Date: Mon, 1 Dec 2025 20:37:01 +0100 Subject: [PATCH 43/48] print extra newline after fetch for better looks --- src/main.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main.rs b/src/main.rs index 4e664bb..ab93580 100644 --- a/src/main.rs +++ b/src/main.rs @@ -90,7 +90,7 @@ fn print_system_info( {blue} ▟█▛{cyan}▗█▖ {cyan}▟█▛ {cyan} {blue}Desktop{reset}  {desktop} {blue} ▝█▛ {cyan}██▖{blue}▗▄▄▄▄▄▄▄▄▄▄▄ {cyan} {blue}Memory{reset}  {memory_usage} {blue} ▝ {cyan}▟█▜█▖{blue}▀▀▀▀▀██▛▀▀▘ {cyan}󱥎 {blue}Storage (/){reset}  {storage} - {cyan} ▟█▘ ▜█▖ {blue}▝█▛ {cyan} {blue}Colors{reset}  {colors}\n"); + {cyan} ▟█▘ ▜█▖ {blue}▝█▛ {cyan} {blue}Colors{reset}  {colors}\n\n"); Ok(stdout().write_all(system_info.as_bytes())?) } From f0cf18dba708e03d3878d26ced25c00daec4a128 Mon Sep 17 00:00:00 2001 From: Uzair Aftab Date: Tue, 9 Dec 2025 19:36:29 +0100 Subject: [PATCH 44/48] fix: handle partial writes from libc::write --- src/main.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/main.rs b/src/main.rs index d02f16e..5e37d85 100644 --- a/src/main.rs +++ b/src/main.rs @@ -107,5 +107,8 @@ fn print_system_info( if written < 0 { return Err(io::Error::last_os_error().into()); } + if written as usize != len { + return Err(io::Error::new(io::ErrorKind::WriteZero, "partial write to stdout").into()); + } Ok(()) } From 7b8e736ff74c4b183a4f129e7f6884e9140aa360 Mon Sep 17 00:00:00 2001 From: NotAShelf Date: Wed, 10 Dec 2025 11:06:58 +0300 Subject: [PATCH 45/48] microfetch: update memory icon Fixes #34 Signed-off-by: NotAShelf Change-Id: Iff9da39a11eb4060eb314343efa2674b6a6a6964 --- src/main.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main.rs b/src/main.rs index c3e7c69..e29d6d1 100644 --- a/src/main.rs +++ b/src/main.rs @@ -94,7 +94,7 @@ fn print_system_info( {blue} ▟█▛ {blue}▝█▘{cyan}▟█▛ {cyan} {blue}Shell{reset}  {shell} {blue}▟█████▛ {cyan}▟█████▛ {cyan} {blue}Uptime{reset}  {uptime} {blue} ▟█▛{cyan}▗█▖ {cyan}▟█▛ {cyan} {blue}Desktop{reset}  {desktop} - {blue} ▝█▛ {cyan}██▖{blue}▗▄▄▄▄▄▄▄▄▄▄▄ {cyan} {blue}Memory{reset}  {memory_usage} + {blue} ▝█▛ {cyan}██▖{blue}▗▄▄▄▄▄▄▄▄▄▄▄ {cyan}󰍛 {blue}Memory{reset}  {memory_usage} {blue} ▝ {cyan}▟█▜█▖{blue}▀▀▀▀▀██▛▀▀▘ {cyan}󱥎 {blue}Storage (/){reset}  {storage} {cyan} ▟█▘ ▜█▖ {blue}▝█▛ {cyan} {blue}Colors{reset}  {colors}\n\n" )?; From cf3321b17ac6e865ef0ee17e930589226caf1610 Mon Sep 17 00:00:00 2001 From: NotAShelf Date: Wed, 10 Dec 2025 12:06:13 +0300 Subject: [PATCH 46/48] chore: bump dependencies Signed-off-by: NotAShelf Change-Id: I95094ead2493107a2d4bdd3f797fee246a6a6964 --- Cargo.lock | 12 ++++++------ Cargo.toml | 4 ++-- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3a23f75..e2397ab 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -557,9 +557,9 @@ checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c" [[package]] name = "hotpath" -version = "0.7.5" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08382b985a19a79d95d35e2e201b02cc4b99efe2f47d82f3fd4301bb0005bb68" +checksum = "4b0a2c66c081fe3684a54a7e5d059c9d9ad6b3ee5ccea14f6e4f056dbd77becf" dependencies = [ "arc-swap", "base64", @@ -586,9 +586,9 @@ dependencies = [ [[package]] name = "hotpath-macros" -version = "0.7.5" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d618063f89423ebe079a69f5435a13d4909219d4e359757118b75fd05ae65d0" +checksum = "a38fa43ca80cf906cd05127e490d740a51abb38316db7bce9d95e89724a81761" dependencies = [ "proc-macro2", "quote", @@ -786,9 +786,9 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "libc" -version = "0.2.177" +version = "0.2.178" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976" +checksum = "37c93d8daa9d8a012fd8ab92f088405fb202ea0b6ab73ee2482ae66af4f42091" [[package]] name = "libredox" diff --git a/Cargo.toml b/Cargo.toml index c245c00..9f6cc4a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,8 +12,8 @@ name = "microfetch" path = "src/main.rs" [dependencies] -hotpath = { optional = true, version = "0.7.5" } -libc = "0.2.177" +hotpath = { optional = true, version = "0.8.0" } +libc = "0.2.178" [dev-dependencies] criterion = "0.8.0" From 6640fdd559afebbe4c05fb4d5b054206d30d1d05 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 10 Dec 2025 09:08:02 +0000 Subject: [PATCH 47/48] chore(deps): bump criterion from 0.8.0 to 0.8.1 Bumps [criterion](https://github.com/criterion-rs/criterion.rs) from 0.8.0 to 0.8.1. - [Release notes](https://github.com/criterion-rs/criterion.rs/releases) - [Changelog](https://github.com/criterion-rs/criterion.rs/blob/master/CHANGELOG.md) - [Commits](https://github.com/criterion-rs/criterion.rs/compare/criterion-v0.8.0...criterion-v0.8.1) --- updated-dependencies: - dependency-name: criterion dependency-version: 0.8.1 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- Cargo.lock | 8 ++++---- Cargo.toml | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e2397ab..49e7cf4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -280,9 +280,9 @@ dependencies = [ [[package]] name = "criterion" -version = "0.8.0" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0dfe5e9e71bdcf4e4954f7d14da74d1cdb92a3a07686452d1509652684b1aab" +checksum = "4d883447757bb0ee46f233e9dc22eb84d93a9508c9b868687b274fc431d886bf" dependencies = [ "alloca", "anes", @@ -305,9 +305,9 @@ dependencies = [ [[package]] name = "criterion-plot" -version = "0.8.0" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5de36c2bee19fba779808f92bf5d9b0fa5a40095c277aba10c458a12b35d21d6" +checksum = "ed943f81ea2faa8dcecbbfa50164acf95d555afec96a27871663b300e387b2e4" dependencies = [ "cast", "itertools", diff --git a/Cargo.toml b/Cargo.toml index 9f6cc4a..0c70446 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,7 +16,7 @@ hotpath = { optional = true, version = "0.8.0" } libc = "0.2.178" [dev-dependencies] -criterion = "0.8.0" +criterion = "0.8.1" [features] hotpath = [ "dep:hotpath", "hotpath/hotpath" ] From 2066d781fe95c7c131c8d5006145b2a4b63682a7 Mon Sep 17 00:00:00 2001 From: NotAShelf Date: Wed, 10 Dec 2025 12:34:05 +0300 Subject: [PATCH 48/48] docs: update motivation section a little God redditors are insufferable Signed-off-by: NotAShelf Change-Id: I468d0fe0c1aaaa8d08d41373cfc8993c6a6a6964 --- README.md | 114 ++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 71 insertions(+), 43 deletions(-) diff --git a/README.md b/README.md index f148a61..8f59640 100644 --- a/README.md +++ b/README.md @@ -6,15 +6,11 @@

-

- Microfetch -

-

- Microscopic fetch tool in Rust, for NixOS systems, with special emphasis on speed -

+

Microfetch

+

Microscopic fetch tool in Rust, for NixOS systems, with special emphasis on speed


Synopsis
- Features | Motivation
+ Features | Motivation
| Benchmarks
Installation
@@ -65,34 +61,42 @@ on your system: it is pretty _[fast](#benchmarks)_... ## Motivation -Fastfetch, as its name probably hinted, is a very fast fetch tool written in C. -However, I am not interested in _any_ of its additional features, and I'm not -interested in its configuration options. Sure I can _configure_ it when I -dislike the defaults, but how often would I really change the configuration... +[Rube-Goldmark Machine]: https://en.wikipedia.org/wiki/Rube_Goldberg_machine -Microfetch is my response to this problem. It is an _even faster_ fetch tool -that I would've written in Bash and put in my `~/.bashrc` but is _actually_ -incredibly fast because it opts out of all the customization options provided by -tools such as Fastfetch. Ultimately, it's a small, opinionated binary with a -nice size that doesn't bother me, and incredible speed. Customization? No thank -you. I cannot re-iterate it enough, Microfetch is _annoyingly fast_. +Fastfetch, as its name _probably_ already hinted, is a very fast fetch tool +written in C. I used to use Fastfetch on my systems, but I eventually came to +the realization that I am _not interested in any of its additional features_. I +don't use Sixel, I don't change my configuration more than maybe once a year and +I don't even display most of the fields that it does. Sure the configurability +is nice and I can configure the defaults that I do not like but how often do I +really do that? -The project is written in Rust, which comes at the cost of "bloated" dependency -trees and the increased build times, but we make an extended effort to keep the -dependencies minimal and build times managable. The latter is also very easily -mitigated with Nix's binary cache systems. Since Microfetch is already in -Nixpkgs, you are recommended to use it to utilize the binary cache properly. The -usage of Rust _is_ nice, however, since it provides us with incredible tooling -and a very powerful language that allows for Microfetch to be as fast as -possible. Sure C could've been used here as well, but do you think I hate -myself? +Since I already enjoy programming challenges, and don't use a fetch program that +often, I eventually came to try and answer the question _how fast can I make my +fetch script?_ It is an _even faster_ fetch tool that I would've written in Bash +and put in my `~/.bashrc` but is _actually_ incredibly fast because it opts out +of all the customization options provided by tools such as Fastfetch. Since +Fetch scripts are kind of a coming-of-age ritual for most Linux users, I've +decided to use it on my system. You also might be interested if you like the +defaults and like speed. -> [!IMPORTANT] -> **Update as of November 30th, 2025**: -> -> Microfetch now inlines handwritten assembly for even better performance. I -> know I previously said I do not hate myself but I'm beginning to suspect this -> is no longer the case. Enjoy the performance benefits! +Ultimately, it's a small, opinionated binary with a nice size that doesn't +bother me, and incredible speed. Customization? No thank you. I cannot +re-iterate it enough, Microfetch is _annoyingly fast_. It does not, however, +solve a technical problem. The "problem" Microfetch solves is entirely +self-imposed. On the matter of _size_, the project is written in Rust, which +comes at the cost of "bloated" dependency trees and the increased build times, +but we make an extended effort to keep the dependencies minimal and build times +managable. The latter is also very easily mitigated with Nix's binary cache +systems. Since Microfetch is already in Nixpkgs, you are recommended to use it +to utilize the binary cache properly. The usage of Rust _is_ nice, however, +since it provides us with incredible tooling and a very powerful language that +allows for Microfetch to be as fast as possible. ~~Sure C could've been used +here as well, but do you think I hate myself?~~ Microfetch now features +handwritten assembly to unsafely optimize some areas. In hindsight you all +should have seen this coming. Is it faster? Yes. + +Also see: [Rube-Goldmark Machine] ## Benchmarks @@ -200,17 +204,31 @@ You can't. ### Why? -Customization, of any kind, is expensive: I could try reading environment -variables, parse command-line arguments or read a configuration file but all of -those increment execution time and resource consumption by a lot. +Customization, of most kinds, are expensive: I could try reading environment +variables, parse command-line arguments or read a configuration file to allow +configuring various fields but those inflate execution time and the resource +consumption by a lot. Since Microfetch is closer to a code golf challenge than a +program that attempts to fill a gap, I have elected not to make this trade. ### Really? -To be fair, you _can_ customize Microfetch by, well, patching it. It's not the -best way per se, but it will be the only way that does not compromise on speed. +[main module]: ./src/main.rs +[discussions tab]: https://github.com/NotAShelf/microfetch/discussions + +To be fair, you _can_ customize Microfetch by, well, patching it. It is +certainly not the easiest way of doing so but if you are planning to change +something in Microfetch, patching is the best way to go. It will also the only +way that does not compromise on speed, unless you patch in bad code. Various +users have adapted Microfetch to their distribution by patching the +[main module] and inserting the logo of their choice. This is also the best way +to go if you plan to make small changes. If your changes are not small, you +might want to look for a program that is designed to be customizable; Microfetch +is built for maximum performance. The Nix package allows passing patches in a streamlined manner by passing -`.overrideAttrs` to the derivation. +`.overrideAttrs` to the derivation. You can apply your patches in `patches` and +share your derivations with people. Feel free to use the [discussions tab] to +share your own variants of Microfetch! ## Contributing @@ -222,13 +240,22 @@ Contributions that help improve performance in specific areas of Microfetch are welcome. Though, prepare to be bombarded with questions if your changes are large. -## Hacking +### Hacking -A Nix flake is provided. `nix develop` to get started. Direnv users may simply -run `direnv allow` to get started. +A Nix flake is provided. You may use `nix develop` to get started. Direnv users +may instead run `direnv allow` to get a complete environment with shell +integration. -Non-nix users will need `cargo` and `gcc` installed on their system, see -`Cargo.toml` for available release profiles. +Non-Nix user will need `cargo`, `clang` and `mold` installed on their system to +build Microfetch. As Mold seems to yield _slightly_ better results than the +default linker, it has been set as the default in `.cargo/config.toml` for +x86-64 Linux. You may override those defaults using the `RUSTFLAGS` environment +variable. For example: + +```sh +# Use ld instead of Mold +$ RUSTFLAGS="-C linker=/path/to/ld.lld" cargo build +``` ## Thanks @@ -245,6 +272,7 @@ person about current issues. To list a few, special thanks to: - [@sioodmy](https://github.com/sioodmy) - Being cute - [@mewoocat](https://github.com/mewoocat) - The awesome NixOS logo ASCII used in Microfetch +- [@uzaaft](https://github.com/uzaaft) - Helping me going faster Additionally a big thank you to everyone who used, talked about or criticized Microfetch. I might have missed your name here, but you have my thanks.