Skip to content

Commit 75e0078

Browse files
committed
Optimize TokenStreamBuilder::push.
Currently, when two tokens must be glued together, this function duplicates large chunks of the existing streams. This can cause quadratic behaviour. This commit changes the function so that it overwrites the last token with a glued token, which avoids the quadratic behaviour. This removes the need for `TokenStreamBuilder::push_all_but_{first,last}_tree`. The commit also restructures `push` somewhat, by removing `TokenStream::{first_tree_and_joint,last_tree_if_joint}` in favour of more pattern matching and some comments. This makes the code shorter, and in my opinion, more readable.
1 parent 3832a63 commit 75e0078

File tree

1 file changed

+43
-51
lines changed

1 file changed

+43
-51
lines changed

src/libsyntax/tokenstream.rs

+43-51
Original file line numberDiff line numberDiff line change
@@ -390,25 +390,6 @@ impl TokenStream {
390390
.collect())
391391
}))
392392
}
393-
394-
fn first_tree_and_joint(&self) -> Option<TreeAndJoint> {
395-
self.0.as_ref().map(|stream| {
396-
stream.first().unwrap().clone()
397-
})
398-
}
399-
400-
fn last_tree_if_joint(&self) -> Option<TokenTree> {
401-
match self.0 {
402-
None => None,
403-
Some(ref stream) => {
404-
if let (tree, Joint) = stream.last().unwrap() {
405-
Some(tree.clone())
406-
} else {
407-
None
408-
}
409-
}
410-
}
411-
}
412393
}
413394

414395
// 99.5%+ of the time we have 1 or 2 elements in this vector.
@@ -421,18 +402,49 @@ impl TokenStreamBuilder {
421402
}
422403

423404
pub fn push<T: Into<TokenStream>>(&mut self, stream: T) {
424-
let stream = stream.into();
425-
let last_tree_if_joint = self.0.last().and_then(TokenStream::last_tree_if_joint);
426-
if let Some(TokenTree::Token(last_token)) = last_tree_if_joint {
427-
if let Some((TokenTree::Token(token), is_joint)) = stream.first_tree_and_joint() {
428-
if let Some(glued_tok) = last_token.glue(&token) {
429-
let last_stream = self.0.pop().unwrap();
430-
self.push_all_but_last_tree(&last_stream);
431-
let glued_tt = TokenTree::Token(glued_tok);
432-
let glued_tokenstream = TokenStream::new(vec![(glued_tt, is_joint)]);
433-
self.0.push(glued_tokenstream);
434-
self.push_all_but_first_tree(&stream);
435-
return
405+
let mut stream = stream.into();
406+
407+
// If `self` is not empty and the last tree within the last stream is a
408+
// token tree marked with `Joint`...
409+
if let Some(TokenStream(Some(ref mut last_stream_lrc))) = self.0.last_mut() {
410+
if let Some((TokenTree::Token(last_token), Joint)) = last_stream_lrc.last() {
411+
412+
// ...and `stream` is not empty and the first tree within it is
413+
// a token tree...
414+
if let TokenStream(Some(ref mut stream_lrc)) = stream {
415+
if let Some((TokenTree::Token(token), is_joint)) = stream_lrc.first() {
416+
417+
// ...and the two tokens can be glued together...
418+
if let Some(glued_tok) = last_token.glue(&token) {
419+
420+
// ...then do so, by overwriting the last token
421+
// tree in `self` and removing the first token tree
422+
// from `stream`. This requires using `make_mut()`
423+
// on the last stream in `self` and on `stream`,
424+
// and in practice this doesn't cause cloning 99.9%
425+
// of the time.
426+
427+
// Overwrite the last token tree with the merged
428+
// token.
429+
let last_vec_mut = Lrc::make_mut(last_stream_lrc);
430+
*last_vec_mut.last_mut().unwrap() =
431+
(TokenTree::Token(glued_tok), *is_joint);
432+
433+
// Remove the first token tree from `stream`. (This
434+
// is almost always the only tree in `stream`.)
435+
let stream_vec_mut = Lrc::make_mut(stream_lrc);
436+
stream_vec_mut.remove(0);
437+
438+
// Don't push `stream` if it's empty -- that could
439+
// block subsequent token gluing, by getting
440+
// between two token trees that should be glued
441+
// together.
442+
if !stream.is_empty() {
443+
self.0.push(stream);
444+
}
445+
return;
446+
}
447+
}
436448
}
437449
}
438450
}
@@ -442,26 +454,6 @@ impl TokenStreamBuilder {
442454
pub fn build(self) -> TokenStream {
443455
TokenStream::from_streams(self.0)
444456
}
445-
446-
fn push_all_but_last_tree(&mut self, stream: &TokenStream) {
447-
if let Some(ref streams) = stream.0 {
448-
let len = streams.len();
449-
match len {
450-
1 => {}
451-
_ => self.0.push(TokenStream(Some(Lrc::new(streams[0 .. len - 1].to_vec())))),
452-
}
453-
}
454-
}
455-
456-
fn push_all_but_first_tree(&mut self, stream: &TokenStream) {
457-
if let Some(ref streams) = stream.0 {
458-
let len = streams.len();
459-
match len {
460-
1 => {}
461-
_ => self.0.push(TokenStream(Some(Lrc::new(streams[1 .. len].to_vec())))),
462-
}
463-
}
464-
}
465457
}
466458

467459
#[derive(Clone)]

0 commit comments

Comments
 (0)