From 51275525c1c566cd60300f5a3d67ae097c81d404 Mon Sep 17 00:00:00 2001 From: Mahmoud Al-Qudsi Date: Wed, 15 May 2024 22:35:07 -0500 Subject: [PATCH] Speed up extract_prefix_and_unescape_yaml On the completions and history thread, the parent function HistoryFileContents::decode_item() is responsible for ~60% of the CPU time, and extract_prefix_and_unescape_yaml() alone comprising 14% (of the total). This change removes allocations in the event that the history item is either fully or partially plain yaml with no escapes to begin with, and brings down the execution time of this function to only 7% of the total execution time. The bulk of the remaining time is spent in wcs2string(), which is called unconditionally and is naturally alloc-heavy. --- src/history/file.rs | 28 ++++++++++++++++++++-------- 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/src/history/file.rs b/src/history/file.rs index 2837a4017..25bb391f7 100644 --- a/src/history/file.rs +++ b/src/history/file.rs @@ -1,6 +1,7 @@ //! Implemention of history files. use std::{ + borrow::Cow, fs::File, io::{Read, Seek, SeekFrom, Write}, ops::{Deref, DerefMut}, @@ -312,18 +313,29 @@ fn trim_leading_spaces(s: &[u8]) -> (usize, &[u8]) { (count, &s[count..]) } -fn extract_prefix_and_unescape_yaml(line: &[u8]) -> Option<(Vec, Vec)> { +fn extract_prefix_and_unescape_yaml(line: &[u8]) -> Option<(Cow<[u8]>, Cow<[u8]>)> { let mut split = line.splitn(2, |c| *c == b':'); let key = split.next().unwrap(); let value = split.next()?; - assert!(split.next().is_none()); - let mut key = key.to_owned(); + let key: Cow<[u8]> = if key.iter().copied().any(|b| b == b'\\') { + let mut key = key.to_owned(); + unescape_yaml_fish_2_0(&mut key); + key.into() + } else { + key.into() + }; + // Skip a space after the : if necessary. - let mut value = trim_start(value).to_owned(); + let value = trim_start(value); + let value: Cow<[u8]> = if value.iter().copied().any(|b| b == b'\\') { + let mut value = value.to_owned(); + unescape_yaml_fish_2_0(&mut value); + value.into() + } else { + value.into() + }; - unescape_yaml_fish_2_0(&mut key); - unescape_yaml_fish_2_0(&mut value); Some((key, value)) } @@ -361,7 +373,7 @@ fn decode_item_fish_2_0(mut data: &[u8]) -> Option { // We are definitely going to consume this line. data = &data[advance..]; - if key == b"when" { + if *key == *b"when" { // Parse an int from the timestamp. Should this fail, 0 is acceptable. when = time_from_seconds( std::str::from_utf8(&value) @@ -369,7 +381,7 @@ fn decode_item_fish_2_0(mut data: &[u8]) -> Option { .and_then(|s| s.parse().ok()) .unwrap_or(0), ); - } else if key == b"paths" { + } else if *key == *b"paths" { // Read lines starting with " - " until we can't read any more. loop { let (advance, line) = read_line(data);