From 596730bf19f04a97ef835b27466998b50f26e230 Mon Sep 17 00:00:00 2001 From: Jesse Luehrs Date: Sat, 9 Nov 2019 10:42:44 -0500 Subject: use a fixed size buffer for cell data this dramatically speeds things up --- src/cell.rs | 78 +++++++++++++++++++++++++++++++++++++++++++++-------------- src/row.rs | 2 +- tests/text.rs | 8 ++++++ 3 files changed, 69 insertions(+), 19 deletions(-) diff --git a/src/cell.rs b/src/cell.rs index d0d6f0c..61a3cd3 100644 --- a/src/cell.rs +++ b/src/cell.rs @@ -1,33 +1,73 @@ use unicode_normalization::UnicodeNormalization as _; use unicode_width::UnicodeWidthChar as _; +const CODEPOINTS_IN_CELL: usize = 6; + /// Represents a single terminal cell. -#[derive(Clone, Debug, Default, Eq, PartialEq)] +#[derive(Clone, Debug, Default, Eq)] pub struct Cell { - contents: String, + contents: [char; CODEPOINTS_IN_CELL], + len: u8, attrs: crate::attrs::Attrs, } +impl PartialEq for Cell { + fn eq(&self, other: &Self) -> bool { + if self.attrs != other.attrs { + return false; + } + if self.len != other.len { + return false; + } + let len = self.len as usize; + self.contents[..len] == other.contents[..len] + } +} + impl Cell { pub(crate) fn set(&mut self, c: char, a: crate::attrs::Attrs) { - let mut buf = vec![0; 4]; - c.encode_utf8(&mut buf); - self.contents = unsafe { String::from_utf8_unchecked(buf) }; + self.contents[0] = c; + self.len = 1; self.attrs = a; } pub(crate) fn append(&mut self, c: char) { - self.contents.push(c); + if self.len as usize >= CODEPOINTS_IN_CELL { + return; + } + + self.contents[self.len as usize] = c; + self.len += 1; + // some fonts have combined characters but can't render combining // characters correctly, so try to prefer precombined characters when // possible - if !unicode_normalization::is_nfc(&self.contents) { - self.contents = self.contents.nfc().collect(); + if unicode_normalization::is_nfc_quick( + self.contents.iter().copied().take(CODEPOINTS_IN_CELL), + ) == unicode_normalization::IsNormalized::Yes + { + return; } + + let mut new_contents = ['\x00'; CODEPOINTS_IN_CELL]; + let mut new_len = 0; + for c in self + .contents + .iter() + .copied() + .take(self.len as usize) + .nfc() + .take(CODEPOINTS_IN_CELL) + { + new_contents[new_len as usize] = c; + new_len += 1; + } + self.contents = new_contents; + self.len = new_len; } pub(crate) fn clear(&mut self, bgcolor: crate::attrs::Color) { - self.contents.clear(); + self.len = 0; self.attrs.clear(); self.attrs.bgcolor = bgcolor; } @@ -37,13 +77,16 @@ impl Cell { /// Can include multiple unicode characters if combining characters are /// used, but will contain at most one character with a non-zero character /// width. - pub fn contents(&self) -> &str { - &self.contents + pub fn contents(&self) -> String { + self.contents + .iter() + .take(self.len as usize) + .collect::() } /// Returns whether the cell contains any text data. pub fn has_contents(&self) -> bool { - self.contents != "" + self.len > 0 } /// Returns whether the text data in the cell represents a wide character. @@ -51,12 +94,11 @@ impl Cell { // strings in this context should always be an arbitrary character // followed by zero or more zero-width characters, so we should only // have to look at the first character - let width = self - .contents - .chars() - .next() - .map_or(0, |c| c.width().unwrap_or(0)); - width > 1 + if self.len == 0 { + false + } else { + self.contents[0].width().unwrap_or(0) > 1 + } } pub(crate) fn attrs(&self) -> &crate::attrs::Attrs { diff --git a/src/row.rs b/src/row.rs index 4655f48..4511183 100644 --- a/src/row.rs +++ b/src/row.rs @@ -85,7 +85,7 @@ impl Row { if cell.has_contents() { // using write! here is significantly slower, for some reason // write!(contents, "{}", cell.contents()).unwrap(); - contents.push_str(cell.contents()); + contents.push_str(&cell.contents()); } else { contents.push(' '); } diff --git a/tests/text.rs b/tests/text.rs index 5c16039..e588a68 100644 --- a/tests/text.rs +++ b/tests/text.rs @@ -149,6 +149,14 @@ fn combining() { parser.process("\r\n\u{0301}".as_bytes()); assert_eq!(parser.screen().cell(9, 79).unwrap().contents(), "a"); assert_eq!(parser.screen().cell(10, 0).unwrap().contents(), ""); + + parser.process("\x1bcabcdefg\x1b[1;3H\u{0301}".as_bytes()); + assert_eq!(parser.screen().contents(), "ab́cdefg"); + parser.process("\x1b[1;2Hb\x1b[1;8H".as_bytes()); + assert_eq!(parser.screen().contents(), "abcdefg"); + let screen = parser.screen().clone(); + parser.process(b"\x1bcabcdefg"); + assert_eq!(parser.screen().contents_diff(&screen), b"\x1b[m"); } #[test] -- cgit v1.2.3-54-g00ecf