Skip to content

Commit 7953c16

Browse files
authored
Avoid allocating a Vec for every token during rendering (#760)
1 parent f3adf8a commit 7953c16

File tree

3 files changed

+65
-62
lines changed

3 files changed

+65
-62
lines changed

librubyfmt/src/line_tokens.rs

Lines changed: 33 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -251,65 +251,64 @@ pub enum AbstractLineToken<'src> {
251251
}
252252

253253
impl<'src> AbstractLineToken<'src> {
254-
pub fn into_single_line(self) -> Vec<ConcreteLineTokenAndTargets<'src>> {
254+
pub fn write_single_line(self, out: &mut Vec<ConcreteLineTokenAndTargets<'src>>) {
255255
match self {
256256
Self::CollapsingNewLine(heredoc_strings) => {
257-
let mut res = Vec::new();
258257
if heredoc_strings.is_some() {
259-
res.push(cltats_hard_newline());
258+
out.push(cltats_hard_newline());
260259
}
261-
res.extend(Self::shimmy_and_shake_heredocs(heredoc_strings));
262-
res
260+
Self::write_heredocs(heredoc_strings, out);
263261
}
264262
Self::SoftNewline(heredoc_strings) => {
265-
let mut res = vec![ConcreteLineTokenAndTargets::ConcreteLineToken(
263+
out.push(ConcreteLineTokenAndTargets::ConcreteLineToken(
266264
ConcreteLineToken::Space,
267-
)];
268-
res.extend(Self::shimmy_and_shake_heredocs(heredoc_strings));
269-
res
265+
));
266+
Self::write_heredocs(heredoc_strings, out);
270267
}
271-
Self::SoftIndent { .. } => Vec::new(),
268+
Self::SoftIndent { .. } => {}
272269
Self::ConcreteLineToken(clt) => {
273-
vec![ConcreteLineTokenAndTargets::ConcreteLineToken(clt)]
270+
out.push(ConcreteLineTokenAndTargets::ConcreteLineToken(clt));
271+
}
272+
Self::BreakableEntry(be) => {
273+
out.push(ConcreteLineTokenAndTargets::BreakableEntry(be));
274274
}
275-
Self::BreakableEntry(be) => vec![ConcreteLineTokenAndTargets::BreakableEntry(be)],
276275
Self::BreakableCallChainEntry(bcce) => {
277-
vec![ConcreteLineTokenAndTargets::BreakableCallChainEntry(bcce)]
276+
out.push(ConcreteLineTokenAndTargets::BreakableCallChainEntry(bcce));
278277
}
279278
}
280279
}
281280

282-
pub fn into_multi_line(self) -> Vec<ConcreteLineTokenAndTargets<'src>> {
281+
pub fn write_multi_line(self, out: &mut Vec<ConcreteLineTokenAndTargets<'src>>) {
283282
match self {
284283
Self::CollapsingNewLine(heredoc_strings) => {
285-
let mut res = vec![cltats_hard_newline()];
286-
res.extend(Self::shimmy_and_shake_heredocs(heredoc_strings));
287-
res
284+
out.push(cltats_hard_newline());
285+
Self::write_heredocs(heredoc_strings, out);
288286
}
289287
Self::SoftNewline(heredoc_strings) => {
290-
let mut res = vec![cltats_hard_newline()];
291-
res.extend(Self::shimmy_and_shake_heredocs(heredoc_strings));
292-
res
288+
out.push(cltats_hard_newline());
289+
Self::write_heredocs(heredoc_strings, out);
293290
}
294291
Self::SoftIndent { depth } => {
295-
vec![ConcreteLineTokenAndTargets::ConcreteLineToken(
292+
out.push(ConcreteLineTokenAndTargets::ConcreteLineToken(
296293
ConcreteLineToken::Indent { depth },
297-
)]
294+
));
298295
}
299296
Self::ConcreteLineToken(clt) => {
300-
vec![ConcreteLineTokenAndTargets::ConcreteLineToken(clt)]
297+
out.push(ConcreteLineTokenAndTargets::ConcreteLineToken(clt));
298+
}
299+
Self::BreakableEntry(be) => {
300+
out.push(ConcreteLineTokenAndTargets::BreakableEntry(be));
301301
}
302-
Self::BreakableEntry(be) => vec![ConcreteLineTokenAndTargets::BreakableEntry(be)],
303302
Self::BreakableCallChainEntry(bcce) => {
304-
vec![ConcreteLineTokenAndTargets::BreakableCallChainEntry(bcce)]
303+
out.push(ConcreteLineTokenAndTargets::BreakableCallChainEntry(bcce));
305304
}
306305
}
307306
}
308307

309-
fn shimmy_and_shake_heredocs(
308+
fn write_heredocs(
310309
heredoc_strings: Option<Vec<HeredocString>>,
311-
) -> Vec<ConcreteLineTokenAndTargets<'src>> {
312-
let mut res = vec![];
310+
out: &mut Vec<ConcreteLineTokenAndTargets<'src>>,
311+
) {
313312
if let Some(values) = heredoc_strings {
314313
for hds in values {
315314
let indent = hds.indent;
@@ -318,19 +317,18 @@ impl<'src> AbstractLineToken<'src> {
318317

319318
let s = hds.render_as_string();
320319
if !s.is_empty() {
321-
res.push(clats_direct_part(s));
322-
res.push(cltats_hard_newline());
320+
out.push(clats_direct_part(s));
321+
out.push(cltats_hard_newline());
323322
}
324323
if !kind.is_bare() {
325-
res.push(clats_indent(indent));
324+
out.push(clats_indent(indent));
326325
}
327-
res.push(clats_heredoc_close(symbol));
328-
res.push(cltats_hard_newline());
326+
out.push(clats_heredoc_close(symbol));
327+
out.push(cltats_hard_newline());
329328
let indent_depth = if indent != 0 { indent - 2 } else { indent };
330-
res.push(clats_indent(indent_depth));
329+
out.push(clats_indent(indent_depth));
331330
}
332331
}
333-
res
334332
}
335333

336334
pub fn is_comment(&self) -> bool {

librubyfmt/src/render_queue_writer.rs

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -230,12 +230,15 @@ impl<'src> RenderQueueWriter<'src> {
230230
let force_single_line =
231231
!be.any_collapsing_newline_has_heredoc_content() && be.in_string_embexpr();
232232

233+
let mut tokens = Vec::new();
233234
if !force_single_line
234235
&& (be.is_multiline() || Self::renders_over_max_line_length(accum, &be))
235236
{
236-
Self::render_as(accum, be.into_tokens(ConvertType::MultiLine));
237+
be.write_tokens(ConvertType::MultiLine, &mut tokens);
238+
Self::render_as(accum, tokens);
237239
} else {
238-
Self::render_as(accum, be.into_tokens(ConvertType::SingleLine));
240+
be.write_tokens(ConvertType::SingleLine, &mut tokens);
241+
Self::render_as(accum, tokens);
239242
// after running accum looks like this (or some variant):
240243
// [.., Comma, Space, DirectPart {part: ""}, <close_delimiter>]
241244
// so we remove items at positions length-2 until there is nothing
@@ -250,15 +253,17 @@ impl<'src> RenderQueueWriter<'src> {
250253
) {
251254
let must_multiline =
252255
bcce.any_collapsing_newline_has_heredoc_content() && bcce.in_string_embexpr();
256+
let mut tokens = Vec::new();
253257
if must_multiline
254258
|| ((bcce.is_multiline() || Self::renders_over_max_line_length(accum, &bcce))
255259
&& !bcce.in_string_embexpr())
256260
{
257-
let tokens = bcce.into_tokens(ConvertType::MultiLine);
261+
bcce.write_tokens(ConvertType::MultiLine, &mut tokens);
258262
Self::render_as(accum, tokens);
259263
} else {
260264
bcce.remove_call_chain_magic_tokens();
261-
Self::render_as(accum, bcce.into_tokens(ConvertType::SingleLine));
265+
bcce.write_tokens(ConvertType::SingleLine, &mut tokens);
266+
Self::render_as(accum, tokens);
262267
}
263268
}
264269

librubyfmt/src/render_targets.rs

Lines changed: 23 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ impl<'src> BaseQueue<'src> {
4949

5050
pub trait AbstractTokenTarget<'src>: std::fmt::Debug {
5151
fn push(&mut self, lt: AbstractLineToken<'src>);
52-
fn into_tokens(self, ct: ConvertType) -> Vec<ConcreteLineTokenAndTargets<'src>>;
52+
fn write_tokens(self, ct: ConvertType, out: &mut Vec<ConcreteLineTokenAndTargets<'src>>);
5353
fn is_multiline(&self) -> bool;
5454
fn single_line_string_length(&self, current_line_length: usize) -> usize;
5555
fn tokens(&self) -> &Vec<AbstractLineToken<'src>>;
@@ -96,21 +96,21 @@ impl<'src> AbstractTokenTarget<'src> for BreakableEntry<'src> {
9696
self.tokens.push(lt);
9797
}
9898

99-
fn into_tokens(self, ct: ConvertType) -> Vec<ConcreteLineTokenAndTargets<'src>> {
99+
fn write_tokens(self, ct: ConvertType, out: &mut Vec<ConcreteLineTokenAndTargets<'src>>) {
100100
match ct {
101101
ConvertType::MultiLine => {
102-
let mut new_tokens: Vec<_> = Vec::with_capacity(self.tokens.len() + 2);
103-
new_tokens.push(self.delims.multi_line_open().into());
104-
new_tokens.extend(self.tokens.into_iter().flat_map(|t| t.into_multi_line()));
105-
new_tokens.push(self.delims.multi_line_close().into());
106-
new_tokens
102+
out.push(self.delims.multi_line_open().into());
103+
for t in self.tokens {
104+
t.write_multi_line(out);
105+
}
106+
out.push(self.delims.multi_line_close().into());
107107
}
108108
ConvertType::SingleLine => {
109-
let mut new_tokens: Vec<_> = Vec::with_capacity(self.tokens.len() + 2);
110-
new_tokens.push(self.delims.single_line_open().into());
111-
new_tokens.extend(self.tokens.into_iter().flat_map(|t| t.into_single_line()));
112-
new_tokens.push(self.delims.single_line_close().into());
113-
new_tokens
109+
out.push(self.delims.single_line_open().into());
110+
for t in self.tokens {
111+
t.write_single_line(out);
112+
}
113+
out.push(self.delims.single_line_close().into());
114114
}
115115
}
116116
}
@@ -228,18 +228,18 @@ impl<'src> AbstractTokenTarget<'src> for BreakableCallChainEntry<'src> {
228228
self.tokens.push(lt);
229229
}
230230

231-
fn into_tokens(self, ct: ConvertType) -> Vec<ConcreteLineTokenAndTargets<'src>> {
231+
fn write_tokens(self, ct: ConvertType, out: &mut Vec<ConcreteLineTokenAndTargets<'src>>) {
232232
match ct {
233-
ConvertType::MultiLine => self
234-
.tokens
235-
.into_iter()
236-
.flat_map(|t| t.into_multi_line())
237-
.collect(),
238-
ConvertType::SingleLine => self
239-
.tokens
240-
.into_iter()
241-
.flat_map(|t| t.into_single_line())
242-
.collect(),
233+
ConvertType::MultiLine => {
234+
for t in self.tokens {
235+
t.write_multi_line(out);
236+
}
237+
}
238+
ConvertType::SingleLine => {
239+
for t in self.tokens {
240+
t.write_single_line(out);
241+
}
242+
}
243243
}
244244
}
245245

0 commit comments

Comments
 (0)