refactor: improve stdout performance by using BufWriter (#55)

This commit is contained in:
三咲雅 · Misaki Masa 2023-08-15 00:10:52 +08:00 committed by GitHub
parent 053652c740
commit 7e32b5de6a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 58 additions and 53 deletions

1
Cargo.lock generated
View file

@ -11,7 +11,6 @@ dependencies = [
"color_quant", "color_quant",
"config", "config",
"image", "image",
"md-5",
"once_cell", "once_cell",
"ratatui", "ratatui",
"shared", "shared",

View file

@ -12,7 +12,6 @@ anyhow = "^1"
base64 = "^0" base64 = "^0"
color_quant = "^1" color_quant = "^1"
image = "^0" image = "^0"
md-5 = "^0"
once_cell = "^1" once_cell = "^1"
ratatui = "^0" ratatui = "^0"
tokio = { version = "^1", features = [ "parking_lot" ] } tokio = { version = "^1", features = [ "parking_lot" ] }

View file

@ -22,7 +22,7 @@ impl Adaptor {
pub async fn image_show(mut path: &Path, rect: Rect) -> Result<()> { pub async fn image_show(mut path: &Path, rect: Rect) -> Result<()> {
if IMAGE_SHOWN.swap(true, Ordering::Relaxed) { if IMAGE_SHOWN.swap(true, Ordering::Relaxed) {
Self::image_hide(rect); Self::image_hide(rect).ok();
} }
let cache = BOOT.cache(path); let cache = BOOT.cache(path);
@ -34,29 +34,24 @@ impl Adaptor {
PreviewAdaptor::Kitty => Kitty::image_show(path, rect).await, PreviewAdaptor::Kitty => Kitty::image_show(path, rect).await,
PreviewAdaptor::Iterm2 => Iterm2::image_show(path, rect).await, PreviewAdaptor::Iterm2 => Iterm2::image_show(path, rect).await,
PreviewAdaptor::Sixel => Sixel::image_show(path, rect).await, PreviewAdaptor::Sixel => Sixel::image_show(path, rect).await,
_ => { _ => Ok(if let Some(tx) = &*UEBERZUG {
if let Some(tx) = &*UEBERZUG { tx.send(Some((path.to_path_buf(), rect)))?;
tx.send(Some((path.to_path_buf(), rect))).ok(); }),
}
Ok(())
}
} }
} }
pub fn image_hide(rect: Rect) { pub fn image_hide(rect: Rect) -> Result<()> {
if !IMAGE_SHOWN.swap(false, Ordering::Relaxed) { if !IMAGE_SHOWN.swap(false, Ordering::Relaxed) {
return; return Ok(());
} }
match PREVIEW.adaptor { match PREVIEW.adaptor {
PreviewAdaptor::Kitty => Kitty::image_hide(), PreviewAdaptor::Kitty => Kitty::image_hide(),
PreviewAdaptor::Iterm2 => Iterm2::image_hide(rect), PreviewAdaptor::Iterm2 => Iterm2::image_hide(rect),
PreviewAdaptor::Sixel => Sixel::image_hide(rect), PreviewAdaptor::Sixel => Sixel::image_hide(rect),
_ => { _ => Ok(if let Some(tx) = &*UEBERZUG {
if let Some(tx) = &*UEBERZUG { tx.send(None)?;
tx.send(None).ok(); }),
}
}
} }
} }
} }

View file

@ -1,4 +1,4 @@
use std::{io::{stdout, Write}, path::Path}; use std::{io::{stdout, BufWriter, Write}, path::Path};
use anyhow::Result; use anyhow::Result;
use base64::{engine::general_purpose, Engine}; use base64::{engine::general_purpose, Engine};
@ -15,18 +15,21 @@ impl Iterm2 {
let img = Image::crop(path, (rect.width, rect.height)).await?; let img = Image::crop(path, (rect.width, rect.height)).await?;
let b = Self::encode(img).await?; let b = Self::encode(img).await?;
Term::move_to(rect.x, rect.y).ok(); let mut stdout = stdout().lock();
stdout().write_all(&b).ok(); Term::move_to(&mut stdout, rect.x, rect.y)?;
Ok(()) Ok(stdout.write_all(&b)?)
} }
#[inline] #[inline]
pub(super) fn image_hide(rect: Rect) { pub(super) fn image_hide(rect: Rect) -> Result<()> {
let s = " ".repeat(rect.width as usize); let s = " ".repeat(rect.width as usize);
let mut stdout = BufWriter::new(stdout().lock());
for y in rect.top()..=rect.bottom() { for y in rect.top()..=rect.bottom() {
Term::move_to(rect.x, y).ok(); Term::move_to(&mut stdout, rect.x, y)?;
stdout().write_all(s.as_bytes()).ok(); stdout.write_all(s.as_bytes())?;
} }
Ok(())
} }
async fn encode(img: DynamicImage) -> Result<Vec<u8>> { async fn encode(img: DynamicImage) -> Result<Vec<u8>> {

View file

@ -15,13 +15,13 @@ impl Kitty {
let img = Image::crop(path, (rect.width, rect.height)).await?; let img = Image::crop(path, (rect.width, rect.height)).await?;
let b = Self::encode(img).await?; let b = Self::encode(img).await?;
Term::move_to(rect.x, rect.y).ok(); let mut stdout = stdout().lock();
stdout().write_all(&b).ok(); Term::move_to(&mut stdout, rect.x, rect.y)?;
Ok(()) Ok(stdout.write_all(&b)?)
} }
#[inline] #[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<Vec<u8>> { async fn encode(img: DynamicImage) -> Result<Vec<u8>> {
fn output(raw: &[u8], format: u8, size: (u32, u32)) -> Result<Vec<u8>> { fn output(raw: &[u8], format: u8, size: (u32, u32)) -> Result<Vec<u8>> {

View file

@ -1,4 +1,4 @@
use std::{io::{stdout, Write}, path::Path}; use std::{io::{stdout, BufWriter, Write}, path::Path};
use anyhow::{bail, Result}; use anyhow::{bail, Result};
use color_quant::NeuQuant; use color_quant::NeuQuant;
@ -15,18 +15,21 @@ impl Sixel {
let img = Image::crop(path, (rect.width, rect.height)).await?; let img = Image::crop(path, (rect.width, rect.height)).await?;
let b = Self::encode(img).await?; let b = Self::encode(img).await?;
Term::move_to(rect.x, rect.y).ok(); let mut stdout = stdout().lock();
stdout().write_all(&b).ok(); Term::move_to(&mut stdout, rect.x, rect.y)?;
Ok(()) Ok(stdout.write_all(&b)?)
} }
#[inline] #[inline]
pub(super) fn image_hide(rect: Rect) { pub(super) fn image_hide(rect: Rect) -> Result<()> {
let s = " ".repeat(rect.width as usize); let s = " ".repeat(rect.width as usize);
let mut stdout = BufWriter::new(stdout().lock());
for y in rect.top()..=rect.bottom() { for y in rect.top()..=rect.bottom() {
Term::move_to(rect.x, y).ok(); Term::move_to(&mut stdout, rect.x, y)?;
stdout().write_all(s.as_bytes()).ok(); stdout.write_all(s.as_bytes())?;
} }
Ok(())
} }
async fn encode(img: DynamicImage) -> Result<Vec<u8>> { async fn encode(img: DynamicImage) -> Result<Vec<u8>> {

View file

@ -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 clap::Parser;
use md5::{Digest, Md5}; use md5::{Digest, Md5};
@ -44,7 +44,7 @@ impl Boot {
pub fn cache(&self, path: &Path) -> PathBuf { pub fn cache(&self, path: &Path) -> PathBuf {
self self
.cache_dir .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] #[inline]

View file

@ -262,7 +262,7 @@ impl Manager {
} }
async fn bulk_rename_do(root: PathBuf, old: Vec<PathBuf>, new: Vec<PathBuf>) -> Result<()> { async fn bulk_rename_do(root: PathBuf, old: Vec<PathBuf>, new: Vec<PathBuf>) -> Result<()> {
Term::clear()?; Term::clear(&mut stdout())?;
if old.len() != new.len() { if old.len() != new.len() {
println!("Number of old and new differ, press ENTER to exit"); println!("Number of old and new differ, press ENTER to exit");
stdin().read_exact(&mut [0]).await?; stdin().read_exact(&mut [0]).await?;
@ -303,7 +303,7 @@ impl Manager {
return Ok(()); return Ok(());
} }
Term::clear()?; Term::clear(&mut stdout())?;
{ {
let mut stdout = BufWriter::new(stdout().lock()); let mut stdout = BufWriter::new(stdout().lock());
writeln!(stdout, "Failed to rename:")?; writeln!(stdout, "Failed to rename:")?;

View file

@ -77,7 +77,7 @@ impl Preview {
pub fn reset(&mut self) -> bool { pub fn reset(&mut self) -> bool {
self.handle.take().map(|h| h.abort()); self.handle.take().map(|h| h.abort());
self.incr.fetch_add(1, Ordering::Relaxed); self.incr.fetch_add(1, Ordering::Relaxed);
Adaptor::image_hide(Self::rect()); Adaptor::image_hide(Self::rect()).ok();
self.lock = None; self.lock = None;
!matches!( !matches!(
@ -89,7 +89,7 @@ impl Preview {
pub fn reset_image(&mut self) -> bool { pub fn reset_image(&mut self) -> bool {
self.handle.take().map(|h| h.abort()); self.handle.take().map(|h| h.abort());
self.incr.fetch_add(1, Ordering::Relaxed); self.incr.fetch_add(1, Ordering::Relaxed);
Adaptor::image_hide(Self::rect()); Adaptor::image_hide(Self::rect()).ok();
if matches!(self.data, PreviewData::Image) { if matches!(self.data, PreviewData::Image) {
self.lock = None; self.lock = None;

View file

@ -2,8 +2,8 @@ use std::{collections::{BTreeMap, HashMap, HashSet}, ffi::OsStr, io::{stdout, Wr
use config::{manager::SortBy, open::Opener, OPEN}; use config::{manager::SortBy, open::Opener, OPEN};
use crossterm::terminal::{disable_raw_mode, enable_raw_mode}; use crossterm::terminal::{disable_raw_mode, enable_raw_mode};
use shared::{tty_size, Defer, MimeKind}; use shared::{tty_size, Defer, MimeKind, Term};
use tokio::{io::AsyncReadExt, select, sync::mpsc, time}; use tokio::{io::{stdin, AsyncReadExt}, select, sync::mpsc, time};
use tracing::trace; use tracing::trace;
use super::{task::TaskSummary, Scheduler, TASKS_PADDING, TASKS_PERCENT}; use super::{task::TaskSummary, Scheduler, TASKS_PADDING, TASKS_PERCENT};
@ -85,17 +85,18 @@ impl Tasks {
Event::Stop(false, None).emit(); 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(); stdout().write_all(buffered.as_bytes()).ok();
enable_raw_mode().ok(); enable_raw_mode().ok();
let mut stdin = tokio::io::stdin(); let mut stdin = stdin();
let mut quit = [0; 1]; let mut quit = [0; 10];
loop { loop {
select! { select! {
Some(line) = rx.recv() => { Some(line) = rx.recv() => {
stdout().write_all(line.as_bytes()).ok(); let mut stdout = stdout().lock();
stdout().write_all(b"\r\n").ok(); stdout.write_all(line.as_bytes()).ok();
stdout.write_all(b"\r\n").ok();
} }
_ = time::sleep(time::Duration::from_millis(100)) => { _ = time::sleep(time::Duration::from_millis(100)) => {
if scheduler.running.read().get(id).is_none() { if scheduler.running.read().get(id).is_none() {

View file

@ -32,17 +32,22 @@ impl Term {
Ok(term) Ok(term)
} }
pub fn clear() -> Result<()> { #[inline]
execute!(stdout(), Clear(ClearType::All))?; pub fn clear(stdout: &mut impl Write) -> Result<()> {
println!(); execute!(stdout, Clear(ClearType::All))?;
stdout().flush()?; writeln!(stdout)?;
Ok(()) 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)?) } pub fn set_cursor_block() -> Result<()> { Ok(execute!(stdout(), SetCursorStyle::BlinkingBlock)?) }
#[inline]
pub fn set_cursor_bar() -> Result<()> { Ok(execute!(stdout(), SetCursorStyle::BlinkingBar)?) } pub fn set_cursor_bar() -> Result<()> { Ok(execute!(stdout(), SetCursorStyle::BlinkingBar)?) }
} }