diff options
author | Titus Wormer <tituswormer@gmail.com> | 2022-07-20 17:19:17 +0200 |
---|---|---|
committer | Titus Wormer <tituswormer@gmail.com> | 2022-07-20 17:19:17 +0200 |
commit | a820d849c3e20a1d72137072d70a7c8e00306f98 (patch) | |
tree | c2916ab31d6d481e0b53a06aa9b95dfcddd4163f /src/compiler.rs | |
parent | 7894ec75a7070591c3499fce1f409563c4edc7d7 (diff) | |
download | markdown-rs-a820d849c3e20a1d72137072d70a7c8e00306f98.tar.gz markdown-rs-a820d849c3e20a1d72137072d70a7c8e00306f98.tar.bz2 markdown-rs-a820d849c3e20a1d72137072d70a7c8e00306f98.zip |
Refactor to improve allocation around strings
Diffstat (limited to 'src/compiler.rs')
-rw-r--r-- | src/compiler.rs | 174 |
1 files changed, 80 insertions, 94 deletions
diff --git a/src/compiler.rs b/src/compiler.rs index c79abed..1723190 100644 --- a/src/compiler.rs +++ b/src/compiler.rs @@ -84,7 +84,7 @@ struct CompileContext<'a> { pub line_ending_default: LineEnding, pub allow_dangerous_html: bool, /// Intermediate results. - pub buffers: Vec<Vec<String>>, + pub buffers: Vec<String>, pub index: usize, } @@ -125,78 +125,62 @@ impl<'a> CompileContext<'a> { }, line_ending_default: line_ending, allow_dangerous_html: options.allow_dangerous_html, - buffers: vec![vec![]], + buffers: vec![String::new()], index: 0, } } /// Push a buffer. pub fn buffer(&mut self) { - self.buffers.push(vec![]); + self.buffers.push(String::new()); } /// Pop a buffer, returning its value. pub fn resume(&mut self) -> String { - self.buffers - .pop() - .expect("Cannot resume w/o buffer") - .concat() + self.buffers.pop().expect("Cannot resume w/o buffer") } - pub fn push(&mut self, value: String) { + pub fn push<'x, S: Into<&'x str>>(&mut self, value: S) { + let value = value.into(); self.buffers .last_mut() .expect("Cannot push w/o buffer") - .push(value); + .push_str(value); self.last_was_tag = false; } - pub fn tag(&mut self, value: String) { - if self.tags { - self.buffers - .last_mut() - .expect("Cannot push w/o buffer") - .push(value); - self.last_was_tag = true; + pub fn push_raw<'x, S: Into<&'x str>>(&mut self, value: S) { + let value = value.into(); + if self.ignore_encode { + self.push(value); + } else { + self.push(&*encode(value)); } } - /// Get the last chunk of current buffer. - pub fn buf_tail_slice(&self) -> Option<&String> { - self.buf_tail().last() + pub fn tag<'x, S: Into<&'x str>>(&mut self, value: S) { + if self.tags { + self.push(value.into()); + self.last_was_tag = true; + } } /// Get the current buffer. - pub fn buf_tail(&self) -> &Vec<String> { + pub fn buf_tail(&self) -> &String { self.buffers .last() .expect("at least one buffer should exist") } - /// Optionally encode. - pub fn encode_opt(&self, value: &str) -> String { - if self.ignore_encode { - value.to_string() - } else { - encode(value) - } - } - /// Add a line ending. pub fn line_ending(&mut self) { - let line_ending = self.line_ending_default.as_str().to_string(); - // lastWasTag = false - self.push(line_ending); + let eol = self.line_ending_default.as_str().to_string(); + self.push(&*eol); } /// Add a line ending if needed (as in, there’s no eol/eof already). pub fn line_ending_if_needed(&mut self) { - let slice = self.buf_tail_slice(); - let last_char = if let Some(x) = slice { - x.chars().last() - } else { - None - }; + let last_char = self.buf_tail().chars().last(); let mut add = true; if let Some(x) = last_char { @@ -314,7 +298,7 @@ pub fn compile(events: &[Event], codes: &[Code], options: &Options) -> String { .buffers .get(0) .expect("expected 1 final buffer") - .concat() + .to_string() } /// Handle [`Enter`][EventType::Enter]. @@ -415,14 +399,14 @@ fn on_enter_buffer(context: &mut CompileContext) { fn on_enter_block_quote(context: &mut CompileContext) { context.tight_stack.push(false); context.line_ending_if_needed(); - context.tag("<blockquote>".to_string()); + context.tag("<blockquote>"); } /// Handle [`Enter`][EventType::Enter]:[`CodeIndented`][Token::CodeIndented]. fn on_enter_code_indented(context: &mut CompileContext) { context.code_flow_seen_data = Some(false); context.line_ending_if_needed(); - context.tag("<pre><code>".to_string()); + context.tag("<pre><code>"); } /// Handle [`Enter`][EventType::Enter]:[`CodeFenced`][Token::CodeFenced]. @@ -430,14 +414,14 @@ fn on_enter_code_fenced(context: &mut CompileContext) { context.code_flow_seen_data = Some(false); context.line_ending_if_needed(); // Note that no `>` is used, which is added later. - context.tag("<pre><code".to_string()); + context.tag("<pre><code"); context.code_fenced_fences_count = Some(0); } /// Handle [`Enter`][EventType::Enter]:[`CodeText`][Token::CodeText]. fn on_enter_code_text(context: &mut CompileContext) { context.code_text_inside = true; - context.tag("<code>".to_string()); + context.tag("<code>"); context.buffer(); } @@ -462,7 +446,7 @@ fn on_enter_definition_destination_string(context: &mut CompileContext) { /// Handle [`Enter`][EventType::Enter]:[`Emphasis`][Token::Emphasis]. fn on_enter_emphasis(context: &mut CompileContext) { - context.tag("<em>".to_string()); + context.tag("<em>"); } /// Handle [`Enter`][EventType::Enter]:[`HtmlFlow`][Token::HtmlFlow]. @@ -563,7 +547,7 @@ fn on_enter_list(context: &mut CompileContext) { context.tight_stack.push(!loose); context.line_ending_if_needed(); // Note: no `>`. - context.tag(format!( + context.tag(&*format!( "<{}", if *token_type == Token::ListOrdered { "ol" @@ -579,11 +563,11 @@ fn on_enter_list_item_marker(context: &mut CompileContext) { let expect_first_item = context.expect_first_item.take().unwrap(); if expect_first_item { - context.tag(">".to_string()); + context.tag(">"); } context.line_ending_if_needed(); - context.tag("<li>".to_string()); + context.tag("<li>"); context.expect_first_item = Some(false); // “Hack” to prevent a line ending from showing up if the item is empty. context.last_was_tag = false; @@ -595,7 +579,7 @@ fn on_enter_paragraph(context: &mut CompileContext) { if !tight { context.line_ending_if_needed(); - context.tag("<p>".to_string()); + context.tag("<p>"); } } @@ -616,7 +600,7 @@ fn on_enter_resource_destination_string(context: &mut CompileContext) { /// Handle [`Enter`][EventType::Enter]:[`Strong`][Token::Strong]. fn on_enter_strong(context: &mut CompileContext) { - context.tag("<strong>".to_string()); + context.tag("<strong>"); } /// Handle [`Exit`][EventType::Exit]:[`AutolinkEmail`][Token::AutolinkEmail]. @@ -626,15 +610,15 @@ fn on_exit_autolink_email(context: &mut CompileContext) { &from_exit_event(context.events, context.index), false, ); - context.tag(format!( + context.tag(&*format!( "<a href=\"{}\">", sanitize_uri( format!("mailto:{}", slice.as_str()).as_str(), &context.protocol_href ) )); - context.push(context.encode_opt(&slice)); - context.tag("</a>".to_string()); + context.push_raw(&*slice); + context.tag("</a>"); } /// Handle [`Exit`][EventType::Exit]:[`AutolinkProtocol`][Token::AutolinkProtocol]. @@ -644,17 +628,17 @@ fn on_exit_autolink_protocol(context: &mut CompileContext) { &from_exit_event(context.events, context.index), false, ); - context.tag(format!( + context.tag(&*format!( "<a href=\"{}\">", sanitize_uri(slice.as_str(), &context.protocol_href) )); - context.push(context.encode_opt(&slice)); - context.tag("</a>".to_string()); + context.push_raw(&*slice); + context.tag("</a>"); } /// Handle [`Exit`][EventType::Exit]:{[`HardBreakEscape`][Token::HardBreakEscape],[`HardBreakTrailing`][Token::HardBreakTrailing]}. fn on_exit_break(context: &mut CompileContext) { - context.tag("<br />".to_string()); + context.tag("<br />"); } /// Handle [`Exit`][EventType::Exit]:[`BlankLineEnding`][Token::BlankLineEnding]. @@ -669,7 +653,7 @@ fn on_exit_block_quote(context: &mut CompileContext) { context.tight_stack.pop(); context.line_ending_if_needed(); context.slurp_one_line_ending = false; - context.tag("</blockquote>".to_string()); + context.tag("</blockquote>"); } /// Handle [`Exit`][EventType::Exit]:[`CharacterReferenceMarker`][Token::CharacterReferenceMarker]. @@ -705,17 +689,17 @@ fn on_exit_character_reference_value(context: &mut CompileContext) { CharacterReferenceKind::Named => decode_named(ref_string), }; - context.push(context.encode_opt(&value)); + context.push_raw(&*value); } /// Handle [`Exit`][EventType::Exit]:[`CodeFlowChunk`][Token::CodeFlowChunk]. fn on_exit_code_flow_chunk(context: &mut CompileContext) { context.code_flow_seen_data = Some(true); - context.push(context.encode_opt(&serialize( + context.push_raw(&*serialize( context.codes, &from_exit_event(context.events, context.index), false, - ))); + )); } /// Handle [`Exit`][EventType::Exit]:[`CodeFencedFence`][Token::CodeFencedFence]. @@ -727,7 +711,7 @@ fn on_exit_code_fenced_fence(context: &mut CompileContext) { }; if count == 0 { - context.tag(">".to_string()); + context.tag(">"); context.slurp_one_line_ending = true; } @@ -737,7 +721,7 @@ fn on_exit_code_fenced_fence(context: &mut CompileContext) { /// Handle [`Exit`][EventType::Exit]:[`CodeFencedFenceInfo`][Token::CodeFencedFenceInfo]. fn on_exit_code_fenced_fence_info(context: &mut CompileContext) { let value = context.resume(); - context.tag(format!(" class=\"language-{}\"", value)); + context.tag(&*format!(" class=\"language-{}\"", value)); } /// Handle [`Exit`][EventType::Exit]:{[`CodeFenced`][Token::CodeFenced],[`CodeIndented`][Token::CodeIndented]}. @@ -764,7 +748,7 @@ fn on_exit_code_flow(context: &mut CompileContext) { context.line_ending_if_needed(); } - context.tag("</code></pre>".to_string()); + context.tag("</code></pre>"); if let Some(count) = context.code_fenced_fences_count.take() { if count < 2 { @@ -792,12 +776,12 @@ fn on_exit_code_text(context: &mut CompileContext) { } context.code_text_inside = false; - context.push(if trim { + context.push(&*if trim { result[1..(result.len() - 1)].to_string() } else { result }); - context.tag("</code>".to_string()); + context.tag("</code>"); } /// Handle [`Exit`][EventType::Exit]:*. @@ -810,11 +794,11 @@ fn on_exit_drop(context: &mut CompileContext) { /// Handle [`Exit`][EventType::Exit]:{[`CodeTextData`][Token::CodeTextData],[`Data`][Token::Data],[`CharacterEscapeValue`][Token::CharacterEscapeValue]}. fn on_exit_data(context: &mut CompileContext) { // Just output it. - context.push(context.encode_opt(&serialize( + context.push_raw(&*serialize( context.codes, &from_exit_event(context.events, context.index), false, - ))); + )); } /// Handle [`Exit`][EventType::Exit]:[`Definition`][Token::Definition]. @@ -870,7 +854,7 @@ fn on_exit_definition_title_string(context: &mut CompileContext) { /// Handle [`Exit`][EventType::Exit]:[`Strong`][Token::Emphasis]. fn on_exit_emphasis(context: &mut CompileContext) { - context.tag("</em>".to_string()); + context.tag("</em>"); } /// Handle [`Exit`][EventType::Exit]:[`HeadingAtx`][Token::HeadingAtx]. @@ -880,7 +864,7 @@ fn on_exit_heading_atx(context: &mut CompileContext) { .take() .expect("`atx_opening_sequence_size` must be set in headings"); - context.tag(format!("</h{}>", rank)); + context.tag(&*format!("</h{}>", rank)); } /// Handle [`Exit`][EventType::Exit]:[`HeadingAtxSequence`][Token::HeadingAtxSequence]. @@ -895,14 +879,14 @@ fn on_exit_heading_atx_sequence(context: &mut CompileContext) { .len(); context.line_ending_if_needed(); context.atx_opening_sequence_size = Some(rank); - context.tag(format!("<h{}>", rank)); + context.tag(&*format!("<h{}>", rank)); } } /// Handle [`Exit`][EventType::Exit]:[`HeadingAtxText`][Token::HeadingAtxText]. fn on_exit_heading_atx_text(context: &mut CompileContext) { let value = context.resume(); - context.push(value); + context.push(&*value); } /// Handle [`Exit`][EventType::Exit]:[`HeadingSetextText`][Token::HeadingSetextText]. @@ -925,9 +909,9 @@ fn on_exit_heading_setext_underline(context: &mut CompileContext) { let level: usize = if head == Code::Char('-') { 2 } else { 1 }; context.line_ending_if_needed(); - context.tag(format!("<h{}>", level)); - context.push(text); - context.tag(format!("</h{}>", level)); + context.tag(&*format!("<h{}>", level)); + context.push(&*text); + context.tag(&*format!("</h{}>", level)); } /// Handle [`Exit`][EventType::Exit]:{[`HtmlFlow`][Token::HtmlFlow],[`HtmlText`][Token::HtmlText]}. @@ -942,7 +926,7 @@ fn on_exit_html_data(context: &mut CompileContext) { &from_exit_event(context.events, context.index), false, ); - context.push(context.encode_opt(&slice)); + context.push_raw(&*slice); } /// Handle [`Exit`][EventType::Exit]:[`Label`][Token::Label]. @@ -965,15 +949,15 @@ fn on_exit_label_text(context: &mut CompileContext) { /// Handle [`Exit`][EventType::Exit]:[`LineEnding`][Token::LineEnding]. fn on_exit_line_ending(context: &mut CompileContext) { if context.code_text_inside { - context.push(" ".to_string()); + context.push(" "); } else if context.slurp_one_line_ending { context.slurp_one_line_ending = false; } else { - context.push(context.encode_opt(&serialize( + context.push_raw(&*serialize( context.codes, &from_exit_event(context.events, context.index), false, - ))); + )); } } @@ -986,7 +970,7 @@ fn on_exit_list(context: &mut CompileContext) { }; context.tight_stack.pop(); context.line_ending(); - context.tag(format!("</{}>", tag_name)); + context.tag(&*format!("</{}>", tag_name)); } /// Handle [`Exit`][EventType::Exit]:[`ListItem`][Token::ListItem]. @@ -1012,7 +996,7 @@ fn on_exit_list_item(context: &mut CompileContext) { context.line_ending_if_needed(); } - context.tag("</li>".to_string()); + context.tag("</li>"); } /// Handle [`Exit`][EventType::Exit]:[`ListItemValue`][Token::ListItemValue]. @@ -1028,7 +1012,9 @@ fn on_exit_list_item_value(context: &mut CompileContext) { let value = slice.parse::<u32>().ok().unwrap(); if value != 1 { - context.tag(format!(" start=\"{}\"", encode(&value.to_string()))); + context.tag(" start=\""); + context.tag(&*value.to_string()); + context.tag("\""); } } } @@ -1082,9 +1068,9 @@ fn on_exit_media(context: &mut CompileContext) { }; let destination = if let Some(destination) = destination { - destination.clone() + destination } else { - "".to_string() + "" }; let title = if let Some(title) = title { @@ -1094,20 +1080,20 @@ fn on_exit_media(context: &mut CompileContext) { }; if media.image { - context.tag(format!( + context.tag(&*format!( "<img src=\"{}\" alt=\"", - sanitize_uri(&destination, &context.protocol_src), + sanitize_uri(destination, &context.protocol_src), )); - context.push(label); - context.tag(format!("\"{} />", title)); + context.push(&*label); + context.tag(&*format!("\"{} />", title)); } else { - context.tag(format!( + context.tag(&*format!( "<a href=\"{}\"{}>", - sanitize_uri(&destination, &context.protocol_href), + sanitize_uri(destination, &context.protocol_href), title, )); - context.push(label); - context.tag("</a>".to_string()); + context.push(&*label); + context.tag("</a>"); }; } @@ -1118,7 +1104,7 @@ fn on_exit_paragraph(context: &mut CompileContext) { if *tight { context.slurp_one_line_ending = true; } else { - context.tag("</p>".to_string()); + context.tag("</p>"); } } @@ -1151,11 +1137,11 @@ fn on_exit_resource_title_string(context: &mut CompileContext) { /// Handle [`Exit`][EventType::Exit]:[`Strong`][Token::Strong]. fn on_exit_strong(context: &mut CompileContext) { - context.tag("</strong>".to_string()); + context.tag("</strong>"); } /// Handle [`Exit`][EventType::Exit]:[`ThematicBreak`][Token::ThematicBreak]. fn on_exit_thematic_break(context: &mut CompileContext) { context.line_ending_if_needed(); - context.tag("<hr />".to_string()); + context.tag("<hr />"); } |