]> git.lizzy.rs Git - rust.git/commitdiff
Avoid allocation in ast::String::value if the string needs no unescaping
authorLukas Wirth <lukastw97@gmail.com>
Thu, 26 Nov 2020 22:08:02 +0000 (23:08 +0100)
committerLukas Wirth <lukastw97@gmail.com>
Fri, 27 Nov 2020 11:03:58 +0000 (12:03 +0100)
crates/syntax/src/ast/token_ext.rs

index ac03264205a087ebd9cfd4df92d7c8e0dcbff3dd..b985861f228640fe9124891864263342c82cdf70 100644 (file)
@@ -130,19 +130,28 @@ pub fn value(&self) -> Option<Cow<'_, str>> {
         let text = self.text().as_str();
         let text = &text[self.text_range_between_quotes()? - self.syntax().text_range().start()];
 
-        let mut buf = String::with_capacity(text.len());
+        let mut buf = String::new();
+        let mut text_iter = text.chars();
         let mut has_error = false;
-        unescape_literal(text, Mode::Str, &mut |_, unescaped_char| match unescaped_char {
-            Ok(c) => buf.push(c),
-            Err(_) => has_error = true,
+        unescape_literal(text, Mode::Str, &mut |char_range, unescaped_char| match (
+            unescaped_char,
+            buf.capacity() == 0,
+        ) {
+            (Ok(c), false) => buf.push(c),
+            (Ok(c), true) if Some(c) == text_iter.next() => (),
+            (Ok(c), true) => {
+                buf.reserve_exact(text.len());
+                buf.push_str(&text[..char_range.start]);
+                buf.push(c);
+            }
+            (Err(_), _) => has_error = true,
         });
 
-        if has_error {
-            return None;
+        match (has_error, buf.capacity() == 0) {
+            (true, _) => None,
+            (false, true) => Some(Cow::Borrowed(text)),
+            (false, false) => Some(Cow::Owned(buf)),
         }
-        // FIXME: don't actually allocate for borrowed case
-        let res = if buf == text { Cow::Borrowed(text) } else { Cow::Owned(buf) };
-        Some(res)
     }
 
     pub fn quote_offsets(&self) -> Option<QuoteOffsets> {