From 6c439e27c47d0e2d4da9c2d253a57156bf32e5d8 Mon Sep 17 00:00:00 2001 From: Jesse Luehrs Date: Wed, 13 Nov 2019 07:20:33 -0500 Subject: attempt to avoid copies by writing directly to a std::io::Write this turns out to be slower, i think because it ends up doing a much larger number of small writes, and the copying overhead isn't as high as going through all of the machinery involved in stdout locking/buffering/syscalls/etc. --- examples/process_diff.rs | 12 +-- examples/process_full.rs | 11 ++- src/attrs.rs | 14 ++-- src/grid.rs | 52 +++++++----- src/row.rs | 68 +++++++-------- src/screen.rs | 132 ++++++++++++++++++----------- src/term.rs | 212 ++++++++++++++++++++++++----------------------- 7 files changed, 274 insertions(+), 227 deletions(-) diff --git a/examples/process_diff.rs b/examples/process_diff.rs index c141269..55ebbf2 100644 --- a/examples/process_diff.rs +++ b/examples/process_diff.rs @@ -1,4 +1,4 @@ -use std::io::{Read as _, Write as _}; +use std::io::Read as _; fn read_frames() -> impl Iterator> { (1..=7625).map(|i| { @@ -14,13 +14,13 @@ fn read_frames() -> impl Iterator> { fn draw_frames(frames: &[Vec]) { let mut stdout = std::io::stdout(); let mut parser = vt100::Parser::default(); - let mut screen = parser.screen().clone(); for frame in frames { + let screen = parser.screen().clone(); parser.process(&frame); - let new_screen = parser.screen().clone(); - let diff = new_screen.contents_diff(&screen); - stdout.write_all(&diff).unwrap(); - screen = new_screen; + parser + .screen() + .write_contents_diff(&mut stdout, &screen) + .unwrap(); } } diff --git a/examples/process_full.rs b/examples/process_full.rs index 5aa5422..3be0ec0 100644 --- a/examples/process_full.rs +++ b/examples/process_full.rs @@ -1,4 +1,4 @@ -use std::io::{Read as _, Write as _}; +use std::io::Read as _; fn read_frames() -> impl Iterator> { (1..=7625).map(|i| { @@ -12,12 +12,15 @@ fn read_frames() -> impl Iterator> { } fn draw_frames(frames: &[Vec]) { - let mut stdout = std::io::stdout(); + let stdout = std::io::stdout(); + let mut stdout = stdout.lock(); let mut parser = vt100::Parser::default(); for frame in frames { parser.process(&frame); - let contents = parser.screen().contents_formatted(); - stdout.write_all(&contents).unwrap(); + parser + .screen() + .write_contents_formatted(&mut stdout) + .unwrap(); } } diff --git a/src/attrs.rs b/src/attrs.rs index eab752c..8e6b44f 100644 --- a/src/attrs.rs +++ b/src/attrs.rs @@ -1,4 +1,4 @@ -use crate::term::BufWrite as _; +use crate::term::WriteTo as _; /// Represents a foreground or background color for cells. #[derive(Eq, PartialEq, Debug, Copy, Clone)] @@ -83,14 +83,14 @@ impl Attrs { } } - pub fn write_escape_code_diff( + pub fn write_escape_code_diff( &self, - contents: &mut Vec, + w: &mut W, other: &Self, - ) { + ) -> std::io::Result<()> { if self != other && self == &Self::default() { - crate::term::ClearAttrs::default().write_buf(contents); - return; + crate::term::ClearAttrs::default().write_to(w)?; + return Ok(()); } let attrs = crate::term::Attrs::default(); @@ -126,6 +126,6 @@ impl Attrs { attrs.inverse(self.inverse()) }; - attrs.write_buf(contents); + attrs.write_to(w) } } diff --git a/src/grid.rs b/src/grid.rs index 80d44ba..4248b1c 100644 --- a/src/grid.rs +++ b/src/grid.rs @@ -1,4 +1,4 @@ -use crate::term::BufWrite as _; +use crate::term::WriteTo as _; use std::convert::TryInto as _; #[derive(Clone, Debug)] @@ -173,25 +173,31 @@ impl Grid { self.scrollback_offset = rows.min(self.scrollback.len()); } - pub fn write_contents(&self, contents: &mut String) { + pub fn write_contents( + &self, + w: &mut W, + ) -> std::io::Result<()> { for row in self.visible_rows() { - row.write_contents(contents, 0, self.size.cols); + row.write_contents(w, 0, self.size.cols)?; if !row.wrapped() { - contents.push_str("\n"); + w.write_all(b"\n")?; } } - while contents.ends_with('\n') { - contents.truncate(contents.len() - 1); - } + // XXX + // while contents.ends_with('\n') { + // contents.truncate(contents.len() - 1); + // } + + Ok(()) } - pub fn write_contents_formatted( + pub fn write_contents_formatted( &self, - contents: &mut Vec, - ) -> crate::attrs::Attrs { - crate::term::ClearAttrs::default().write_buf(contents); - crate::term::ClearScreen::default().write_buf(contents); + w: &mut W, + ) -> std::io::Result { + crate::term::ClearAttrs::default().write_to(w)?; + crate::term::ClearScreen::default().write_to(w)?; let mut prev_attrs = crate::attrs::Attrs::default(); let mut prev_pos = Pos::default(); @@ -199,30 +205,30 @@ impl Grid { for (i, row) in self.visible_rows().enumerate() { let i = i.try_into().unwrap(); let (new_pos, new_attrs) = row.write_contents_formatted( - contents, + w, 0, self.size.cols, i, wrapping, prev_pos, prev_attrs, - ); + )?; prev_pos = new_pos; prev_attrs = new_attrs; wrapping = row.wrapped(); } - crate::term::MoveFromTo::new(prev_pos, self.pos).write_buf(contents); + crate::term::MoveFromTo::new(prev_pos, self.pos).write_to(w)?; - prev_attrs + Ok(prev_attrs) } - pub fn write_contents_diff( + pub fn write_contents_diff( &self, - contents: &mut Vec, + w: &mut W, prev: &Self, mut prev_attrs: crate::attrs::Attrs, - ) -> crate::attrs::Attrs { + ) -> std::io::Result { let mut prev_pos = prev.pos; let mut wrapping = false; for (i, (row, prev_row)) in @@ -230,7 +236,7 @@ impl Grid { { let i = i.try_into().unwrap(); let (new_pos, new_attrs) = row.write_contents_diff( - contents, + w, prev_row, 0, self.size.cols, @@ -238,15 +244,15 @@ impl Grid { wrapping, prev_pos, prev_attrs, - ); + )?; prev_pos = new_pos; prev_attrs = new_attrs; wrapping = row.wrapped(); } - crate::term::MoveFromTo::new(prev_pos, self.pos).write_buf(contents); + crate::term::MoveFromTo::new(prev_pos, self.pos).write_to(w)?; - prev_attrs + Ok(prev_attrs) } pub fn erase_all(&mut self, attrs: crate::attrs::Attrs) { diff --git a/src/row.rs b/src/row.rs index 52b6823..87e2544 100644 --- a/src/row.rs +++ b/src/row.rs @@ -1,4 +1,4 @@ -use crate::term::BufWrite as _; +use crate::term::WriteTo as _; use std::convert::TryInto as _; #[derive(Clone, Debug)] @@ -68,12 +68,12 @@ impl Row { self.wrapped } - pub fn write_contents( + pub fn write_contents( &self, - contents: &mut String, + w: &mut W, start: u16, width: u16, - ) { + ) -> std::io::Result<()> { let mut prev_was_wide = false; let mut prev_col = start; @@ -92,26 +92,28 @@ impl Row { let col: u16 = col.try_into().unwrap(); if cell.has_contents() { for _ in 0..(col - prev_col) { - contents.push(' '); + w.write_all(b" ")?; } prev_col += col - prev_col; - contents.push_str(&cell.contents()); + w.write_all(cell.contents().as_bytes())?; prev_col += if cell.is_wide() { 2 } else { 1 }; } } + + Ok(()) } - pub fn write_contents_formatted( + pub fn write_contents_formatted( &self, - contents: &mut Vec, + w: &mut W, start: u16, width: u16, row: u16, wrapping: bool, mut prev_pos: crate::grid::Pos, mut prev_attrs: crate::attrs::Attrs, - ) -> (crate::grid::Pos, crate::attrs::Attrs) { + ) -> std::io::Result<(crate::grid::Pos, crate::attrs::Attrs)> { let mut prev_was_wide = false; let default_cell = crate::cell::Cell::default(); @@ -137,14 +139,14 @@ impl Row { if cell.has_contents() || cell.attrs() != attrs { let new_pos = crate::grid::Pos { row, col: prev_col }; crate::term::MoveFromTo::new(prev_pos, new_pos) - .write_buf(contents); + .write_to(w)?; prev_pos = new_pos; if &prev_attrs != attrs { - attrs.write_escape_code_diff(contents, &prev_attrs); + attrs.write_escape_code_diff(w, &prev_attrs)?; prev_attrs = *attrs; } crate::term::EraseChar::new(pos.col - prev_col) - .write_buf(contents); + .write_to(w)?; erase = None; } } @@ -159,17 +161,17 @@ impl Row { || pos.col != 0 { crate::term::MoveFromTo::new(prev_pos, pos) - .write_buf(contents); + .write_to(w)?; } prev_pos = pos; } if &prev_attrs != attrs { - attrs.write_escape_code_diff(contents, &prev_attrs); + attrs.write_escape_code_diff(w, &prev_attrs)?; prev_attrs = *attrs; } - contents.extend(cell.contents().as_bytes()); + w.write_all(cell.contents().as_bytes())?; prev_pos.col += if cell.is_wide() { 2 } else { 1 }; } else if erase.is_none() { erase = Some((pos.col, attrs)); @@ -178,25 +180,24 @@ impl Row { } if let Some((prev_col, attrs)) = erase { let new_pos = crate::grid::Pos { row, col: prev_col }; - crate::term::MoveFromTo::new(prev_pos, new_pos) - .write_buf(contents); + crate::term::MoveFromTo::new(prev_pos, new_pos).write_to(w)?; prev_pos = new_pos; if &prev_attrs != attrs { - attrs.write_escape_code_diff(contents, &prev_attrs); + attrs.write_escape_code_diff(w, &prev_attrs)?; prev_attrs = *attrs; } - crate::term::ClearRowForward::default().write_buf(contents); + crate::term::ClearRowForward::default().write_to(w)?; } - (prev_pos, prev_attrs) + Ok((prev_pos, prev_attrs)) } // while it's true that most of the logic in this is identical to // write_contents_formatted, i can't figure out how to break out the // common parts without making things noticeably slower. - pub fn write_contents_diff( + pub fn write_contents_diff( &self, - contents: &mut Vec, + w: &mut W, prev: &Self, start: u16, width: u16, @@ -204,7 +205,7 @@ impl Row { wrapping: bool, mut prev_pos: crate::grid::Pos, mut prev_attrs: crate::attrs::Attrs, - ) -> (crate::grid::Pos, crate::attrs::Attrs) { + ) -> std::io::Result<(crate::grid::Pos, crate::attrs::Attrs)> { let mut prev_was_wide = false; let mut erase: Option<(u16, &crate::attrs::Attrs)> = None; @@ -230,14 +231,14 @@ impl Row { if cell.has_contents() || cell.attrs() != attrs { let new_pos = crate::grid::Pos { row, col: prev_col }; crate::term::MoveFromTo::new(prev_pos, new_pos) - .write_buf(contents); + .write_to(w)?; prev_pos = new_pos; if &prev_attrs != attrs { - attrs.write_escape_code_diff(contents, &prev_attrs); + attrs.write_escape_code_diff(w, &prev_attrs)?; prev_attrs = *attrs; } crate::term::EraseChar::new(pos.col - prev_col) - .write_buf(contents); + .write_to(w)?; erase = None; } } @@ -252,17 +253,17 @@ impl Row { || pos.col != 0 { crate::term::MoveFromTo::new(prev_pos, pos) - .write_buf(contents); + .write_to(w)?; } prev_pos = pos; } if &prev_attrs != attrs { - attrs.write_escape_code_diff(contents, &prev_attrs); + attrs.write_escape_code_diff(w, &prev_attrs)?; prev_attrs = *attrs; } - contents.extend(cell.contents().as_bytes()); + w.write_all(cell.contents().as_bytes())?; prev_pos.col += if cell.is_wide() { 2 } else { 1 }; } else if erase.is_none() { erase = Some((pos.col, attrs)); @@ -271,16 +272,15 @@ impl Row { } if let Some((prev_col, attrs)) = erase { let new_pos = crate::grid::Pos { row, col: prev_col }; - crate::term::MoveFromTo::new(prev_pos, new_pos) - .write_buf(contents); + crate::term::MoveFromTo::new(prev_pos, new_pos).write_to(w)?; prev_pos = new_pos; if &prev_attrs != attrs { - attrs.write_escape_code_diff(contents, &prev_attrs); + attrs.write_escape_code_diff(w, &prev_attrs)?; prev_attrs = *attrs; } - crate::term::ClearRowForward::default().write_buf(contents); + crate::term::ClearRowForward::default().write_to(w)?; } - (prev_pos, prev_attrs) + Ok((prev_pos, prev_attrs)) } } diff --git a/src/screen.rs b/src/screen.rs index 3128f78..750f090 100644 --- a/src/screen.rs +++ b/src/screen.rs @@ -1,4 +1,4 @@ -use crate::term::BufWrite as _; +use crate::term::WriteTo as _; use std::convert::TryInto as _; use unicode_width::UnicodeWidthChar as _; @@ -141,13 +141,16 @@ impl Screen { /// This will not include any formatting information, and will be in plain /// text format. pub fn contents(&self) -> String { - let mut contents = String::new(); - self.write_contents(&mut contents); - contents + let mut contents = vec![]; + self.write_contents(&mut contents).unwrap(); + String::from_utf8(contents).unwrap() } - fn write_contents(&self, contents: &mut String) { - self.grid().write_contents(contents); + pub fn write_contents( + &self, + w: &mut W, + ) -> std::io::Result<()> { + self.grid().write_contents(w) } /// Returns the text contents of the terminal by row, restricted to the @@ -163,9 +166,9 @@ impl Screen { width: u16, ) -> impl Iterator + '_ { self.grid().visible_rows().map(move |row| { - let mut contents = String::new(); - row.write_contents(&mut contents, start, width); - contents + let mut contents = vec![]; + row.write_contents(&mut contents, start, width).unwrap(); + String::from_utf8(contents).unwrap() }) } @@ -176,14 +179,18 @@ impl Screen { /// terminal parser, and will result in the same visual output. pub fn contents_formatted(&self) -> Vec { let mut contents = vec![]; - self.write_contents_formatted(&mut contents); + self.write_contents_formatted(&mut contents).unwrap(); contents } - fn write_contents_formatted(&self, contents: &mut Vec) { - crate::term::HideCursor::new(self.hide_cursor()).write_buf(contents); - let prev_attrs = self.grid().write_contents_formatted(contents); - self.attrs.write_escape_code_diff(contents, &prev_attrs); + pub fn write_contents_formatted( + &self, + w: &mut W, + ) -> std::io::Result<()> { + crate::term::HideCursor::new(self.hide_cursor()).write_to(w)?; + let prev_attrs = self.grid().write_contents_formatted(w)?; + self.attrs.write_escape_code_diff(w, &prev_attrs)?; + Ok(()) } /// Returns the formatted visible contents of the terminal by row, @@ -212,7 +219,8 @@ impl Screen { false, crate::grid::Pos { row: i, col: start }, crate::attrs::Attrs::default(), - ); + ) + .unwrap(); contents }) } @@ -229,21 +237,23 @@ impl Screen { /// flickering than redrawing the entire screen contents. pub fn contents_diff(&self, prev: &Self) -> Vec { let mut contents = vec![]; - self.write_contents_diff(&mut contents, prev); + self.write_contents_diff(&mut contents, prev).unwrap(); contents } - fn write_contents_diff(&self, contents: &mut Vec, prev: &Self) { + pub fn write_contents_diff( + &self, + w: &mut W, + prev: &Self, + ) -> std::io::Result<()> { if self.hide_cursor() != prev.hide_cursor() { - crate::term::HideCursor::new(self.hide_cursor()) - .write_buf(contents); + crate::term::HideCursor::new(self.hide_cursor()).write_to(w)?; } - let prev_attrs = self.grid().write_contents_diff( - contents, - prev.grid(), - prev.attrs, - ); - self.attrs.write_escape_code_diff(contents, &prev_attrs); + let prev_attrs = + self.grid() + .write_contents_diff(w, prev.grid(), prev.attrs)?; + self.attrs.write_escape_code_diff(w, &prev_attrs)?; + Ok(()) } /// Returns a sequence of terminal byte streams sufficient to turn the @@ -276,54 +286,63 @@ impl Screen { false, crate::grid::Pos { row: i, col: start }, crate::attrs::Attrs::default(), - ); + ) + .unwrap(); contents }) } pub fn input_mode_formatted(&self) -> Vec { let mut contents = vec![]; - self.write_input_mode_formatted(&mut contents); + self.write_input_mode_formatted(&mut contents).unwrap(); contents } - fn write_input_mode_formatted(&self, contents: &mut Vec) { + pub fn write_input_mode_formatted( + &self, + w: &mut W, + ) -> std::io::Result<()> { crate::term::ApplicationKeypad::new( self.mode(Mode::ApplicationKeypad), ) - .write_buf(contents); + .write_to(w)?; crate::term::ApplicationCursor::new( self.mode(Mode::ApplicationCursor), ) - .write_buf(contents); + .write_to(w)?; crate::term::BracketedPaste::new(self.mode(Mode::BracketedPaste)) - .write_buf(contents); + .write_to(w)?; crate::term::MouseProtocolMode::new( self.mouse_protocol_mode, MouseProtocolMode::None, ) - .write_buf(contents); + .write_to(w)?; crate::term::MouseProtocolEncoding::new( self.mouse_protocol_encoding, MouseProtocolEncoding::Default, ) - .write_buf(contents); + .write_to(w)?; + Ok(()) } pub fn input_mode_diff(&self, prev: &Self) -> Vec { let mut contents = vec![]; - self.write_input_mode_diff(&mut contents, prev); + self.write_input_mode_diff(&mut contents, prev).unwrap(); contents } - fn write_input_mode_diff(&self, contents: &mut Vec, prev: &Self) { + pub fn write_input_mode_diff( + &self, + w: &mut W, + prev: &Self, + ) -> std::io::Result<()> { if self.mode(Mode::ApplicationKeypad) != prev.mode(Mode::ApplicationKeypad) { crate::term::ApplicationKeypad::new( self.mode(Mode::ApplicationKeypad), ) - .write_buf(contents); + .write_to(w)?; } if self.mode(Mode::ApplicationCursor) != prev.mode(Mode::ApplicationCursor) @@ -331,65 +350,78 @@ impl Screen { crate::term::ApplicationCursor::new( self.mode(Mode::ApplicationCursor), ) - .write_buf(contents); + .write_to(w)?; } if self.mode(Mode::BracketedPaste) != prev.mode(Mode::BracketedPaste) { crate::term::BracketedPaste::new(self.mode(Mode::BracketedPaste)) - .write_buf(contents); + .write_to(w)?; } crate::term::MouseProtocolMode::new( self.mouse_protocol_mode, prev.mouse_protocol_mode, ) - .write_buf(contents); + .write_to(w)?; crate::term::MouseProtocolEncoding::new( self.mouse_protocol_encoding, prev.mouse_protocol_encoding, ) - .write_buf(contents); + .write_to(w)?; + Ok(()) } pub fn title_formatted(&self) -> Vec { let mut contents = vec![]; - self.write_title_formatted(&mut contents); + self.write_title_formatted(&mut contents).unwrap(); contents } - fn write_title_formatted(&self, contents: &mut Vec) { + pub fn write_title_formatted( + &self, + w: &mut W, + ) -> std::io::Result<()> { crate::term::ChangeTitle::new(&self.icon_name, &self.title, "", "") - .write_buf(contents); + .write_to(w) } pub fn title_diff(&self, prev: &Self) -> Vec { let mut contents = vec![]; - self.write_title_diff(&mut contents, prev); + self.write_title_diff(&mut contents, prev).unwrap(); contents } - fn write_title_diff(&self, contents: &mut Vec, prev: &Self) { + pub fn write_title_diff( + &self, + w: &mut W, + prev: &Self, + ) -> std::io::Result<()> { crate::term::ChangeTitle::new( &self.icon_name, &self.title, &prev.icon_name, &prev.title, ) - .write_buf(contents); + .write_to(w) } pub fn bells_diff(&self, prev: &Self) -> Vec { let mut contents = vec![]; - self.write_bells_diff(&mut contents, prev); + self.write_bells_diff(&mut contents, prev).unwrap(); contents } - fn write_bells_diff(&self, contents: &mut Vec, prev: &Self) { + pub fn write_bells_diff( + &self, + w: &mut W, + prev: &Self, + ) -> std::io::Result<()> { if self.audible_bell_count != prev.audible_bell_count { - crate::term::AudibleBell::default().write_buf(contents); + crate::term::AudibleBell::default().write_to(w)?; } if self.visual_bell_count != prev.visual_bell_count { - crate::term::VisualBell::default().write_buf(contents); + crate::term::VisualBell::default().write_to(w)?; } + Ok(()) } /// Returns the `Cell` object at the given location in the terminal, if it diff --git a/src/term.rs b/src/term.rs index d2f7c9b..3c6623b 100644 --- a/src/term.rs +++ b/src/term.rs @@ -1,16 +1,16 @@ // TODO: read all of this from terminfo -pub trait BufWrite { - fn write_buf(&self, buf: &mut Vec); +pub trait WriteTo { + fn write_to(&self, w: &mut W) -> std::io::Result<()>; } #[derive(Default, Debug)] -#[must_use = "this struct does nothing unless you call write_buf"] +#[must_use = "this struct does nothing unless you call write_to"] pub struct ClearScreen; -impl BufWrite for ClearScreen { - fn write_buf(&self, buf: &mut Vec) { - buf.extend_from_slice(b"\x1b[H\x1b[J"); +impl WriteTo for ClearScreen { + fn write_to(&self, w: &mut W) -> std::io::Result<()> { + w.write_all(b"\x1b[H\x1b[J") } } @@ -18,9 +18,9 @@ impl BufWrite for ClearScreen { #[must_use = "this struct does nothing unless you call write_buf"] pub struct ClearRowForward; -impl BufWrite for ClearRowForward { - fn write_buf(&self, buf: &mut Vec) { - buf.extend_from_slice(b"\x1b[K"); +impl WriteTo for ClearRowForward { + fn write_to(&self, w: &mut W) -> std::io::Result<()> { + w.write_all(b"\x1b[K") } } @@ -28,9 +28,9 @@ impl BufWrite for ClearRowForward { #[must_use = "this struct does nothing unless you call write_buf"] pub struct CRLF; -impl BufWrite for CRLF { - fn write_buf(&self, buf: &mut Vec) { - buf.extend_from_slice(b"\r\n"); +impl WriteTo for CRLF { + fn write_to(&self, w: &mut W) -> std::io::Result<()> { + w.write_all(b"\r\n") } } @@ -50,17 +50,18 @@ impl MoveTo { } } -impl BufWrite for MoveTo { - fn write_buf(&self, buf: &mut Vec) { +impl WriteTo for MoveTo { + fn write_to(&self, w: &mut W) -> std::io::Result<()> { if self.row == 0 && self.col == 0 { - buf.extend_from_slice(b"\x1b[H"); + w.write_all(b"\x1b[H")?; } else { - buf.extend_from_slice(b"\x1b["); - itoa::write(&mut *buf, self.row + 1).unwrap(); - buf.push(b';'); - itoa::write(&mut *buf, self.col + 1).unwrap(); - buf.push(b'H'); + w.write_all(b"\x1b[")?; + itoa::write(&mut *w, self.row + 1)?; + w.write_all(b";")?; + itoa::write(&mut *w, self.col + 1)?; + w.write_all(b"H")?; } + Ok(()) } } @@ -68,9 +69,9 @@ impl BufWrite for MoveTo { #[must_use = "this struct does nothing unless you call write_buf"] pub struct ClearAttrs; -impl BufWrite for ClearAttrs { - fn write_buf(&self, buf: &mut Vec) { - buf.extend_from_slice(b"\x1b[m") +impl WriteTo for ClearAttrs { + fn write_to(&self, w: &mut W) -> std::io::Result<()> { + w.write_all(b"\x1b[m") } } @@ -117,9 +118,9 @@ impl Attrs { } } -impl BufWrite for Attrs { +impl WriteTo for Attrs { #[allow(unused_assignments)] - fn write_buf(&self, buf: &mut Vec) { + fn write_to(&self, w: &mut W) -> std::io::Result<()> { if self.fgcolor.is_none() && self.bgcolor.is_none() && self.bold.is_none() @@ -127,10 +128,10 @@ impl BufWrite for Attrs { && self.underline.is_none() && self.inverse.is_none() { - return; + return Ok(()); } - buf.extend_from_slice(b"\x1b["); + w.write_all(b"\x1b[")?; let mut first = true; macro_rules! write_param { @@ -138,9 +139,9 @@ impl BufWrite for Attrs { if first { first = false; } else { - buf.push(b';'); + w.write_all(b";")?; } - itoa::write(&mut *buf, $i).unwrap(); + itoa::write(&mut *w, $i).unwrap(); }; } @@ -228,7 +229,7 @@ impl BufWrite for Attrs { } } - buf.push(b'm'); + w.write_all(b"m") } } @@ -250,17 +251,18 @@ impl Default for MoveRight { } } -impl BufWrite for MoveRight { - fn write_buf(&self, buf: &mut Vec) { +impl WriteTo for MoveRight { + fn write_to(&self, w: &mut W) -> std::io::Result<()> { match self.count { 0 => {} - 1 => buf.extend_from_slice(b"\x1b[C"), + 1 => w.write_all(b"\x1b[C")?, n => { - buf.extend_from_slice(b"\x1b["); - itoa::write(&mut *buf, n).unwrap(); - buf.push(b'C'); + w.write_all(b"\x1b[")?; + itoa::write(&mut *w, n)?; + w.write_all(b"C")?; } } + Ok(()) } } @@ -282,17 +284,18 @@ impl Default for EraseChar { } } -impl BufWrite for EraseChar { - fn write_buf(&self, buf: &mut Vec) { +impl WriteTo for EraseChar { + fn write_to(&self, w: &mut W) -> std::io::Result<()> { match self.count { 0 => {} - 1 => buf.extend_from_slice(b"\x1b[X"), + 1 => w.write_all(b"\x1b[X")?, n => { - buf.extend_from_slice(b"\x1b["); - itoa::write(&mut *buf, n).unwrap(); - buf.push(b'X'); + w.write_all(b"\x1b[")?; + itoa::write(&mut *w, n)?; + w.write_all(b"X")?; } } + Ok(()) } } @@ -308,12 +311,12 @@ impl HideCursor { } } -impl BufWrite for HideCursor { - fn write_buf(&self, buf: &mut Vec) { +impl WriteTo for HideCursor { + fn write_to(&self, w: &mut W) -> std::io::Result<()> { if self.state { - buf.extend_from_slice(b"\x1b[?25l") + w.write_all(b"\x1b[?25l") } else { - buf.extend_from_slice(b"\x1b[?25h") + w.write_all(b"\x1b[?25h") } } } @@ -331,16 +334,18 @@ impl MoveFromTo { } } -impl BufWrite for MoveFromTo { - fn write_buf(&self, buf: &mut Vec) { - if self.to.row == self.from.row + 1 && self.to.col == 0 { - crate::term::CRLF::default().write_buf(buf); - } else if self.from.row == self.to.row && self.from.col < self.to.col +impl WriteTo for MoveFromTo { + fn write_to(&self, w: &mut W) -> std::io::Result<()> { + if self.to == self.from { + Ok(()) + } else if self.to.row == self.from.row + 1 && self.to.col == 0 { + crate::term::CRLF::default().write_to(w) + } else if self.to.row == self.from.row && self.to.col > self.from.col { crate::term::MoveRight::new(self.to.col - self.from.col) - .write_buf(buf); - } else if self.to != self.from { - crate::term::MoveTo::new(self.to).write_buf(buf); + .write_to(w) + } else { + crate::term::MoveTo::new(self.to).write_to(w) } } } @@ -349,9 +354,9 @@ impl BufWrite for MoveFromTo { #[must_use = "this struct does nothing unless you call write_buf"] pub struct AudibleBell; -impl BufWrite for AudibleBell { - fn write_buf(&self, buf: &mut Vec) { - buf.push(b'\x07'); +impl WriteTo for AudibleBell { + fn write_to(&self, w: &mut W) -> std::io::Result<()> { + w.write_all(b"\x07") } } @@ -359,9 +364,9 @@ impl BufWrite for AudibleBell { #[must_use = "this struct does nothing unless you call write_buf"] pub struct VisualBell; -impl BufWrite for VisualBell { - fn write_buf(&self, buf: &mut Vec) { - buf.extend_from_slice(b"\x1bg"); +impl WriteTo for VisualBell { + fn write_to(&self, w: &mut W) -> std::io::Result<()> { + w.write_all(b"\x1bg") } } @@ -389,27 +394,28 @@ impl<'a> ChangeTitle<'a> { } } -impl<'a> BufWrite for ChangeTitle<'a> { - fn write_buf(&self, buf: &mut Vec) { +impl<'a, W: std::io::Write> WriteTo for ChangeTitle<'a> { + fn write_to(&self, w: &mut W) -> std::io::Result<()> { if self.icon_name == self.title && (self.icon_name != self.prev_icon_name || self.title != self.prev_title) { - buf.extend_from_slice(b"\x1b]0;"); - buf.extend_from_slice(self.icon_name.as_bytes()); - buf.push(b'\x07'); + w.write_all(b"\x1b]0;")?; + w.write_all(self.icon_name.as_bytes())?; + w.write_all(b"\x07")?; } else { if self.icon_name != self.prev_icon_name { - buf.extend_from_slice(b"\x1b]1;"); - buf.extend_from_slice(self.icon_name.as_bytes()); - buf.push(b'\x07'); + w.write_all(b"\x1b]1;")?; + w.write_all(self.icon_name.as_bytes())?; + w.write_all(b"\x07")?; } if self.title != self.prev_title { - buf.extend_from_slice(b"\x1b]2;"); - buf.extend_from_slice(self.title.as_bytes()); - buf.push(b'\x07'); + w.write_all(b"\x1b]2;")?; + w.write_all(self.icon_name.as_bytes())?; + w.write_all(b"\x07")?; } } + Ok(()) } } @@ -425,12 +431,12 @@ impl ApplicationKeypad { } } -impl BufWrite for ApplicationKeypad { - fn write_buf(&self, buf: &mut Vec) { +impl WriteTo for ApplicationKeypad { + fn write_to(&self, w: &mut W) -> std::io::Result<()> { if self.state { - buf.extend_from_slice(b"\x1b=") + w.write_all(b"\x1b=") } else { - buf.extend_from_slice(b"\x1b>") + w.write_all(b"\x1b>") } } } @@ -447,12 +453,12 @@ impl ApplicationCursor { } } -impl BufWrite for ApplicationCursor { - fn write_buf(&self, buf: &mut Vec) { +impl WriteTo for ApplicationCursor { + fn write_to(&self, w: &mut W) -> std::io::Result<()> { if self.state { - buf.extend_from_slice(b"\x1b[?1h") + w.write_all(b"\x1b[?1h") } else { - buf.extend_from_slice(b"\x1b[?1l") + w.write_all(b"\x1b[?1l") } } } @@ -469,12 +475,12 @@ impl BracketedPaste { } } -impl BufWrite for BracketedPaste { - fn write_buf(&self, buf: &mut Vec) { +impl WriteTo for BracketedPaste { + fn write_to(&self, w: &mut W) -> std::io::Result<()> { if self.state { - buf.extend_from_slice(b"\x1b[?2004h") + w.write_all(b"\x1b[?2004h") } else { - buf.extend_from_slice(b"\x1b[?2004l") + w.write_all(b"\x1b[?2004l") } } } @@ -495,39 +501,39 @@ impl MouseProtocolMode { } } -impl BufWrite for MouseProtocolMode { - fn write_buf(&self, buf: &mut Vec) { +impl WriteTo for MouseProtocolMode { + fn write_to(&self, w: &mut W) -> std::io::Result<()> { if self.mode == self.prev { - return; + return Ok(()); } match self.mode { crate::screen::MouseProtocolMode::None => match self.prev { - crate::screen::MouseProtocolMode::None => {} + crate::screen::MouseProtocolMode::None => Ok(()), crate::screen::MouseProtocolMode::Press => { - buf.extend_from_slice(b"\x1b[?9l"); + w.write_all(b"\x1b[?9l") } crate::screen::MouseProtocolMode::PressRelease => { - buf.extend_from_slice(b"\x1b[?1000l"); + w.write_all(b"\x1b[?1000l") } crate::screen::MouseProtocolMode::ButtonMotion => { - buf.extend_from_slice(b"\x1b[?1002l"); + w.write_all(b"\x1b[?1002l") } crate::screen::MouseProtocolMode::AnyMotion => { - buf.extend_from_slice(b"\x1b[?1003l"); + w.write_all(b"\x1b[?1003l") } }, crate::screen::MouseProtocolMode::Press => { - buf.extend_from_slice(b"\x1b[?9h"); + w.write_all(b"\x1b[?9h") } crate::screen::MouseProtocolMode::PressRelease => { - buf.extend_from_slice(b"\x1b[?1000h"); + w.write_all(b"\x1b[?1000h") } crate::screen::MouseProtocolMode::ButtonMotion => { - buf.extend_from_slice(b"\x1b[?1002h"); + w.write_all(b"\x1b[?1002h") } crate::screen::MouseProtocolMode::AnyMotion => { - buf.extend_from_slice(b"\x1b[?1003h"); + w.write_all(b"\x1b[?1003h") } } } @@ -549,29 +555,29 @@ impl MouseProtocolEncoding { } } -impl BufWrite for MouseProtocolEncoding { - fn write_buf(&self, buf: &mut Vec) { +impl WriteTo for MouseProtocolEncoding { + fn write_to(&self, w: &mut W) -> std::io::Result<()> { if self.encoding == self.prev { - return; + return Ok(()); } match self.encoding { crate::screen::MouseProtocolEncoding::Default => { match self.prev { - crate::screen::MouseProtocolEncoding::Default => {} + crate::screen::MouseProtocolEncoding::Default => Ok(()), crate::screen::MouseProtocolEncoding::Utf8 => { - buf.extend_from_slice(b"\x1b[?1005l"); + w.write_all(b"\x1b[?1005l") } crate::screen::MouseProtocolEncoding::Sgr => { - buf.extend_from_slice(b"\x1b[?1006l"); + w.write_all(b"\x1b[?1006l") } } } crate::screen::MouseProtocolEncoding::Utf8 => { - buf.extend_from_slice(b"\x1b[?1005h"); + w.write_all(b"\x1b[?1005h") } crate::screen::MouseProtocolEncoding::Sgr => { - buf.extend_from_slice(b"\x1b[?1006h"); + w.write_all(b"\x1b[?1006h") } } } -- cgit v1.2.3-54-g00ecf