From 7e32b5de6a3fb41a08c9ffef0ca9a2a3fb753695 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: Tue, 15 Aug 2023 00:10:52 +0800 Subject: [PATCH] refactor: improve stdout performance by using `BufWriter` (#55) --- Cargo.lock | 1 - adaptor/Cargo.toml | 1 - adaptor/src/adaptor.rs | 23 +++++++++-------------- adaptor/src/iterm2.rs | 17 ++++++++++------- adaptor/src/kitty.rs | 8 ++++---- adaptor/src/sixel.rs | 17 ++++++++++------- config/src/boot/boot.rs | 4 ++-- core/src/manager/manager.rs | 4 ++-- core/src/manager/preview.rs | 4 ++-- core/src/tasks/tasks.rs | 15 ++++++++------- shared/src/term.rs | 17 +++++++++++------ 11 files changed, 58 insertions(+), 53 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2bc18dec..833de826 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -11,7 +11,6 @@ dependencies = [ "color_quant", "config", "image", - "md-5", "once_cell", "ratatui", "shared", diff --git a/adaptor/Cargo.toml b/adaptor/Cargo.toml index 9372bc6b..774a592e 100644 --- a/adaptor/Cargo.toml +++ b/adaptor/Cargo.toml @@ -12,7 +12,6 @@ anyhow = "^1" base64 = "^0" color_quant = "^1" image = "^0" -md-5 = "^0" once_cell = "^1" ratatui = "^0" tokio = { version = "^1", features = [ "parking_lot" ] } diff --git a/adaptor/src/adaptor.rs b/adaptor/src/adaptor.rs index 621aba7f..22d3f7b1 100644 --- a/adaptor/src/adaptor.rs +++ b/adaptor/src/adaptor.rs @@ -22,7 +22,7 @@ impl Adaptor { pub async fn image_show(mut path: &Path, rect: Rect) -> Result<()> { if IMAGE_SHOWN.swap(true, Ordering::Relaxed) { - Self::image_hide(rect); + Self::image_hide(rect).ok(); } let cache = BOOT.cache(path); @@ -34,29 +34,24 @@ impl Adaptor { PreviewAdaptor::Kitty => Kitty::image_show(path, rect).await, PreviewAdaptor::Iterm2 => Iterm2::image_show(path, rect).await, PreviewAdaptor::Sixel => Sixel::image_show(path, rect).await, - _ => { - if let Some(tx) = &*UEBERZUG { - tx.send(Some((path.to_path_buf(), rect))).ok(); - } - Ok(()) - } + _ => Ok(if let Some(tx) = &*UEBERZUG { + tx.send(Some((path.to_path_buf(), rect)))?; + }), } } - pub fn image_hide(rect: Rect) { + pub fn image_hide(rect: Rect) -> Result<()> { if !IMAGE_SHOWN.swap(false, Ordering::Relaxed) { - return; + return Ok(()); } match PREVIEW.adaptor { PreviewAdaptor::Kitty => Kitty::image_hide(), PreviewAdaptor::Iterm2 => Iterm2::image_hide(rect), PreviewAdaptor::Sixel => Sixel::image_hide(rect), - _ => { - if let Some(tx) = &*UEBERZUG { - tx.send(None).ok(); - } - } + _ => Ok(if let Some(tx) = &*UEBERZUG { + tx.send(None)?; + }), } } } diff --git a/adaptor/src/iterm2.rs b/adaptor/src/iterm2.rs index de00a740..5d791571 100644 --- a/adaptor/src/iterm2.rs +++ b/adaptor/src/iterm2.rs @@ -1,4 +1,4 @@ -use std::{io::{stdout, Write}, path::Path}; +use std::{io::{stdout, BufWriter, Write}, path::Path}; use anyhow::Result; use base64::{engine::general_purpose, Engine}; @@ -15,18 +15,21 @@ impl Iterm2 { let img = Image::crop(path, (rect.width, rect.height)).await?; let b = Self::encode(img).await?; - Term::move_to(rect.x, rect.y).ok(); - stdout().write_all(&b).ok(); - Ok(()) + let mut stdout = stdout().lock(); + Term::move_to(&mut stdout, rect.x, rect.y)?; + Ok(stdout.write_all(&b)?) } #[inline] - pub(super) fn image_hide(rect: Rect) { + pub(super) fn image_hide(rect: Rect) -> Result<()> { let s = " ".repeat(rect.width as usize); + let mut stdout = BufWriter::new(stdout().lock()); + for y in rect.top()..=rect.bottom() { - Term::move_to(rect.x, y).ok(); - stdout().write_all(s.as_bytes()).ok(); + Term::move_to(&mut stdout, rect.x, y)?; + stdout.write_all(s.as_bytes())?; } + Ok(()) } async fn encode(img: DynamicImage) -> Result> { diff --git a/adaptor/src/kitty.rs b/adaptor/src/kitty.rs index a533f1ac..c2f0997d 100644 --- a/adaptor/src/kitty.rs +++ b/adaptor/src/kitty.rs @@ -15,13 +15,13 @@ impl Kitty { let img = Image::crop(path, (rect.width, rect.height)).await?; let b = Self::encode(img).await?; - Term::move_to(rect.x, rect.y).ok(); - stdout().write_all(&b).ok(); - Ok(()) + let mut stdout = stdout().lock(); + Term::move_to(&mut stdout, rect.x, rect.y)?; + Ok(stdout.write_all(&b)?) } #[inline] - pub(super) fn image_hide() { stdout().write_all(b"\x1b\\\x1b_Ga=d\x1b\\").ok(); } + pub(super) fn image_hide() -> Result<()> { Ok(stdout().write_all(b"\x1b\\\x1b_Ga=d\x1b\\")?) } async fn encode(img: DynamicImage) -> Result> { fn output(raw: &[u8], format: u8, size: (u32, u32)) -> Result> { diff --git a/adaptor/src/sixel.rs b/adaptor/src/sixel.rs index 5a733f4e..f53656d3 100644 --- a/adaptor/src/sixel.rs +++ b/adaptor/src/sixel.rs @@ -1,4 +1,4 @@ -use std::{io::{stdout, Write}, path::Path}; +use std::{io::{stdout, BufWriter, Write}, path::Path}; use anyhow::{bail, Result}; use color_quant::NeuQuant; @@ -15,18 +15,21 @@ impl Sixel { let img = Image::crop(path, (rect.width, rect.height)).await?; let b = Self::encode(img).await?; - Term::move_to(rect.x, rect.y).ok(); - stdout().write_all(&b).ok(); - Ok(()) + let mut stdout = stdout().lock(); + Term::move_to(&mut stdout, rect.x, rect.y)?; + Ok(stdout.write_all(&b)?) } #[inline] - pub(super) fn image_hide(rect: Rect) { + pub(super) fn image_hide(rect: Rect) -> Result<()> { let s = " ".repeat(rect.width as usize); + let mut stdout = BufWriter::new(stdout().lock()); + for y in rect.top()..=rect.bottom() { - Term::move_to(rect.x, y).ok(); - stdout().write_all(s.as_bytes()).ok(); + Term::move_to(&mut stdout, rect.x, y)?; + stdout.write_all(s.as_bytes())?; } + Ok(()) } async fn encode(img: DynamicImage) -> Result> { diff --git a/config/src/boot/boot.rs b/config/src/boot/boot.rs index 94a938e5..71b02674 100644 --- a/config/src/boot/boot.rs +++ b/config/src/boot/boot.rs @@ -1,4 +1,4 @@ -use std::{env, fs, path::{Path, PathBuf}, time::{self, SystemTime}}; +use std::{env, fs, os::unix::prelude::OsStrExt, path::{Path, PathBuf}, time::{self, SystemTime}}; use clap::Parser; use md5::{Digest, Md5}; @@ -44,7 +44,7 @@ impl Boot { pub fn cache(&self, path: &Path) -> PathBuf { self .cache_dir - .join(format!("{:x}", Md5::new_with_prefix(path.to_string_lossy().as_bytes()).finalize())) + .join(format!("{:x}", Md5::new_with_prefix(path.as_os_str().as_bytes()).finalize())) } #[inline] diff --git a/core/src/manager/manager.rs b/core/src/manager/manager.rs index c24e0690..b013ee11 100644 --- a/core/src/manager/manager.rs +++ b/core/src/manager/manager.rs @@ -262,7 +262,7 @@ impl Manager { } async fn bulk_rename_do(root: PathBuf, old: Vec, new: Vec) -> Result<()> { - Term::clear()?; + Term::clear(&mut stdout())?; if old.len() != new.len() { println!("Number of old and new differ, press ENTER to exit"); stdin().read_exact(&mut [0]).await?; @@ -303,7 +303,7 @@ impl Manager { return Ok(()); } - Term::clear()?; + Term::clear(&mut stdout())?; { let mut stdout = BufWriter::new(stdout().lock()); writeln!(stdout, "Failed to rename:")?; diff --git a/core/src/manager/preview.rs b/core/src/manager/preview.rs index 7f3313d0..583a2e22 100644 --- a/core/src/manager/preview.rs +++ b/core/src/manager/preview.rs @@ -77,7 +77,7 @@ impl Preview { pub fn reset(&mut self) -> bool { self.handle.take().map(|h| h.abort()); self.incr.fetch_add(1, Ordering::Relaxed); - Adaptor::image_hide(Self::rect()); + Adaptor::image_hide(Self::rect()).ok(); self.lock = None; !matches!( @@ -89,7 +89,7 @@ impl Preview { pub fn reset_image(&mut self) -> bool { self.handle.take().map(|h| h.abort()); self.incr.fetch_add(1, Ordering::Relaxed); - Adaptor::image_hide(Self::rect()); + Adaptor::image_hide(Self::rect()).ok(); if matches!(self.data, PreviewData::Image) { self.lock = None; diff --git a/core/src/tasks/tasks.rs b/core/src/tasks/tasks.rs index 836e13c4..56919a16 100644 --- a/core/src/tasks/tasks.rs +++ b/core/src/tasks/tasks.rs @@ -2,8 +2,8 @@ use std::{collections::{BTreeMap, HashMap, HashSet}, ffi::OsStr, io::{stdout, Wr use config::{manager::SortBy, open::Opener, OPEN}; use crossterm::terminal::{disable_raw_mode, enable_raw_mode}; -use shared::{tty_size, Defer, MimeKind}; -use tokio::{io::AsyncReadExt, select, sync::mpsc, time}; +use shared::{tty_size, Defer, MimeKind, Term}; +use tokio::{io::{stdin, AsyncReadExt}, select, sync::mpsc, time}; use tracing::trace; use super::{task::TaskSummary, Scheduler, TASKS_PADDING, TASKS_PERCENT}; @@ -85,17 +85,18 @@ impl Tasks { Event::Stop(false, None).emit(); }); - stdout().write_all("\n".repeat(tty_size().ws_row as usize).as_bytes()).ok(); + Term::clear(&mut stdout()).ok(); stdout().write_all(buffered.as_bytes()).ok(); enable_raw_mode().ok(); - let mut stdin = tokio::io::stdin(); - let mut quit = [0; 1]; + let mut stdin = stdin(); + let mut quit = [0; 10]; loop { select! { Some(line) = rx.recv() => { - stdout().write_all(line.as_bytes()).ok(); - stdout().write_all(b"\r\n").ok(); + let mut stdout = stdout().lock(); + stdout.write_all(line.as_bytes()).ok(); + stdout.write_all(b"\r\n").ok(); } _ = time::sleep(time::Duration::from_millis(100)) => { if scheduler.running.read().get(id).is_none() { diff --git a/shared/src/term.rs b/shared/src/term.rs index 01de6dde..d2e28e74 100644 --- a/shared/src/term.rs +++ b/shared/src/term.rs @@ -32,17 +32,22 @@ impl Term { Ok(term) } - pub fn clear() -> Result<()> { - execute!(stdout(), Clear(ClearType::All))?; - println!(); - stdout().flush()?; - Ok(()) + #[inline] + pub fn clear(stdout: &mut impl Write) -> Result<()> { + execute!(stdout, Clear(ClearType::All))?; + writeln!(stdout)?; + Ok(stdout.flush()?) } - pub fn move_to(x: u16, y: u16) -> Result<()> { Ok(execute!(stdout(), MoveTo(x, y))?) } + #[inline] + pub fn move_to(stdout: &mut impl Write, x: u16, y: u16) -> Result<()> { + Ok(execute!(stdout, MoveTo(x, y))?) + } + #[inline] pub fn set_cursor_block() -> Result<()> { Ok(execute!(stdout(), SetCursorStyle::BlinkingBlock)?) } + #[inline] pub fn set_cursor_bar() -> Result<()> { Ok(execute!(stdout(), SetCursorStyle::BlinkingBar)?) } }