1
0
Fork 0
mirror of https://github.com/alacritty/alacritty.git synced 2024-11-18 13:55:23 -05:00

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.
This commit is contained in:
Christian Duerr 2018-04-30 00:22:15 +02:00
parent cf23f56999
commit 946ef1e306
No known key found for this signature in database
GPG key ID: 85CDAE3C164BA7B4
2 changed files with 63 additions and 49 deletions

View file

@ -121,7 +121,7 @@ impl<T: Copy + Clone> Grid<T> {
// TODO (jwilm) Allocating each line at this point is expensive and // TODO (jwilm) Allocating each line at this point is expensive and
// delays startup. A nice solution might be having `Row` delay // delays startup. A nice solution might be having `Row` delay
// allocation until it's actually used. // allocation until it's actually used.
for _ in 0..raw.capacity() { for _ in 0..raw.len() {
raw.push(Row::new(cols, &template)); raw.push(Row::new(cols, &template));
} }

View file

@ -20,6 +20,8 @@ pub struct Storage<T> {
inner: Vec<T>, inner: Vec<T>,
zero: usize, zero: usize,
visible_lines: Line, visible_lines: Line,
#[serde(skip)]
len: usize,
} }
impl<T: PartialEq> ::std::cmp::PartialEq for Storage<T> { impl<T: PartialEq> ::std::cmp::PartialEq for Storage<T> {
@ -39,58 +41,49 @@ impl<T> Storage<T> {
inner: Vec::with_capacity(cap), inner: Vec::with_capacity(cap),
zero: 0, zero: 0,
visible_lines: lines - 1, visible_lines: lines - 1,
len: cap,
} }
} }
#[inline]
pub fn capacity(&self) -> usize {
self.inner.capacity()
}
/// Increase the number of lines in the buffer /// Increase the number of lines in the buffer
pub fn grow_visible_lines(&mut self, next: Line, template_row: T) pub fn grow_visible_lines(&mut self, next: Line, template_row: T)
where where
T: Clone, T: Clone,
{ {
// Calculate insert position (before the first line) // Number of lines the buffer needs to grow
let offset = self.zero % self.inner.len();
// Insert new template row for every line grown
let lines_to_grow = (next - (self.visible_lines + 1)).0; let lines_to_grow = (next - (self.visible_lines + 1)).0;
for _ in 0..lines_to_grow {
self.inner.insert(offset, template_row.clone()); // 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);
// Get the position of the start of the buffer
let offset = self.zero % self.inner.len();
// Split off the beginning of the raw inner buffer
let mut start_buffer = self.inner.split_off(offset);
// 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);
// Add the start to the raw inner buffer again
self.inner.append(&mut start_buffer);
// Update the zero to after the lines we just inserted
self.zero = offset + lines_to_grow;
} }
// Set zero to old zero + lines grown // Update visible lines and raw buffer length
self.zero = offset + lines_to_grow; self.len += lines_to_grow;
// Update visible lines
self.visible_lines = next - 1; self.visible_lines = next - 1;
} }
/// Decrease the number of lines in the buffer /// Decrease the number of lines in the buffer
pub fn shrink_visible_lines(&mut self, next: Line) { pub fn shrink_visible_lines(&mut self, next: Line) {
// Calculate shrinkage and last line of buffer // Shrink the size without removing any lines
let shrinkage = (self.visible_lines + 1 - next).0; self.len -= (self.visible_lines - (next - 1)).0;
let offset = (self.zero + self.inner.len() - 1) % self.inner.len();
// 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);
// 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();
// Delete all lines in reverse order
for i in shrink_before.chain(shrink_after).rev() {
self.inner.remove(i);
}
// 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 // Update visible lines
self.visible_lines = next - 1; self.visible_lines = next - 1;
@ -103,17 +96,17 @@ impl<T> Storage<T> {
#[inline] #[inline]
pub fn len(&self) -> usize { pub fn len(&self) -> usize {
self.inner.len() self.len
} }
/// Compute actual index in underlying storage given the requested index. /// Compute actual index in underlying storage given the requested index.
#[inline] #[inline]
fn compute_index(&self, requested: usize) -> usize { 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 { 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) { 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) { pub fn rotate(&mut self, count: isize) {
let len = self.len(); let len = self.inner.len();
assert!(count.abs() as usize <= len); assert!(count.abs() as usize <= len);
self.zero += (count + len as isize) as usize % len; self.zero += (count + len as isize) as usize % len;
} }
// Fast path // Fast path
pub fn rotate_up(&mut self, count: usize) { 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", "-"], inner: vec!["0", "1", "-"],
zero: 0, zero: 0,
visible_lines: Line(2), visible_lines: Line(2),
len: 3,
}; };
// Grow buffer // Grow buffer
@ -222,9 +216,11 @@ fn grow_after_zero() {
inner: vec!["-", "0", "1", "-"], inner: vec!["-", "0", "1", "-"],
zero: 1, zero: 1,
visible_lines: Line(0), visible_lines: Line(0),
len: 4,
}; };
assert_eq!(storage.inner, expected.inner); assert_eq!(storage.inner, expected.inner);
assert_eq!(storage.zero, expected.zero); assert_eq!(storage.zero, expected.zero);
assert_eq!(storage.len, expected.len);
} }
/// Grow the buffer one line at the start of the buffer /// Grow the buffer one line at the start of the buffer
@ -245,6 +241,7 @@ fn grow_before_zero() {
inner: vec!["-", "0", "1"], inner: vec!["-", "0", "1"],
zero: 1, zero: 1,
visible_lines: Line(2), visible_lines: Line(2),
len: 3,
}; };
// Grow buffer // Grow buffer
@ -255,9 +252,11 @@ fn grow_before_zero() {
inner: vec!["-", "-", "0", "1"], inner: vec!["-", "-", "0", "1"],
zero: 2, zero: 2,
visible_lines: Line(0), visible_lines: Line(0),
len: 4,
}; };
assert_eq!(storage.inner, expected.inner); assert_eq!(storage.inner, expected.inner);
assert_eq!(storage.zero, expected.zero); assert_eq!(storage.zero, expected.zero);
assert_eq!(storage.len, expected.len);
} }
/// Shrink the buffer one line at the start of the buffer /// Shrink the buffer one line at the start of the buffer
@ -267,6 +266,7 @@ fn grow_before_zero() {
/// 1: 0 <- Zero /// 1: 0 <- Zero
/// 2: 1 /// 2: 1
/// After: /// After:
/// 0: 2 <- Hidden
/// 0: 0 <- Zero /// 0: 0 <- Zero
/// 1: 1 /// 1: 1
#[test] #[test]
@ -276,6 +276,7 @@ fn shrink_before_zero() {
inner: vec!["2", "0", "1"], inner: vec!["2", "0", "1"],
zero: 1, zero: 1,
visible_lines: Line(2), visible_lines: Line(2),
len: 3,
}; };
// Shrink buffer // Shrink buffer
@ -283,12 +284,14 @@ fn shrink_before_zero() {
// Make sure the result is correct // Make sure the result is correct
let expected = Storage { let expected = Storage {
inner: vec!["0", "1"], inner: vec!["2", "0", "1"],
zero: 0, zero: 1,
visible_lines: Line(0), visible_lines: Line(0),
len: 2,
}; };
assert_eq!(storage.inner, expected.inner); assert_eq!(storage.inner, expected.inner);
assert_eq!(storage.zero, expected.zero); assert_eq!(storage.zero, expected.zero);
assert_eq!(storage.len, expected.len);
} }
/// Shrink the buffer one line at the end of the buffer /// Shrink the buffer one line at the end of the buffer
@ -300,6 +303,7 @@ fn shrink_before_zero() {
/// After: /// After:
/// 0: 0 <- Zero /// 0: 0 <- Zero
/// 1: 1 /// 1: 1
/// 2: 2 <- Hidden
#[test] #[test]
fn shrink_after_zero() { fn shrink_after_zero() {
// Setup storage area // Setup storage area
@ -307,6 +311,7 @@ fn shrink_after_zero() {
inner: vec!["0", "1", "2"], inner: vec!["0", "1", "2"],
zero: 0, zero: 0,
visible_lines: Line(2), visible_lines: Line(2),
len: 3,
}; };
// Shrink buffer // Shrink buffer
@ -314,12 +319,14 @@ fn shrink_after_zero() {
// Make sure the result is correct // Make sure the result is correct
let expected = Storage { let expected = Storage {
inner: vec!["0", "1"], inner: vec!["0", "1", "2"],
zero: 0, zero: 0,
visible_lines: Line(0), visible_lines: Line(0),
len: 2,
}; };
assert_eq!(storage.inner, expected.inner); assert_eq!(storage.inner, expected.inner);
assert_eq!(storage.zero, expected.zero); assert_eq!(storage.zero, expected.zero);
assert_eq!(storage.len, expected.len);
} }
/// Shrink the buffer at the start and end of the buffer /// Shrink the buffer at the start and end of the buffer
@ -332,8 +339,12 @@ fn shrink_after_zero() {
/// 4: 2 /// 4: 2
/// 5: 3 /// 5: 3
/// After: /// After:
/// 0: 0 <- Zero /// 0: 4 <- Hidden
/// 1: 1 /// 1: 5 <- Hidden
/// 2: 0 <- Zero
/// 3: 1
/// 4: 2 <- Hidden
/// 5: 3 <- Hidden
#[test] #[test]
fn shrink_before_and_after_zero() { fn shrink_before_and_after_zero() {
// Setup storage area // Setup storage area
@ -341,6 +352,7 @@ fn shrink_before_and_after_zero() {
inner: vec!["4", "5", "0", "1", "2", "3"], inner: vec!["4", "5", "0", "1", "2", "3"],
zero: 2, zero: 2,
visible_lines: Line(5), visible_lines: Line(5),
len: 6,
}; };
// Shrink buffer // Shrink buffer
@ -348,10 +360,12 @@ fn shrink_before_and_after_zero() {
// Make sure the result is correct // Make sure the result is correct
let expected = Storage { let expected = Storage {
inner: vec!["0", "1"], inner: vec!["4", "5", "0", "1", "2", "3"],
zero: 0, zero: 2,
visible_lines: Line(0), visible_lines: Line(0),
len: 2,
}; };
assert_eq!(storage.inner, expected.inner); assert_eq!(storage.inner, expected.inner);
assert_eq!(storage.zero, expected.zero); assert_eq!(storage.zero, expected.zero);
assert_eq!(storage.len, expected.len);
} }