diff options
author | Christian Duerr <contact@christianduerr.com> | 2018-04-30 00:22:15 +0200 |
---|---|---|
committer | Christian Duerr <contact@christianduerr.com> | 2018-04-30 00:41:21 +0200 |
commit | 946ef1e3067daef4b8131d5cc7c8d87175465e0a (patch) | |
tree | a48207aa1a20e7714e1b8f625ec6dfdf9a911972 | |
parent | cf23f56999bee9884de81cd7e046e9a53ebb1632 (diff) | |
download | alacritty-946ef1e3067daef4b8131d5cc7c8d87175465e0a.tar.gz alacritty-946ef1e3067daef4b8131d5cc7c8d87175465e0a.zip |
Improve the resizing implementation
Until now the resizing implementation with scrollback has been really
inefficient because it made use of APIs like `Vec::insert`. This has
been rewored with this commit.
A `len` property has been added to the `Storage` struct which keeps
track of the actual length of the raw buffer. This has changed both
shrinking and growing implementations.
With shrinking, no more lines are removed, only the `len` property is
updated to set all lines shrunk to an "invisible" state which cannot be
accessed from the outside, this effectively changes it to a O(1)
operation. The only issue with this would be memory consumption, but
since the maximum shrinkage is the number of lines on one screen, it
should be a rather small impacte (probabl <100 lines usually). If
desired it would be possible to change this to shrink the raw inner
buffer whenever there are more than X lines hidden.
Growing now works in a similar way to shrinking, if the "invisible"
lines are enough, no new lines are inserted but rather the invisible
buffer is made visible again. Otherwise the amount of lines that still
needs to be inserted is added to the raw buffer, but instead of the
inefficient `Vec::insert`, the `Vec::push` API is used now.
This fixes #1271.
-rw-r--r-- | src/grid/mod.rs | 2 | ||||
-rw-r--r-- | src/grid/storage.rs | 108 |
2 files changed, 62 insertions, 48 deletions
diff --git a/src/grid/mod.rs b/src/grid/mod.rs index ac54f580..13e6cc00 100644 --- a/src/grid/mod.rs +++ b/src/grid/mod.rs @@ -121,7 +121,7 @@ impl<T: Copy + Clone> Grid<T> { // TODO (jwilm) Allocating each line at this point is expensive and // delays startup. A nice solution might be having `Row` delay // allocation until it's actually used. - for _ in 0..raw.capacity() { + for _ in 0..raw.len() { raw.push(Row::new(cols, &template)); } diff --git a/src/grid/storage.rs b/src/grid/storage.rs index fb66b0c3..63b30831 100644 --- a/src/grid/storage.rs +++ b/src/grid/storage.rs @@ -20,6 +20,8 @@ pub struct Storage<T> { inner: Vec<T>, zero: usize, visible_lines: Line, + #[serde(skip)] + len: usize, } impl<T: PartialEq> ::std::cmp::PartialEq for Storage<T> { @@ -39,58 +41,49 @@ impl<T> Storage<T> { inner: Vec::with_capacity(cap), zero: 0, visible_lines: lines - 1, + len: cap, } } - #[inline] - pub fn capacity(&self) -> usize { - self.inner.capacity() - } - /// Increase the number of lines in the buffer pub fn grow_visible_lines(&mut self, next: Line, template_row: T) where T: Clone, { - // Calculate insert position (before the first line) - let offset = self.zero % self.inner.len(); - - // Insert new template row for every line grown + // Number of lines the buffer needs to grow let lines_to_grow = (next - (self.visible_lines + 1)).0; - for _ in 0..lines_to_grow { - self.inner.insert(offset, template_row.clone()); - } - // Set zero to old zero + lines grown - self.zero = offset + lines_to_grow; + // Only grow if there are not enough lines still hidden + if lines_to_grow > (self.inner.len() - self.len) { + // Lines to grow additionally to invisible lines + let new_lines_to_grow = lines_to_grow - (self.inner.len() - self.len); - // Update visible lines - self.visible_lines = next - 1; - } + // Get the position of the start of the buffer + let offset = self.zero % self.inner.len(); - /// Decrease the number of lines in the buffer - pub fn shrink_visible_lines(&mut self, next: Line) { - // Calculate shrinkage and last line of buffer - let shrinkage = (self.visible_lines + 1 - next).0; - let offset = (self.zero + self.inner.len() - 1) % self.inner.len(); + // Split off the beginning of the raw inner buffer + let mut start_buffer = self.inner.split_off(offset); - // Generate range of lines that have to be deleted before the zero line - let start = offset.saturating_sub(shrinkage - 1); - let shrink_before = start..(offset + 1); + // Insert new template rows at the end of the raw inner buffer + let mut new_lines = vec![template_row; new_lines_to_grow]; + self.inner.append(&mut new_lines); - // Generate range of lines that have to be deleted after the zero line - let shrink_after = (self.inner.len() + offset + 1 - shrinkage)..self.inner.len(); + // Add the start to the raw inner buffer again + self.inner.append(&mut start_buffer); - // Delete all lines in reverse order - for i in shrink_before.chain(shrink_after).rev() { - self.inner.remove(i); + // Update the zero to after the lines we just inserted + self.zero = offset + lines_to_grow; } - // Check if zero has moved (not the first line in the buffer) - if self.zero % (self.inner.len() + shrinkage) != 0 { - // Set zero to the first deleted line in the buffer - self.zero = start; - } + // Update visible lines and raw buffer length + self.len += lines_to_grow; + self.visible_lines = next - 1; + } + + /// Decrease the number of lines in the buffer + pub fn shrink_visible_lines(&mut self, next: Line) { + // Shrink the size without removing any lines + self.len -= (self.visible_lines - (next - 1)).0; // Update visible lines self.visible_lines = next - 1; @@ -103,17 +96,17 @@ impl<T> Storage<T> { #[inline] pub fn len(&self) -> usize { - self.inner.len() + self.len } /// Compute actual index in underlying storage given the requested index. #[inline] fn compute_index(&self, requested: usize) -> usize { - (requested + self.zero) % self.len() + (requested + self.zero) % self.inner.len() } fn compute_line_index(&self, requested: Line) -> usize { - ((self.len() + self.zero + *self.visible_lines) - *requested) % self.len() + ((self.inner.len() + self.zero + *self.visible_lines) - *requested) % self.inner.len() } pub fn swap_lines(&mut self, a: Line, b: Line) { @@ -127,14 +120,14 @@ impl<T> Storage<T> { } pub fn rotate(&mut self, count: isize) { - let len = self.len(); + let len = self.inner.len(); assert!(count.abs() as usize <= len); self.zero += (count + len as isize) as usize % len; } // Fast path pub fn rotate_up(&mut self, count: usize) { - self.zero = (self.zero + count) % self.len(); + self.zero = (self.zero + count) % self.inner.len(); } } @@ -212,6 +205,7 @@ fn grow_after_zero() { inner: vec!["0", "1", "-"], zero: 0, visible_lines: Line(2), + len: 3, }; // Grow buffer @@ -222,9 +216,11 @@ fn grow_after_zero() { inner: vec!["-", "0", "1", "-"], zero: 1, visible_lines: Line(0), + len: 4, }; assert_eq!(storage.inner, expected.inner); assert_eq!(storage.zero, expected.zero); + assert_eq!(storage.len, expected.len); } /// Grow the buffer one line at the start of the buffer @@ -245,6 +241,7 @@ fn grow_before_zero() { inner: vec!["-", "0", "1"], zero: 1, visible_lines: Line(2), + len: 3, }; // Grow buffer @@ -255,9 +252,11 @@ fn grow_before_zero() { inner: vec!["-", "-", "0", "1"], zero: 2, visible_lines: Line(0), + len: 4, }; assert_eq!(storage.inner, expected.inner); assert_eq!(storage.zero, expected.zero); + assert_eq!(storage.len, expected.len); } /// Shrink the buffer one line at the start of the buffer @@ -267,6 +266,7 @@ fn grow_before_zero() { /// 1: 0 <- Zero /// 2: 1 /// After: +/// 0: 2 <- Hidden /// 0: 0 <- Zero /// 1: 1 #[test] @@ -276,6 +276,7 @@ fn shrink_before_zero() { inner: vec!["2", "0", "1"], zero: 1, visible_lines: Line(2), + len: 3, }; // Shrink buffer @@ -283,12 +284,14 @@ fn shrink_before_zero() { // Make sure the result is correct let expected = Storage { - inner: vec!["0", "1"], - zero: 0, + inner: vec!["2", "0", "1"], + zero: 1, visible_lines: Line(0), + len: 2, }; assert_eq!(storage.inner, expected.inner); assert_eq!(storage.zero, expected.zero); + assert_eq!(storage.len, expected.len); } /// Shrink the buffer one line at the end of the buffer @@ -300,6 +303,7 @@ fn shrink_before_zero() { /// After: /// 0: 0 <- Zero /// 1: 1 +/// 2: 2 <- Hidden #[test] fn shrink_after_zero() { // Setup storage area @@ -307,6 +311,7 @@ fn shrink_after_zero() { inner: vec!["0", "1", "2"], zero: 0, visible_lines: Line(2), + len: 3, }; // Shrink buffer @@ -314,12 +319,14 @@ fn shrink_after_zero() { // Make sure the result is correct let expected = Storage { - inner: vec!["0", "1"], + inner: vec!["0", "1", "2"], zero: 0, visible_lines: Line(0), + len: 2, }; assert_eq!(storage.inner, expected.inner); assert_eq!(storage.zero, expected.zero); + assert_eq!(storage.len, expected.len); } /// Shrink the buffer at the start and end of the buffer @@ -332,8 +339,12 @@ fn shrink_after_zero() { /// 4: 2 /// 5: 3 /// After: -/// 0: 0 <- Zero -/// 1: 1 +/// 0: 4 <- Hidden +/// 1: 5 <- Hidden +/// 2: 0 <- Zero +/// 3: 1 +/// 4: 2 <- Hidden +/// 5: 3 <- Hidden #[test] fn shrink_before_and_after_zero() { // Setup storage area @@ -341,6 +352,7 @@ fn shrink_before_and_after_zero() { inner: vec!["4", "5", "0", "1", "2", "3"], zero: 2, visible_lines: Line(5), + len: 6, }; // Shrink buffer @@ -348,10 +360,12 @@ fn shrink_before_and_after_zero() { // Make sure the result is correct let expected = Storage { - inner: vec!["0", "1"], - zero: 0, + inner: vec!["4", "5", "0", "1", "2", "3"], + zero: 2, visible_lines: Line(0), + len: 2, }; assert_eq!(storage.inner, expected.inner); assert_eq!(storage.zero, expected.zero); + assert_eq!(storage.len, expected.len); } |