diff --git a/src/history/file.rs b/src/history/file.rs index 50ad3c4d3..6127a42df 100644 --- a/src/history/file.rs +++ b/src/history/file.rs @@ -270,15 +270,13 @@ fn maybe_unescape_yaml_fish_2_0(s: &[u8]) -> Cow<[u8]> { // // Benchmarks and code: https://github.com/mqudsi/fish-yaml-unescape-benchmark fn unescape_yaml_fish_2_0(s: &[u8]) -> Vec { - // Safety: we never read from this box; we only write to it then read what we've written. - // Rationale: This function is in a very hot loop and this benchmarks around 8% faster on - // real-world escaped yaml samples from the fish history file. - // + // This function is in a very hot loop and the usage of boxed uninit memory benchmarks around 8% + // faster on real-world escaped yaml samples from the fish history file. + // This is a very long way around of writing `Box::new_uninit_slice(s.len())`, which // requires the unstablized nightly-only feature new_unit (#63291). It optimizes away. - let mut result: Box<[u8]> = std::iter::repeat_with(std::mem::MaybeUninit::uninit) + let mut result: Box<[_]> = std::iter::repeat_with(std::mem::MaybeUninit::uninit) .take(s.len()) - .map(|b| unsafe { b.assume_init() }) .collect(); let mut chars = s.iter().copied(); let mut src_idx = 0; @@ -289,21 +287,26 @@ fn unescape_yaml_fish_2_0(s: &[u8]) -> Vec { // everywhere does not result in a statistically significant improvement to the // performance of this function. let to_copy = chars.by_ref().take_while(|b| *b != b'\\').count(); - result[dst_idx..][..to_copy].copy_from_slice(&s[src_idx..][..to_copy]); + unsafe { + let src = &s[src_idx..].as_ptr(); + // Can use the following when feature(maybe_uninit_slice) is stabilized: + // let dst = std::mem::MaybeUninit::slice_as_mut_ptr(&mut result[dst_idx..]); + let dst = result[dst_idx..].as_mut_ptr().cast(); + std::ptr::copy_nonoverlapping(*src, dst, to_copy); + } dst_idx += to_copy; match chars.next() { - Some(b'\\') => result[dst_idx] = b'\\', - Some(b'n') => result[dst_idx] = b'\n', + Some(b'\\') => result[dst_idx].write(b'\\'), + Some(b'n') => result[dst_idx].write(b'\n'), _ => break, - } + }; src_idx += to_copy + 2; dst_idx += 1; } - let mut result = Vec::from(result); - result.truncate(dst_idx); - result + let result = Box::leak(result); + unsafe { Vec::from_raw_parts(result.as_mut_ptr().cast(), dst_idx, result.len()) } } /// Read one line, stripping off any newline, returning the number of bytes consumed.