diff --git a/CHANGELOG.md b/CHANGELOG.md index 8264a615..17f4eb36 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -45,6 +45,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/): - Rename `mime` fetcher to `mime.local`, and introduce `mime.dir` fetcher to support folder MIME types ([#3222]) - Remove `$0` parameter in opener rules to make the `open` command work under empty directories ([#3226]) - Use `body` instead of the term `content` in confirmations ([#2921]) +- Use `u16` instead of `u32` as the type of `max_width` and `max_height` options to avoid memory exhaustion ([#3313]) - Implement `__pairs` metamethod instead of `__index` for the callback argument of the `@yank` DDS event ([#2997]) ### Deprecated @@ -1533,3 +1534,4 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/): [#3271]: https://github.com/sxyazi/yazi/pull/3271 [#3286]: https://github.com/sxyazi/yazi/pull/3286 [#3290]: https://github.com/sxyazi/yazi/pull/3290 +[#3313]: https://github.com/sxyazi/yazi/pull/3313 diff --git a/yazi-adapter/src/image.rs b/yazi-adapter/src/image.rs index 619a0c18..7a058f6f 100644 --- a/yazi-adapter/src/image.rs +++ b/yazi-adapter/src/image.rs @@ -65,10 +65,10 @@ impl Image { Ok(img) } - pub(super) fn max_pixel(rect: Rect) -> (u32, u32) { + pub(super) fn max_pixel(rect: Rect) -> (u16, u16) { Dimension::cell_size() .map(|(cw, ch)| { - let (w, h) = ((rect.width as f64 * cw) as u32, (rect.height as f64 * ch) as u32); + let (w, h) = ((rect.width as f64 * cw) as u16, (rect.height as f64 * ch) as u16); (w.min(YAZI.preview.max_width), h.min(YAZI.preview.max_height)) }) .unwrap_or((YAZI.preview.max_width, YAZI.preview.max_height)) @@ -122,11 +122,11 @@ impl Image { .map_err(|e| ImageError::IoError(e.into()))? } - fn flip_size(orientation: Orientation, (w, h): (u32, u32)) -> (u32, u32) { + fn flip_size(orientation: Orientation, (w, h): (u16, u16)) -> (u32, u32) { use image::metadata::Orientation::{Rotate90, Rotate90FlipH, Rotate270, Rotate270FlipH}; match orientation { - Rotate90 | Rotate270 | Rotate90FlipH | Rotate270FlipH => (h, w), - _ => (w, h), + Rotate90 | Rotate270 | Rotate90FlipH | Rotate270FlipH => (h as u32, w as u32), + _ => (w as u32, h as u32), } } } diff --git a/yazi-config/src/preview/preview.rs b/yazi-config/src/preview/preview.rs index 18c7b613..50a02941 100644 --- a/yazi-config/src/preview/preview.rs +++ b/yazi-config/src/preview/preview.rs @@ -15,8 +15,8 @@ const TABS: &[&str] = &["", " ", " ", " ", " ", " ", " ", " pub struct Preview { pub wrap: PreviewWrap, pub tab_size: u8, - pub max_width: u32, - pub max_height: u32, + pub max_width: u16, + pub max_height: u16, pub cache_dir: PathBuf, diff --git a/yazi-fs/src/file.rs b/yazi-fs/src/file.rs index 34612f67..0d60bb83 100644 --- a/yazi-fs/src/file.rs +++ b/yazi-fs/src/file.rs @@ -1,6 +1,6 @@ use std::{ffi::OsStr, hash::{Hash, Hasher}, ops::Deref, path::{Path, PathBuf}}; -use yazi_shared::url::{UrlBuf, UrlLike}; +use yazi_shared::{path::PathDyn, url::{UrlBuf, UrlLike}}; use crate::cha::{Cha, ChaType}; @@ -37,7 +37,7 @@ impl File { pub fn url_owned(&self) -> UrlBuf { self.url.to_owned() } #[inline] - pub fn uri(&self) -> &Path { self.url.uri() } + pub fn uri(&self) -> PathDyn<'_> { self.url.uri() } #[inline] pub fn urn(&self) -> &Path { self.url.urn() } diff --git a/yazi-fs/src/path/expand.rs b/yazi-fs/src/path/expand.rs index eb0fedfe..8dfd2fdf 100644 --- a/yazi-fs/src/path/expand.rs +++ b/yazi-fs/src/path/expand.rs @@ -1,6 +1,6 @@ use std::{borrow::Cow, ffi::{OsStr, OsString}, path::{Path, PathBuf}}; -use yazi_shared::{loc::LocBuf, url::{AsUrl, Url, UrlBuf, UrlCow}}; +use yazi_shared::{loc::LocBuf, path::PathLike, url::{AsUrl, Url, UrlBuf, UrlCow}}; use crate::{CWD, path::clean_url}; diff --git a/yazi-plugin/preset/plugins/pdf.lua b/yazi-plugin/preset/plugins/pdf.lua index 39cdbdb0..2fbcd366 100644 --- a/yazi-plugin/preset/plugins/pdf.lua +++ b/yazi-plugin/preset/plugins/pdf.lua @@ -40,9 +40,7 @@ function M:preload(job) "-l", job.skip + 1, "-singlefile", "-jpeg", "-jpegopt", "quality=" .. rt.preview.image_quality, - "-scale-to-x", rt.preview.max_width, "-scale-to-y", "-1", - tostring(job.file.url), - tostring(cache), + tostring(job.file.url), tostring(cache), }) :stderr(Command.PIPED) :output() @@ -54,12 +52,7 @@ function M:preload(job) return true, Err("Failed to convert PDF to image, stderr: %s", output.stderr), pages end - local ok, err = os.rename(string.format("%s.jpg", cache), tostring(cache)) - if ok then - return true - else - return false, Err("Failed to rename `%s.jpg` to `%s`, error: %s", cache, cache, err) - end + return ya.image_precache(Url(cache .. ".jpg"), cache) end return M diff --git a/yazi-shared/src/loc/buf.rs b/yazi-shared/src/loc/buf.rs index 869adcc5..5efd399d 100644 --- a/yazi-shared/src/loc/buf.rs +++ b/yazi-shared/src/loc/buf.rs @@ -204,11 +204,8 @@ where } #[inline] - fn mutate(&mut self, f: F) - where - P: Default, - { - let mut inner = std::mem::take(&mut self.inner); + fn mutate(&mut self, f: F) { + let mut inner = self.inner.take(); f(&mut inner); self.inner = Self::from(inner).inner; } diff --git a/yazi-shared/src/path/buf.rs b/yazi-shared/src/path/buf.rs index 093a6b33..e3790201 100644 --- a/yazi-shared/src/path/buf.rs +++ b/yazi-shared/src/path/buf.rs @@ -4,7 +4,7 @@ use crate::path::{AsInnerView, AsPathView, PathInner, PathLike}; pub trait PathBufLike where - Self: Default + 'static, + Self: 'static, { type Inner: for<'a> AsInnerView<'a, Self::InnerRef<'a>>; type InnerRef<'a>: PathInner<'a>; @@ -21,6 +21,8 @@ where fn set_file_name(&mut self, name: T) where T: for<'a> AsInnerView<'a, Self::InnerRef<'a>>; + + fn take(&mut self) -> Self; } impl PathBufLike for std::path::PathBuf { @@ -42,4 +44,6 @@ impl PathBufLike for std::path::PathBuf { { self.set_file_name(name.as_inner_view()); } + + fn take(&mut self) -> Self { std::mem::take(self) } } diff --git a/yazi-shared/src/path/dyn.rs b/yazi-shared/src/path/dyn.rs index 20ed2460..078d724a 100644 --- a/yazi-shared/src/path/dyn.rs +++ b/yazi-shared/src/path/dyn.rs @@ -1,9 +1,132 @@ +use super::{AsInnerView, AsPathView}; +use crate::path::{PathBufLike, PathLike}; + #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] pub enum PathDyn<'p> { Os(&'p std::path::Path), } +impl<'a> AsPathView<'a, PathDyn<'a>> for PathDyn<'a> { + fn as_path_view(self) -> PathDyn<'a> { self } +} + +impl<'a> AsPathView<'a, PathDyn<'a>> for std::path::Components<'a> { + fn as_path_view(self) -> PathDyn<'a> { PathDyn::Os(self.as_path()) } +} + +impl<'a> From<&'a std::path::Path> for PathDyn<'a> { + fn from(value: &'a std::path::Path) -> Self { PathDyn::Os(value) } +} + +impl<'p> PathLike<'p> for PathDyn<'p> { + type Components<'a> = std::path::Components<'a>; + type Inner = &'p [u8]; + type Owned = PathBufDyn; + type View<'a> = PathDyn<'a>; + + fn components(self) -> Self::Components<'p> { + match self { + Self::Os(p) => p.components(), + } + } + + // FIXME: remove + fn default() -> Self { Self::Os(std::path::Path::new("")) } + + fn encoded_bytes(self) -> &'p [u8] { + match self { + Self::Os(p) => p.as_os_str().as_encoded_bytes(), + } + } + + fn extension(self) -> Option { + Some(match self { + Self::Os(p) => p.extension()?.as_encoded_bytes(), + }) + } + + fn file_name(self) -> Option { + Some(match self { + Self::Os(p) => p.file_name()?.as_encoded_bytes(), + }) + } + + fn file_stem(self) -> Option { + Some(match self { + Self::Os(p) => p.file_stem()?.as_encoded_bytes(), + }) + } + + // FIXME: remove + unsafe fn from_encoded_bytes(bytes: &'p [u8]) -> Self { + Self::Os(std::path::Path::new(unsafe { std::ffi::OsStr::from_encoded_bytes_unchecked(bytes) })) + } + + fn join<'a, T>(self, base: T) -> Self::Owned + where + T: AsPathView<'a, Self::View<'a>>, + { + match (self, base.as_path_view()) { + (Self::Os(p), PathDyn::Os(q)) => Self::Owned::Os(p.join(q)), + } + } + + fn parent(self) -> Option { + Some(match self { + Self::Os(p) => Self::Os(p.parent()?), + }) + } + + fn strip_prefix<'a, T>(self, base: T) -> Option + where + T: AsPathView<'a, Self::View<'a>>, + { + Some(match (self, base.as_path_view()) { + (Self::Os(p), PathDyn::Os(q)) => Self::Os(p.strip_prefix(q).ok()?), + }) + } +} + #[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] pub enum PathBufDyn { Os(std::path::PathBuf), } + +impl PathBufLike for PathBufDyn { + type Borrowed<'a> = PathDyn<'a>; + type Inner = Vec; + type InnerRef<'a> = &'a [u8]; + + fn encoded_bytes(&self) -> &[u8] { + match self { + Self::Os(p) => p.as_os_str().as_encoded_bytes(), + } + } + + // FIXME: remove + unsafe fn from_encoded_bytes(bytes: Vec) -> Self { + Self::Os(std::path::PathBuf::from(unsafe { + std::ffi::OsString::from_encoded_bytes_unchecked(bytes) + })) + } + + fn into_encoded_bytes(self) -> Vec { + match self { + Self::Os(p) => p.into_os_string().into_encoded_bytes(), + } + } + + fn set_file_name(&mut self, name: T) + where + T: for<'a> AsInnerView<'a, Self::InnerRef<'a>>, + { + // TODO: introduce a new `PathInnerDyn` + todo!() + } + + fn take(&mut self) -> Self { + match self { + Self::Os(p) => Self::Os(std::mem::take(p)), + } + } +} diff --git a/yazi-shared/src/path/path.rs b/yazi-shared/src/path/path.rs index c24b1111..3a2e23f3 100644 --- a/yazi-shared/src/path/path.rs +++ b/yazi-shared/src/path/path.rs @@ -23,6 +23,8 @@ where unsafe fn from_encoded_bytes(bytes: &'p [u8]) -> Self; + fn is_empty(self) -> bool { self.encoded_bytes().is_empty() } + #[cfg(unix)] fn is_hidden(self) -> bool { self.file_name().map_or(false, |n| n.encoded_bytes().get(0) == Some(&b'.')) diff --git a/yazi-shared/src/path/traits.rs b/yazi-shared/src/path/traits.rs index 734b5e1e..5b1d636b 100644 --- a/yazi-shared/src/path/traits.rs +++ b/yazi-shared/src/path/traits.rs @@ -82,6 +82,14 @@ impl<'a> AsInnerView<'a, &'a std::ffi::OsStr> for std::ffi::OsString { fn as_inner_view(&'a self) -> &'a std::ffi::OsStr { self } } +impl<'a> AsInnerView<'a, &'a [u8]> for [u8] { + fn as_inner_view(&'a self) -> &'a [u8] { self } +} + +impl<'a> AsInnerView<'a, &'a [u8]> for Vec { + fn as_inner_view(&'a self) -> &'a [u8] { self } +} + impl<'a, T, U> AsInnerView<'a, U> for &T where T: ?Sized + AsInnerView<'a, U>, diff --git a/yazi-shared/src/url/traits.rs b/yazi-shared/src/url/traits.rs index 6c7aec51..a05e6e48 100644 --- a/yazi-shared/src/url/traits.rs +++ b/yazi-shared/src/url/traits.rs @@ -1,6 +1,6 @@ use std::{borrow::Cow, ffi::OsStr, path::{Path, PathBuf}}; -use crate::{loc::Loc, scheme::{AsScheme, SchemeRef}, url::{Components, Display, Url, UrlBuf, UrlCow}}; +use crate::{loc::Loc, path::PathDyn, scheme::{AsScheme, SchemeRef}, url::{Components, Display, Url, UrlBuf, UrlCow}}; // --- AsUrl pub trait AsUrl { @@ -117,7 +117,7 @@ where fn strip_prefix(&self, base: impl AsUrl) -> Option<&Path> { self.as_url().strip_prefix(base) } - fn uri(&self) -> &Path { self.as_url().uri() } + fn uri(&self) -> PathDyn<'_> { self.as_url().uri() } fn urn(&self) -> &Path { self.as_url().urn() } } diff --git a/yazi-shared/src/url/url.rs b/yazi-shared/src/url/url.rs index 3bd2ea93..1747528b 100644 --- a/yazi-shared/src/url/url.rs +++ b/yazi-shared/src/url/url.rs @@ -2,7 +2,7 @@ use std::{borrow::Cow, ffi::OsStr, fmt::{Debug, Formatter}, path::{Path, PathBuf use hashbrown::Equivalent; -use crate::{loc::{Loc, LocBuf}, scheme::SchemeRef, url::{AsUrl, Components, Encode, UrlBuf}}; +use crate::{loc::{Loc, LocBuf}, path::{PathDyn, PathLike}, scheme::SchemeRef, url::{AsUrl, Components, Encode, UrlBuf}}; #[derive(Clone, Copy, Eq, Hash, PartialEq)] pub struct Url<'a> { @@ -97,10 +97,10 @@ impl<'a> Url<'a> { (S::Search(_), S::Regular) => Some(prefix), // Only the entry of archives is a local file - (S::Regular, S::Archive(_)) => Some(prefix).filter(|_| base.uri().as_os_str().is_empty()), - (S::Search(_), S::Archive(_)) => Some(prefix).filter(|_| base.uri().as_os_str().is_empty()), - (S::Archive(_), S::Regular) => Some(prefix).filter(|_| self.uri().as_os_str().is_empty()), - (S::Archive(_), S::Search(_)) => Some(prefix).filter(|_| self.uri().as_os_str().is_empty()), + (S::Regular, S::Archive(_)) => Some(prefix).filter(|_| base.uri().is_empty()), + (S::Search(_), S::Archive(_)) => Some(prefix).filter(|_| base.uri().is_empty()), + (S::Archive(_), S::Regular) => Some(prefix).filter(|_| self.uri().is_empty()), + (S::Archive(_), S::Search(_)) => Some(prefix).filter(|_| self.uri().is_empty()), // Independent virtual file space (S::Regular, S::Sftp(_)) => None, @@ -113,7 +113,7 @@ impl<'a> Url<'a> { } #[inline] - pub fn uri(self) -> &'a Path { self.loc.uri() } + pub fn uri(self) -> PathDyn<'a> { self.loc.uri().into() } #[inline] pub fn urn(self) -> &'a Path { self.loc.urn() }