diff options
author | Jesse Luehrs <doy@tozt.net> | 2019-10-16 13:42:25 -0400 |
---|---|---|
committer | Jesse Luehrs <doy@tozt.net> | 2019-10-16 14:05:22 -0400 |
commit | a16eafac1f98b389cd2eb337120607dfbe584393 (patch) | |
tree | 068d0e13ac038b03a8f6ec0c975176b6fd697659 | |
parent | 78933dcd8c55f9f14386342b3e6f63285bd59fec (diff) | |
download | teleterm-a16eafac1f98b389cd2eb337120607dfbe584393.tar.gz teleterm-a16eafac1f98b389cd2eb337120607dfbe584393.zip |
use different logic for buffering on the client side vs server
they have different needs and performance profiles, so try to be a bit
more intelligent
-rw-r--r-- | src/cmd/record.rs | 2 | ||||
-rw-r--r-- | src/cmd/stream.rs | 2 | ||||
-rw-r--r-- | src/server.rs | 2 | ||||
-rw-r--r-- | src/term.rs | 216 |
4 files changed, 154 insertions, 68 deletions
diff --git a/src/cmd/record.rs b/src/cmd/record.rs index 6702ba9..c233dc4 100644 --- a/src/cmd/record.rs +++ b/src/cmd/record.rs @@ -101,7 +101,7 @@ impl RecordSession { } fn record_bytes(&mut self, buf: &[u8]) { - self.sent_local -= self.buffer.append(buf, self.sent_local); + self.sent_local -= self.buffer.append_client(buf, self.sent_local); } } diff --git a/src/cmd/stream.rs b/src/cmd/stream.rs index c1d62c6..42a4296 100644 --- a/src/cmd/stream.rs +++ b/src/cmd/stream.rs @@ -183,7 +183,7 @@ impl<S: tokio::io::AsyncRead + tokio::io::AsyncWrite + Send + 'static> } else { self.sent_local }; - let truncated = self.buffer.append(buf, written); + let truncated = self.buffer.append_client(buf, written); self.sent_local -= truncated; if self.connected { self.sent_remote -= truncated; diff --git a/src/server.rs b/src/server.rs index 6d38220..42b4c66 100644 --- a/src/server.rs +++ b/src/server.rs @@ -542,7 +542,7 @@ impl<S: tokio::io::AsyncRead + tokio::io::AsyncWrite + Send + 'static> ) -> Result<()> { let saved_data = conn.state.saved_data_mut().unwrap(); - saved_data.append(data, saved_data.len()); + saved_data.append_server(data); for watch_conn in self.watchers_mut() { let watch_id = watch_conn.state.watch_id().unwrap(); if conn.id == watch_id { diff --git a/src/term.rs b/src/term.rs index fd82604..6b25762 100644 --- a/src/term.rs +++ b/src/term.rs @@ -56,18 +56,23 @@ impl Buffer { self_ } - pub fn append(&mut self, buf: &[u8], written: usize) -> usize { + // doesn't parse out window titles, since we don't care about them. also + // tries to spread out truncations a bit to keep each individual run of + // this function reasonably fast (trying to avoid most runs being fast but + // occasionally one being extra slow when it hits a limit). this makes it + // a bit less accurate (clears don't actually reset the entire terminal + // state, so dropping everything before them when we see one isn't quite + // correct), but the client side buffer is only used in the case where + // someone reconnects after disconnecting from the server, which should be + // uncommon enough that it shouldn't matter all that much. + pub fn append_client(&mut self, buf: &[u8], written: usize) -> usize { self.contents.extend_from_slice(buf); - if let Some(title) = self.find_window_title(buf) { - self.title = title; - } - if written == 0 { return written; } - if self.has_reset(buf) { + if self.find_reset(buf).is_some() { self.truncate_at(written); return written; } @@ -84,6 +89,32 @@ impl Buffer { 0 } + // aim for accuracy over speed - on the client side, we're guaranteed to + // see every byte because the bytes are coming from the client, but our + // truncation behavior isn't particularly guaranteed to give correct + // results in all cases, so try to avoid doing it too much if we can help + // it. this makes this function slower (and spikier) than append_client, + // but that's pretty okay because we don't care as much about latency + // here. + pub fn append_server(&mut self, buf: &[u8]) { + self.contents.extend_from_slice(buf); + + if let Some(title) = self.find_window_title(buf) { + self.title = title; + } + + while self.contents.len() > self.max_size { + let hard_truncate = self.contents.len() - self.max_size / 2; + // off by one so that we skip over a reset escape sequence at the + // start of the buffer + if let Some(i) = self.find_reset(&self.contents[1..]) { + self.truncate_at((i + 1).min(hard_truncate)); + } else { + self.truncate_at(hard_truncate); + } + } + } + pub fn contents(&self) -> &[u8] { &self.contents } @@ -119,13 +150,13 @@ impl Buffer { found.map(|(_, title)| title.to_string()) } - fn has_reset(&self, buf: &[u8]) -> bool { + fn find_reset(&self, buf: &[u8]) -> Option<usize> { for reset in RESET { - if twoway::find_bytes(buf, reset).is_some() { - return true; + if let Some(i) = twoway::find_bytes(buf, reset) { + return Some(i); } } - false + None } fn truncate_at(&mut self, i: usize) { @@ -148,13 +179,13 @@ mod test { assert_eq!(buffer.len(), 0); assert_eq!(buffer.title(), ""); - let n = buffer.append(b"foo", 0); + let n = buffer.append_client(b"foo", 0); assert_eq!(buffer.contents(), b"foo"); assert_eq!(buffer.len(), 3); assert_eq!(buffer.title(), ""); assert_eq!(n, 0); - let n = buffer.append(b"bar", 3); + let n = buffer.append_client(b"bar", 3); assert_eq!(buffer.contents(), b"foobar"); assert_eq!(buffer.len(), 6); assert_eq!(buffer.title(), ""); @@ -165,135 +196,137 @@ mod test { fn test_clear() { let mut buffer = Buffer::new(100); - let n = buffer.append(b"foo", 0); - assert_eq!(buffer.len(), 3); + let n = buffer.append_client(b"foo", 0); assert_eq!(buffer.contents(), b"foo"); + assert_eq!(buffer.len(), 3); assert_eq!(buffer.title(), ""); assert_eq!(n, 0); - let n = buffer.append(b"\x1b[3J\x1b[H\x1b[2J", 3); - assert_eq!(buffer.len(), 11); + let n = buffer.append_client(b"\x1b[3J\x1b[H\x1b[2J", 3); assert_eq!(buffer.contents(), b"\x1b[3J\x1b[H\x1b[2J"); + assert_eq!(buffer.len(), 11); assert_eq!(buffer.title(), ""); assert_eq!(n, 3); - let n = buffer.append(b"bar", 11); - assert_eq!(buffer.len(), 14); + let n = buffer.append_client(b"bar", 11); assert_eq!(buffer.contents(), b"\x1b[3J\x1b[H\x1b[2Jbar"); + assert_eq!(buffer.len(), 14); assert_eq!(buffer.title(), ""); assert_eq!(n, 0); - let n = buffer.append(b"baz\x1bcquux", 14); - assert_eq!(buffer.len(), 9); + let n = buffer.append_client(b"baz\x1bcquux", 14); assert_eq!(buffer.contents(), b"baz\x1bcquux"); + assert_eq!(buffer.len(), 9); assert_eq!(buffer.title(), ""); assert_eq!(n, 14); - let n = buffer.append(b"blorg\x1b[H\x1b[J", 9); - assert_eq!(buffer.len(), 11); + let n = buffer.append_client(b"blorg\x1b[H\x1b[J", 9); assert_eq!(buffer.contents(), b"blorg\x1b[H\x1b[J"); + assert_eq!(buffer.len(), 11); assert_eq!(buffer.title(), ""); assert_eq!(n, 9); - let n = buffer.append(b"\x1b[H\x1b[2Jabc", 11); - assert_eq!(buffer.len(), 10); + let n = buffer.append_client(b"\x1b[H\x1b[2Jabc", 11); assert_eq!(buffer.contents(), b"\x1b[H\x1b[2Jabc"); + assert_eq!(buffer.len(), 10); assert_eq!(buffer.title(), ""); assert_eq!(n, 11); - let n = buffer.append(b"first\x1bcsecond\x1b[H\x1b[2Jthird", 10); - assert_eq!(buffer.len(), 25); + let n = + buffer.append_client(b"first\x1bcsecond\x1b[H\x1b[2Jthird", 10); assert_eq!(buffer.contents(), b"first\x1bcsecond\x1b[H\x1b[2Jthird"); + assert_eq!(buffer.len(), 25); assert_eq!(buffer.title(), ""); assert_eq!(n, 10); - let n = buffer.append(b"first\x1b[H\x1b[2Jsecond\x1bcthird", 25); - assert_eq!(buffer.len(), 25); + let n = + buffer.append_client(b"first\x1b[H\x1b[2Jsecond\x1bcthird", 25); assert_eq!(buffer.contents(), b"first\x1b[H\x1b[2Jsecond\x1bcthird"); + assert_eq!(buffer.len(), 25); assert_eq!(buffer.title(), ""); assert_eq!(n, 25); } #[test] fn test_title() { - let mut buffer = Buffer::new(100); + let mut buffer = Buffer::new(50); - let n = buffer.append(b"\x1b]0;this is a title\x07", 0); - assert_eq!(buffer.len(), 20); + buffer.append_server(b"\x1b]0;this is a title\x07"); assert_eq!(buffer.contents(), b"\x1b]0;this is a title\x07"); + assert_eq!(buffer.len(), 20); assert_eq!(buffer.title(), "this is a title"); - assert_eq!(n, 0); - let n = buffer.append(b"\x1b]2;this is another title\x07", 20); - assert_eq!(buffer.len(), 46); + buffer.append_server(b"\x1b]2;this is another title\x07"); assert_eq!( buffer.contents(), &b"\x1b]0;this is a title\x07\x1b]2;this is another title\x07"[..] ); + assert_eq!(buffer.len(), 46); assert_eq!(buffer.title(), "this is another title"); - assert_eq!(n, 0); - let n = buffer.append(b"\x1bcfoo", 46); - assert_eq!(buffer.len(), 5); - assert_eq!(buffer.contents(), b"\x1bcfoo"); + buffer.append_server(b"\x1bcfoo"); + assert_eq!( + buffer.contents(), + &b"is is another title\x07\x1bcfoo"[..] + ); + assert_eq!(buffer.len(), 25); assert_eq!(buffer.title(), "this is another title"); - assert_eq!(n, 46); - let n = buffer - .append(b"\x1bcabc\x1b]0;title1\x07def\x1b]2;title2\x07ghi", 5); - assert_eq!(buffer.len(), 33); + buffer.append_server( + b"\x1bcabc\x1b]0;title1\x07def\x1b]2;title2\x07ghi", + ); assert_eq!( buffer.contents(), - &b"\x1bcabc\x1b]0;title1\x07def\x1b]2;title2\x07ghi"[..] + &b"\x1bcfoo\x1bcabc\x1b]0;title1\x07def\x1b]2;title2\x07ghi"[..] ); + assert_eq!(buffer.len(), 38); assert_eq!(buffer.title(), "title2"); - assert_eq!(n, 5); - let n = buffer - .append(b"\x1bcabc\x1b]2;title3\x07def\x1b]0;title4\x07ghi", 33); - assert_eq!(buffer.len(), 33); + buffer.append_server( + b"\x1bcabc\x1b]2;title3\x07def\x1b]0;title4\x07ghi", + ); assert_eq!( buffer.contents(), &b"\x1bcabc\x1b]2;title3\x07def\x1b]0;title4\x07ghi"[..] ); + assert_eq!(buffer.len(), 33); assert_eq!(buffer.title(), "title4"); - assert_eq!(n, 33); } #[test] - fn test_size_limit() { + fn test_size_limit_client() { let mut buffer = Buffer::new(100); - let n = buffer.append(b"foobarbazq", 0); - assert_eq!(buffer.len(), 10); + let n = buffer.append_client(b"foobarbazq", 0); assert_eq!(buffer.contents(), b"foobarbazq"); + assert_eq!(buffer.len(), 10); assert_eq!(buffer.title(), ""); assert_eq!(n, 0); - let n = buffer.append("0123456789".repeat(9).as_ref(), 10); - assert_eq!(buffer.len(), 100); + let n = buffer.append_client("0123456789".repeat(9).as_ref(), 10); assert_eq!( buffer.contents(), &b"foobarbazq012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789"[..] ); + assert_eq!(buffer.len(), 100); assert_eq!(buffer.title(), ""); assert_eq!(n, 0); - let n = buffer.append(b"z", 100); - assert_eq!(buffer.len(), 50); + let n = buffer.append_client(b"z", 100); assert_eq!( buffer.contents(), &b"1234567890123456789012345678901234567890123456789z"[..] ); + assert_eq!(buffer.len(), 50); assert_eq!(buffer.title(), ""); assert_eq!(n, 51); - let n = buffer.append("abcdefghij".repeat(15).as_ref(), 50); - assert_eq!(buffer.len(), 150); + let n = buffer.append_client("abcdefghij".repeat(15).as_ref(), 50); assert_eq!( buffer.contents(), &b"abcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghij"[..] ); + assert_eq!(buffer.len(), 150); assert_eq!(buffer.title(), ""); assert_eq!(n, 50); } @@ -302,43 +335,96 @@ mod test { fn test_written() { let mut buffer = Buffer::new(100); - let n = buffer.append("abcdefghij".repeat(15).as_ref(), 0); - assert_eq!(buffer.len(), 150); + let n = buffer.append_client("abcdefghij".repeat(15).as_ref(), 0); assert_eq!( buffer.contents(), &b"abcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghij"[..] ); + assert_eq!(buffer.len(), 150); assert_eq!(buffer.title(), ""); assert_eq!(n, 0); let mut buffer2 = buffer.clone(); - let n = buffer2.append(b"z", 0); - assert_eq!(buffer2.len(), 151); + let n = buffer2.append_client(b"z", 0); assert_eq!( buffer2.contents(), &b"abcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijz"[..] ); + assert_eq!(buffer2.len(), 151); assert_eq!(buffer2.title(), ""); assert_eq!(n, 0); let mut buffer2 = buffer.clone(); - let n = buffer2.append(b"z", 20); - assert_eq!(buffer2.len(), 131); + let n = buffer2.append_client(b"z", 20); assert_eq!( buffer2.contents(), &b"abcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijz"[..] ); + assert_eq!(buffer2.len(), 131); assert_eq!(buffer2.title(), ""); assert_eq!(n, 20); let mut buffer2 = buffer.clone(); - let n = buffer2.append(b"z", 130); - assert_eq!(buffer2.len(), 50); + let n = buffer2.append_client(b"z", 130); assert_eq!( buffer2.contents(), &b"bcdefghijabcdefghijabcdefghijabcdefghijabcdefghijz"[..] ); + assert_eq!(buffer2.len(), 50); assert_eq!(buffer2.title(), ""); assert_eq!(n, 101); } + + #[test] + fn test_server() { + let mut buffer = Buffer::new(100); + + buffer.append_server(b"foobar"); + assert_eq!(buffer.contents(), b"foobar"); + assert_eq!(buffer.len(), 6); + assert_eq!(buffer.title(), ""); + + buffer.append_server(b"\x1b[3J\x1b[H\x1b[2J"); + assert_eq!(buffer.contents(), b"foobar\x1b[3J\x1b[H\x1b[2J"); + assert_eq!(buffer.len(), 17); + assert_eq!(buffer.title(), ""); + + buffer.append_server("abcdefghij".repeat(8).as_ref()); + assert_eq!(buffer.contents(), &b"foobar\x1b[3J\x1b[H\x1b[2Jabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghij"[..]); + assert_eq!(buffer.len(), 97); + assert_eq!(buffer.title(), ""); + + buffer.append_server(b"abcd"); + assert_eq!(buffer.contents(), &b"\x1b[3J\x1b[H\x1b[2Jabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcd"[..]); + assert_eq!(buffer.len(), 95); + assert_eq!(buffer.title(), ""); + + buffer.append_server("\x1bcfooobaar".repeat(8).as_ref()); + assert_eq!(buffer.contents(), &b"\x1bcfooobaar\x1bcfooobaar\x1bcfooobaar\x1bcfooobaar\x1bcfooobaar\x1bcfooobaar\x1bcfooobaar\x1bcfooobaar"[..]); + assert_eq!(buffer.len(), 80); + assert_eq!(buffer.title(), ""); + + buffer.append_server("abcdefghij".repeat(5).as_ref()); + assert_eq!(buffer.contents(), &b"\x1bcfooobaar\x1bcfooobaar\x1bcfooobaar\x1bcfooobaar\x1bcfooobaarabcdefghijabcdefghijabcdefghijabcdefghijabcdefghij"[..]); + assert_eq!(buffer.len(), 100); + assert_eq!(buffer.title(), ""); + + buffer.append_server(b"z"); + assert_eq!(buffer.contents(), &b"\x1bcfooobaar\x1bcfooobaar\x1bcfooobaar\x1bcfooobaarabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijz"[..]); + assert_eq!(buffer.len(), 91); + assert_eq!(buffer.title(), ""); + + buffer.append_server(b"bcdefghijabcdefghij\x1bcfooobaar"); + assert_eq!(buffer.contents(), &b"\x1bcfooobaar\x1bcfooobaarabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijzbcdefghijabcdefghij\x1bcfooobaar"[..]); + assert_eq!(buffer.len(), 100); + assert_eq!(buffer.title(), ""); + + buffer.append_server(b"abcdefghijz"); + assert_eq!( + buffer.contents(), + &b"bcdefghijzbcdefghijabcdefghij\x1bcfooobaarabcdefghijz"[..] + ); + assert_eq!(buffer.len(), 50); + assert_eq!(buffer.title(), ""); + } } |