aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJesse Luehrs <doy@tozt.net>2019-10-16 13:42:25 -0400
committerJesse Luehrs <doy@tozt.net>2019-10-16 14:05:22 -0400
commita16eafac1f98b389cd2eb337120607dfbe584393 (patch)
tree068d0e13ac038b03a8f6ec0c975176b6fd697659
parent78933dcd8c55f9f14386342b3e6f63285bd59fec (diff)
downloadteleterm-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.rs2
-rw-r--r--src/cmd/stream.rs2
-rw-r--r--src/server.rs2
-rw-r--r--src/term.rs216
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(), "");
+ }
}