aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristian Duerr <contact@christianduerr.com>2019-11-22 17:09:01 +0100
committerGitHub <noreply@github.com>2019-11-22 17:09:01 +0100
commit624b3e2189df93145559ebba96e0a7754845c033 (patch)
tree5fb5cbf0fe0cfa0f1a96348ee102f50638cd9970
parent5ea4fb6034fe52bd87a402d1208f58be74486c7a (diff)
downloadalacritty-624b3e2189df93145559ebba96e0a7754845c033.tar.gz
alacritty-624b3e2189df93145559ebba96e0a7754845c033.zip
Fix excessive allocations in URL parser
Fixes #3002.
-rw-r--r--alacritty/src/url.rs154
1 files changed, 105 insertions, 49 deletions
diff --git a/alacritty/src/url.rs b/alacritty/src/url.rs
index 43503f60..5c10bcfd 100644
--- a/alacritty/src/url.rs
+++ b/alacritty/src/url.rs
@@ -9,6 +9,7 @@ use font::Metrics;
use alacritty_terminal::index::Point;
use alacritty_terminal::renderer::rects::{RenderLine, RenderRect};
use alacritty_terminal::term::cell::Flags;
+use alacritty_terminal::term::color::Rgb;
use alacritty_terminal::term::{RenderableCell, RenderableCellContent, SizeInfo};
use crate::config::{Config, RelaxedEq};
@@ -48,6 +49,7 @@ impl Url {
pub struct Urls {
locator: UrlLocator,
urls: Vec<Url>,
+ scheme_buffer: Vec<RenderableCell>,
last_point: Option<Point>,
state: UrlLocation,
}
@@ -56,6 +58,7 @@ impl Default for Urls {
fn default() -> Self {
Self {
locator: UrlLocator::new(),
+ scheme_buffer: Vec::new(),
urls: Vec::new(),
state: UrlLocation::Reset,
last_point: None,
@@ -82,63 +85,41 @@ impl Urls {
};
let point: Point = cell.into();
+ let mut end = point;
// Reset URL when empty cells have been skipped
if point != Point::default() && Some(point.sub(num_cols, 1)) != self.last_point {
self.reset();
}
- self.last_point = Some(point);
+
+ // Extend by one cell for double-width characters
+ if cell.flags.contains(Flags::WIDE_CHAR) {
+ end.col += 1;
+ }
+
+ self.last_point = Some(end);
// Advance parser
let last_state = mem::replace(&mut self.state, self.locator.advance(c));
match (self.state, last_state) {
- (UrlLocation::Url(_length, end_offset), _) => {
- let mut end = point;
-
- // Extend by one cell for double-width characters
- if cell.flags.contains(Flags::WIDE_CHAR) {
- end.col += 1;
-
- self.last_point = Some(end);
+ (UrlLocation::Url(_length, end_offset), UrlLocation::Scheme) => {
+ // Create empty URL
+ self.urls.push(Url { lines: Vec::new(), end_offset, num_cols });
+
+ // Push schemes into URL
+ for scheme_cell in self.scheme_buffer.split_off(0) {
+ let point = scheme_cell.into();
+ self.extend_url(point, point, scheme_cell.fg, end_offset);
}
- if let Some(url) = self.urls.last_mut() {
- let last_index = url.lines.len() - 1;
- let last_line = &mut url.lines[last_index];
-
- if last_line.color == cell.fg {
- // Update existing line
- last_line.end = end;
- } else {
- // Create new line with different color
- url.lines.push(RenderLine { start: point, end, color: cell.fg });
- }
-
- // Update offset
- url.end_offset = end_offset;
- }
- },
- (UrlLocation::Reset, UrlLocation::Scheme) => {
- self.urls.pop();
- },
- (UrlLocation::Scheme, UrlLocation::Reset) => {
- self.urls.push(Url {
- lines: vec![RenderLine { start: point, end: point, color: cell.fg }],
- end_offset: 0,
- num_cols,
- });
+ // Push the new cell into URL
+ self.extend_url(point, end, cell.fg, end_offset);
},
- (UrlLocation::Scheme, _) => {
- if let Some(url) = self.urls.last_mut() {
- if let Some(last_line) = url.lines.last_mut() {
- if last_line.color == cell.fg {
- last_line.end = point;
- } else {
- url.lines.push(RenderLine { start: point, end: point, color: cell.fg });
- }
- }
- }
+ (UrlLocation::Url(_length, end_offset), UrlLocation::Url(..)) => {
+ self.extend_url(point, end, cell.fg, end_offset);
},
+ (UrlLocation::Scheme, _) => self.scheme_buffer.push(cell),
+ (UrlLocation::Reset, _) => self.reset(),
_ => (),
}
@@ -148,6 +129,21 @@ impl Urls {
}
}
+ // Extend the last URL
+ fn extend_url(&mut self, start: Point, end: Point, color: Rgb, end_offset: u16) {
+ let url = self.urls.last_mut().unwrap();
+
+ // If color changed, we need to insert a new line
+ if url.lines.last().map(|last| last.color) == Some(color) {
+ url.lines.last_mut().unwrap().end = end;
+ } else {
+ url.lines.push(RenderLine { color, start, end });
+ }
+
+ // Update excluded cells at the end of the URL
+ url.end_offset = end_offset;
+ }
+
pub fn highlighted(
&self,
config: &Config,
@@ -177,12 +173,72 @@ impl Urls {
}
fn reset(&mut self) {
- // Remove temporarily stored scheme URLs
- if let UrlLocation::Scheme = self.state {
- self.urls.pop();
- }
-
self.locator = UrlLocator::new();
self.state = UrlLocation::Reset;
+ self.scheme_buffer.clear();
+ }
+}
+
+#[cfg(test)]
+mod test {
+ use super::*;
+
+ use alacritty_terminal::index::{Column, Line};
+ use alacritty_terminal::term::cell::MAX_ZEROWIDTH_CHARS;
+
+ fn text_to_cells(text: &str) -> Vec<RenderableCell> {
+ text.chars()
+ .enumerate()
+ .map(|(i, c)| RenderableCell {
+ inner: RenderableCellContent::Chars([c; MAX_ZEROWIDTH_CHARS + 1]),
+ line: Line(0),
+ column: Column(i),
+ fg: Default::default(),
+ bg: Default::default(),
+ bg_alpha: 0.,
+ flags: Flags::empty(),
+ })
+ .collect()
+ }
+
+ #[test]
+ fn multi_color_url() {
+ let mut input = text_to_cells("test https://example.org ing");
+ let num_cols = input.len();
+
+ input[10].fg = Rgb { r: 0xff, g: 0x00, b: 0xff };
+
+ let mut urls = Urls::new();
+
+ for cell in input {
+ urls.update(num_cols, cell);
+ }
+
+ let url = urls.urls.first().unwrap();
+ assert_eq!(url.start().col, Column(5));
+ assert_eq!(url.end().col, Column(23));
+ }
+
+ #[test]
+ fn multiple_urls() {
+ let input = text_to_cells("test git:a git:b git:c ing");
+ let num_cols = input.len();
+
+ let mut urls = Urls::new();
+
+ for cell in input {
+ urls.update(num_cols, cell);
+ }
+
+ assert_eq!(urls.urls.len(), 3);
+
+ assert_eq!(urls.urls[0].start().col, Column(5));
+ assert_eq!(urls.urls[0].end().col, Column(9));
+
+ assert_eq!(urls.urls[1].start().col, Column(11));
+ assert_eq!(urls.urls[1].end().col, Column(15));
+
+ assert_eq!(urls.urls[2].start().col, Column(17));
+ assert_eq!(urls.urls[2].end().col, Column(21));
}
}