From 9ddaba4c29caa4d6661529d89e83736aa314e4b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=89=E5=92=B2=E9=9B=85=20=C2=B7=20Misaki=20Masa?= Date: Sat, 26 Oct 2024 15:47:20 +0800 Subject: [PATCH] perf: using `usize` instead of `*const OsStr` reduces memory usage (#1839) --- .github/workflows/check.yml | 6 +++ .github/workflows/draft.yml | 33 +++++++++++--- .github/workflows/test.yml | 3 ++ README.md | 48 +++++++++++++-------- yazi-plugin/src/cha/cha.rs | 6 +-- yazi-shared/src/fs/cha.rs | 22 +++++----- yazi-shared/src/fs/fns.rs | 4 +- yazi-shared/src/fs/loc.rs | 86 ++++++++++++++++--------------------- 8 files changed, 121 insertions(+), 87 deletions(-) diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index 0e7b1d548..f9b4bc734 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -19,6 +19,9 @@ jobs: - name: Setup Rust cache uses: Swatinem/rust-cache@v2 + with: + prefix-key: rust + shared-key: ubuntu-latest@debug - name: Clippy run: cargo clippy --all @@ -36,6 +39,9 @@ jobs: - name: Setup Rust cache uses: Swatinem/rust-cache@v2 + with: + prefix-key: rust + shared-key: ubuntu-latest@debug - name: Rustfmt run: cargo +nightly fmt --all -- --check diff --git a/.github/workflows/draft.yml b/.github/workflows/draft.yml index f8a3d8b46..6731d1242 100644 --- a/.github/workflows/draft.yml +++ b/.github/workflows/draft.yml @@ -32,6 +32,12 @@ jobs: echo "JEMALLOC_SYS_WITH_LG_PAGE=16" >> $GITHUB_ENV echo "CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER=/usr/bin/aarch64-linux-gnu-gcc" >> $GITHUB_ENV + - name: Setup Rust cache + uses: Swatinem/rust-cache@v2 + with: + prefix-key: rust + shared-key: ${{ matrix.target }}@release + - name: Build run: ./scripts/build.sh ${{ matrix.target }} @@ -54,10 +60,15 @@ jobs: - uses: actions/checkout@v4 - name: Setup Rust toolchain - run: rustup toolchain install stable --profile minimal + run: | + rustup toolchain install stable --profile minimal + rustup target add ${{ matrix.target }} - - name: Add target - run: rustup target add ${{ matrix.target }} + - name: Setup Rust cache + uses: Swatinem/rust-cache@v2 + with: + prefix-key: rust + shared-key: ${{ matrix.target }}@release - name: Build env: @@ -98,6 +109,12 @@ jobs: steps: - uses: actions/checkout@v4 + - name: Setup Rust cache + uses: Swatinem/rust-cache@v2 + with: + prefix-key: rust + shared-key: ${{ matrix.target }}@release + - name: Build run: ./scripts/build.sh ${{ matrix.target }} @@ -117,10 +134,16 @@ jobs: steps: - uses: actions/checkout@v4 - - name: Build snap - uses: snapcore/action-build@v1 + - name: Setup Rust cache + uses: Swatinem/rust-cache@v2 + with: + prefix-key: rust + shared-key: ${{ matrix.target }}@release - name: Build + uses: snapcore/action-build@v1 + + - name: Rename snap run: mv yazi_*.snap yazi-${{ matrix.target }}.snap - name: Upload artifact diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index c7296246b..d4a15e8ab 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -23,6 +23,9 @@ jobs: - name: Setup Rust cache uses: Swatinem/rust-cache@v2 + with: + prefix-key: rust + shared-key: ${{ matrix.os }}@debug - name: Build run: cargo build --verbose diff --git a/README.md b/README.md index bb31463d9..01478be2b 100644 --- a/README.md +++ b/README.md @@ -44,26 +44,40 @@ https://github.com/sxyazi/yazi/assets/17523360/92ff23fa-0cd5-4f04-b387-894c12265 ## Image Preview -| Platform | Protocol | Support | -| --------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------- | -------------------------------------------------------------- | -| [kitty](https://github.com/kovidgoyal/kitty) | [Kitty unicode placeholders](https://sw.kovidgoyal.net/kitty/graphics-protocol/#unicode-placeholders) | ✅ Built-in | -| [Konsole](https://invent.kde.org/utilities/konsole) | [Kitty old protocol](https://github.com/sxyazi/yazi/blob/main/yazi-adapter/src/kgp_old.rs) | ✅ Built-in | -| [iTerm2](https://iterm2.com) | [Inline images protocol](https://iterm2.com/documentation-images.html) | ✅ Built-in | -| [WezTerm](https://github.com/wez/wezterm) | [Inline images protocol](https://iterm2.com/documentation-images.html) | ✅ Built-in | -| [Mintty](https://github.com/mintty/mintty) (Git Bash) | [Inline images protocol](https://iterm2.com/documentation-images.html) | ✅ Built-in | -| [foot](https://codeberg.org/dnkl/foot) | [Sixel graphics format](https://www.vt100.net/docs/vt3xx-gp/chapter14.html) | ✅ Built-in | -| [Ghostty](https://mitchellh.com/ghostty) | [Kitty unicode placeholders](https://sw.kovidgoyal.net/kitty/graphics-protocol/#unicode-placeholders) | ✅ Built-in | -| [Windows Terminal](https://github.com/microsoft/terminal) (>= v1.22.2702.0) | [Sixel graphics format](https://www.vt100.net/docs/vt3xx-gp/chapter14.html) | ✅ Built-in | -| [Black Box](https://gitlab.gnome.org/raggesilver/blackbox) | [Sixel graphics format](https://www.vt100.net/docs/vt3xx-gp/chapter14.html) | ✅ Built-in | -| [VSCode](https://github.com/microsoft/vscode) | [Inline images protocol](https://iterm2.com/documentation-images.html) | ✅ Built-in | -| [Tabby](https://github.com/Eugeny/tabby) | [Inline images protocol](https://iterm2.com/documentation-images.html) | ✅ Built-in | -| [Hyper](https://github.com/vercel/hyper) | [Inline images protocol](https://iterm2.com/documentation-images.html) | ✅ Built-in | -| [Rio](https://github.com/raphamorim/rio) | [Inline images protocol](https://iterm2.com/documentation-images.html) | ❌ Rio doesn't correctly clear images (#1786) | -| X11 / Wayland | Window system protocol | ☑️ [Überzug++](https://github.com/jstkdng/ueberzugpp) required | -| Fallback | [ASCII art (Unicode block)](https://en.wikipedia.org/wiki/ASCII_art) | ☑️ [Chafa](https://hpjansson.org/chafa/) required | +| Platform | Protocol | Support | +| --------------------------------------------------------------------------- | -------------------------------------- | --------------------------------------------- | +| [kitty](https://github.com/kovidgoyal/kitty) | [Kitty unicode placeholders][kgp] | ✅ Built-in | +| [iTerm2](https://iterm2.com) | [Inline images protocol][iip] | ✅ Built-in | +| [WezTerm](https://github.com/wez/wezterm) | [Inline images protocol][iip] | ✅ Built-in | +| [Konsole](https://invent.kde.org/utilities/konsole) | [Kitty old protocol][kgp-old] | ✅ Built-in | +| [foot](https://codeberg.org/dnkl/foot) | [Sixel graphics format][sixel] | ✅ Built-in | +| [Ghostty](https://mitchellh.com/ghostty) | [Kitty unicode placeholders][kgp] | ✅ Built-in | +| [Windows Terminal](https://github.com/microsoft/terminal) (>= v1.22.2702.0) | [Sixel graphics format][sixel] | ✅ Built-in | +| [st with Sixel patch](https://github.com/bakkeby/st-flexipatch) | [Sixel graphics format][sixel] | ✅ Built-in | +| [Tabby](https://github.com/Eugeny/tabby) | [Inline images protocol][iip] | ✅ Built-in | +| [VSCode](https://github.com/microsoft/vscode) | [Inline images protocol][iip] | ✅ Built-in | +| [Rio](https://github.com/raphamorim/rio) | [Inline images protocol][iip] | ❌ Rio doesn't correctly clear images (#1786) | +| [Mintty](https://github.com/mintty/mintty) (Git Bash) | [Inline images protocol][iip] | ✅ Built-in | +| [Black Box](https://gitlab.gnome.org/raggesilver/blackbox) | [Sixel graphics format][sixel] | ✅ Built-in | +| [Hyper](https://github.com/vercel/hyper) | [Inline images protocol][iip] | ✅ Built-in | +| X11 / Wayland | Window system protocol | ☑️ [Überzug++][ueberzug] required | +| Fallback | [ASCII art (Unicode block)][ascii-art] | ☑️ [Chafa][chafa] required | See https://yazi-rs.github.io/docs/image-preview for details. + + +[kgp]: https://sw.kovidgoyal.net/kitty/graphics-protocol/#unicode-placeholders +[kgp-old]: https://github.com/sxyazi/yazi/blob/main/yazi-adapter/src/kgp_old.rs +[iip]: https://iterm2.com/documentation-images.html +[sixel]: https://www.vt100.net/docs/vt3xx-gp/chapter14.html +[ascii-art]: https://en.wikipedia.org/wiki/ASCII_art + + + +[ueberzug]: https://github.com/jstkdng/ueberzugpp +[chafa]: https://hpjansson.org/chafa/ + ## License Yazi is MIT-licensed. For more information check the [LICENSE](LICENSE) file. diff --git a/yazi-plugin/src/cha/cha.rs b/yazi-plugin/src/cha/cha.rs index beebfc4d9..95e0ed136 100644 --- a/yazi-plugin/src/cha/cha.rs +++ b/yazi-plugin/src/cha/cha.rs @@ -67,7 +67,7 @@ impl Cha { reg.add_method("perm", |_, _me, ()| { Ok( #[cfg(unix)] - Some(yazi_shared::fs::permissions(_me.perm, _me.is_dummy())), + Some(yazi_shared::fs::permissions(_me.mode, _me.is_dummy())), #[cfg(windows)] None::, ) @@ -95,7 +95,7 @@ impl Cha { warn_deprecated(lua.named_registry_value::("rt")?.current()); Ok( #[cfg(unix)] - Some(yazi_shared::fs::permissions(_me.perm, _me.is_dummy())), + Some(yazi_shared::fs::permissions(_me.mode, _me.is_dummy())), #[cfg(windows)] None::, ) @@ -131,7 +131,7 @@ impl Cha { ctime: parse_time(t.raw_get("ctime").ok())?, mtime: parse_time(t.raw_get("mtime").ok())?, #[cfg(unix)] - perm: t.raw_get("permissions").unwrap_or_default(), + mode: t.raw_get("mode").unwrap_or_default(), #[cfg(unix)] uid: t.raw_get("uid").unwrap_or_default(), #[cfg(unix)] diff --git a/yazi-shared/src/fs/cha.rs b/yazi-shared/src/fs/cha.rs index 57f7fea9f..8f9597fb9 100644 --- a/yazi-shared/src/fs/cha.rs +++ b/yazi-shared/src/fs/cha.rs @@ -26,7 +26,7 @@ pub struct Cha { pub ctime: Option, pub mtime: Option, #[cfg(unix)] - pub perm: libc::mode_t, + pub mode: libc::mode_t, #[cfg(unix)] pub uid: libc::uid_t, #[cfg(unix)] @@ -57,7 +57,7 @@ impl From for Cha { mtime: m.modified().ok(), #[cfg(unix)] - perm: { + mode: { use std::os::unix::prelude::PermissionsExt; m.permissions().mode() as _ }, @@ -85,7 +85,7 @@ impl From for Cha { let mut kind = ChaKind::DUMMY; #[cfg(unix)] - let perm = { + let mode = { use std::os::unix::fs::FileTypeExt; if t.is_dir() { kind |= ChaKind::DIR; @@ -118,7 +118,7 @@ impl From for Cha { Self { kind, #[cfg(unix)] - perm, + mode, ..Default::default() } } @@ -173,7 +173,7 @@ impl Cha { && unix_either!(self.ctime == c.ctime, true) && self.btime == c.btime && self.kind == c.kind - && unix_either!(self.perm == c.perm, true) + && unix_either!(self.mode == c.mode, true) } } @@ -195,27 +195,27 @@ impl Cha { #[inline] pub const fn is_block(&self) -> bool { - unix_either!(self.perm & libc::S_IFMT == libc::S_IFBLK, false) + unix_either!(self.mode & libc::S_IFMT == libc::S_IFBLK, false) } #[inline] pub const fn is_char(&self) -> bool { - unix_either!(self.perm & libc::S_IFMT == libc::S_IFCHR, false) + unix_either!(self.mode & libc::S_IFMT == libc::S_IFCHR, false) } #[inline] pub const fn is_fifo(&self) -> bool { - unix_either!(self.perm & libc::S_IFMT == libc::S_IFIFO, false) + unix_either!(self.mode & libc::S_IFMT == libc::S_IFIFO, false) } #[inline] pub const fn is_sock(&self) -> bool { - unix_either!(self.perm & libc::S_IFMT == libc::S_IFSOCK, false) + unix_either!(self.mode & libc::S_IFMT == libc::S_IFSOCK, false) } #[inline] - pub const fn is_exec(&self) -> bool { unix_either!(self.perm & libc::S_IXUSR != 0, false) } + pub const fn is_exec(&self) -> bool { unix_either!(self.mode & libc::S_IXUSR != 0, false) } #[inline] - pub const fn is_sticky(&self) -> bool { unix_either!(self.perm & libc::S_ISVTX != 0, false) } + pub const fn is_sticky(&self) -> bool { unix_either!(self.mode & libc::S_ISVTX != 0, false) } } diff --git a/yazi-shared/src/fs/fns.rs b/yazi-shared/src/fs/fns.rs index a6115f554..0424332f4 100644 --- a/yazi-shared/src/fs/fns.rs +++ b/yazi-shared/src/fs/fns.rs @@ -258,14 +258,14 @@ async fn _copy_with_progress(from: PathBuf, to: PathBuf, cha: Cha) -> io::Result tokio::task::spawn_blocking(move || { let mut reader = std::fs::File::open(from)?; let mut writer = std::fs::OpenOptions::new() - .mode(cha.perm as u32) + .mode(cha.mode as u32) .write(true) .create(true) .truncate(true) .open(to)?; let written = std::io::copy(&mut reader, &mut writer)?; - unsafe { libc::fchmod(writer.as_raw_fd(), cha.perm) }; + unsafe { libc::fchmod(writer.as_raw_fd(), cha.mode) }; writer.set_times(ft).ok(); Ok(written) diff --git a/yazi-shared/src/fs/loc.rs b/yazi-shared/src/fs/loc.rs index 190e6be7d..e6c5a1803 100644 --- a/yazi-shared/src/fs/loc.rs +++ b/yazi-shared/src/fs/loc.rs @@ -2,31 +2,17 @@ use std::{cmp, ffi::OsStr, fmt::{self, Debug, Formatter}, hash::{Hash, Hasher}, use super::{Urn, UrnBuf}; +#[derive(Clone, Default)] pub struct Loc { path: PathBuf, - urn: *const OsStr, - name: *const OsStr, + urn: usize, + name: usize, } unsafe impl Send for Loc {} unsafe impl Sync for Loc {} -impl Default for Loc { - fn default() -> Self { - Self { path: PathBuf::default(), urn: OsStr::new(""), name: OsStr::new("") } - } -} - -impl Clone for Loc { - fn clone(&self) -> Self { - let path = self.path.clone(); - let name = path.file_name().unwrap_or(OsStr::new("")) as *const OsStr; - let urn = if self.urn() == self.name() { name } else { self.twin_urn(&path) }; - Self { path, urn, name } - } -} - impl Deref for Loc { type Target = PathBuf; @@ -63,56 +49,58 @@ impl Debug for Loc { impl Loc { pub fn new(path: PathBuf) -> Self { - let urn = path.file_name().unwrap_or(OsStr::new("")) as *const OsStr; - Self { path, urn, name: urn } + let name = path.file_name().map_or(0, |s| s.len()); + Self { path, urn: name, name } } pub fn from(base: &Path, path: PathBuf) -> Self { - let urn = path.strip_prefix(base).unwrap_or(&path).as_os_str() as *const OsStr; - let name = path.file_name().unwrap_or(OsStr::new("")) as *const OsStr; + let urn = path.strip_prefix(base).unwrap_or(&path).as_os_str().len(); + let name = path.file_name().map_or(0, |s| s.len()); Self { path, urn, name } } - pub fn base(&self) -> &Path { - let mut it = self.path.components(); - for _ in 0..self.urn().components().count() { - it.next_back().unwrap(); - } - it.as_path() - } - - pub fn rebase(&self, parent: &Path) -> Self { - debug_assert!(self.urn() == self.name()); - - let path = parent.join(self.name()); - let name = path.file_name().unwrap_or(OsStr::new("")) as *const OsStr; - Self { path, urn: name, name } + #[inline] + pub fn urn(&self) -> &Urn { + Urn::new(unsafe { + OsStr::from_encoded_bytes_unchecked( + self.bytes().get_unchecked(self.bytes().len() - self.urn..), + ) + }) } #[inline] - fn twin_urn<'a>(&self, new: &'a Path) -> &'a OsStr { - let total = new.components().count(); - let take = self.urn().components().count(); + pub fn urn_owned(&self) -> UrnBuf { self.urn().to_owned() } - let mut it = new.components(); - for _ in 0..total - take { - it.next().unwrap(); + #[inline] + pub fn name(&self) -> &OsStr { + unsafe { + OsStr::from_encoded_bytes_unchecked( + self.bytes().get_unchecked(self.bytes().len() - self.name..), + ) } - - it.as_path().as_os_str() } -} -impl Loc { #[inline] - pub fn urn(&self) -> &Urn { Urn::new(unsafe { &*self.urn }) } + pub fn base(&self) -> &Path { + Path::new(unsafe { + OsStr::from_encoded_bytes_unchecked( + self.bytes().get_unchecked(..self.bytes().len() - self.urn), + ) + }) + } #[inline] - pub fn urn_owned(&self) -> UrnBuf { self.urn().to_owned() } + pub fn rebase(&self, parent: &Path) -> Self { + debug_assert!(self.urn == self.name); + let path = parent.join(self.name()); - #[inline] - pub fn name(&self) -> &OsStr { unsafe { &*self.name } } + debug_assert!(path.file_name().is_some_and(|s| s.len() == self.name)); + Self { path, urn: self.name, name: self.name } + } #[inline] pub fn into_path(self) -> PathBuf { self.path } + + #[inline] + fn bytes(&self) -> &[u8] { self.path.as_os_str().as_encoded_bytes() } }