diff --git a/Cargo.lock b/Cargo.lock index 87176f0..c8c8bcc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8,9 +8,9 @@ checksum = "739f4a8db6605981345c5654f3a85b056ce52f37a39d34da03f25bf2151ea16e" [[package]] name = "aho-corasick" -version = "0.7.10" +version = "0.7.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8716408b8bc624ed7f65d223ddb9ac2d044c0547b6fa4b0d554f3a9540496ada" +checksum = "7404febffaa47dac81aa44dba71523c9d069b1bdc50a77db41195149e17f68e5" dependencies = [ "memchr", ] @@ -37,7 +37,6 @@ dependencies = [ "parking_lot", "serde", "servo_arc", - "smallvec", "text-size", ] @@ -109,9 +108,9 @@ dependencies = [ [[package]] name = "memchr" -version = "2.3.3" +version = "2.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3728d817d99e5ac407411fa471ff9800a778d88a24685968b36824eaf4bee400" +checksum = "0ee1c47aaa256ecabcaea351eae4a9b01ef39ed810004e298d2511ed284b1525" [[package]] name = "nodrop" @@ -152,9 +151,9 @@ checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce" [[package]] name = "regex" -version = "1.3.6" +version = "1.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f6946991529684867e47d86474e3a6d0c0ab9b82d5821e314b1ede31fa3a4b3" +checksum = "d9251239e129e16308e70d853559389de218ac275b515068abc96829d05b948a" dependencies = [ "aho-corasick", "memchr", @@ -164,9 +163,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.6.17" +version = "0.6.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fe5bd57d1d7414c6b5ed48563a2c855d995ff777729dcd91c369ec7fea395ae" +checksum = "b5eb417147ba9860a96cfe72a0b93bf88fee1744b5636ec99ab20c1aa9376581" [[package]] name = "scopeguard" @@ -176,9 +175,9 @@ checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" [[package]] name = "serde" -version = "1.0.106" +version = "1.0.119" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36df6ac6412072f67cf767ebbde4133a5b2e88e76dc6187fa7104cd16f783399" +checksum = "9bdd36f49e35b61d49efd8aa7fc068fd295961fd2286d0b2ee9a4c7a14e99cc3" [[package]] name = "servo_arc" @@ -202,18 +201,18 @@ checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" [[package]] name = "text-size" -version = "1.0.0" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f03e7efdedc3bc78cb2337f1e2785c39e45f5ef762d9e4ebb137fff7380a6d8a" +checksum = "288cb548dbe72b652243ea797201f3d481a0609a967980fcc5b2315ea811560a" dependencies = [ "serde", ] [[package]] name = "thread_local" -version = "1.0.1" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d40c6d1b69745a6ec6fb1ca717914848da4b44ae29d9b3080cbee91d72a69b14" +checksum = "bb9bc092d0d51e76b2b19d9d85534ffc9ec2db959a2523cdae0697e2972cd447" dependencies = [ "lazy_static", ] diff --git a/Cargo.toml b/Cargo.toml index 13dda96..4acd706 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,7 +12,6 @@ serde = { version = "1.0.89", optional = true, default-features = false } lasso = "0.4.1" text-size = "1.0.0" fxhash= "0.2.1" -smallvec = "1.6.1" servo_arc = { path = "vendor/servo_arc" } parking_lot= "0.11.1" diff --git a/src/green.rs b/src/green.rs index 6b004ad..519fb71 100644 --- a/src/green.rs +++ b/src/green.rs @@ -1,7 +1,7 @@ +mod builder; +mod element; mod node; mod token; -mod element; -mod builder; pub(crate) use self::element::GreenElementRef; use self::element::{GreenElement, PackedGreenElement}; diff --git a/src/green/builder.rs b/src/green/builder.rs index 8e23220..527d222 100644 --- a/src/green/builder.rs +++ b/src/green/builder.rs @@ -2,7 +2,6 @@ use std::{convert::TryFrom, num::NonZeroUsize}; use fxhash::{FxBuildHasher, FxHashMap}; use lasso::{Capacity, Rodeo, Spur}; -use smallvec::SmallVec; use text_size::TextSize; use crate::{ @@ -13,6 +12,12 @@ use crate::{ use super::{node::GreenNodeHead, token::GreenTokenData}; +/// If `node.children() <= CHILDREN_CACHE_THRESHOLD`, we will not create +/// a new [`GreenNode`], but instead lookup in the cache if this node is +/// already present. If so we use the one in the cache, otherwise we insert +/// this node into the cache. +const CHILDREN_CACHE_THRESHOLD: usize = 3; + #[derive(Debug)] pub struct NodeCache { nodes: FxHashMap, @@ -39,6 +44,7 @@ impl NodeCache { I::IntoIter: ExactSizeIterator, { let children = children.into_iter(); + // Green nodes are fully immutable, so it's ok to deduplicate them. // This is the same optimization that Roslyn does // https://github.com/KirillOsenkov/Bliki/wiki/Roslyn-Immutable-Trees @@ -46,18 +52,69 @@ impl NodeCache { // For example, all `#[inline]` in this file share the same green node! // For `libsyntax/parse/parser.rs`, measurements show that deduping saves // 17% of the memory for green nodes! - if children.len() <= 3 { - let children: SmallVec<[_; 3]> = children.collect(); - let head = GreenNodeHead::from_child_slice(kind, children.as_ref()); - self.nodes - .entry(head.clone()) - .or_insert_with(|| GreenNode::from_head_and_children(head, children)) - .clone() + if children.len() <= CHILDREN_CACHE_THRESHOLD { + self.get_cached_node(kind, children) } else { GreenNode::new(kind, children) } } + /// Creates a [`GreenNode`] by looking inside the cache or inserting + /// a new node into the cache if it's a cache miss. + fn get_cached_node(&mut self, kind: SyntaxKind, children: I) -> GreenNode + where + I: IntoIterator, + I::IntoIter: ExactSizeIterator, + { + #[derive(Clone)] + struct ChildrenIter { + data: [Option; CHILDREN_CACHE_THRESHOLD], + idx: usize, + len: usize, + } + + impl ChildrenIter { + fn new(data: [Option; CHILDREN_CACHE_THRESHOLD], count: usize) -> Self { + ChildrenIter { + data, + idx: 0, + len: count, + } + } + } + + impl Iterator for ChildrenIter { + type Item = GreenElement; + + fn next(&mut self) -> Option { + let item = self.data.get_mut(self.idx)?; + self.idx += 1; + item.take() + } + } + + impl ExactSizeIterator for ChildrenIter { + fn len(&self) -> usize { + self.len - self.idx + } + } + + let mut data: [Option; CHILDREN_CACHE_THRESHOLD] = [None, None, None]; + let mut count = 0; + + for child in children { + data[count] = Some(child); + count += 1; + } + let children = ChildrenIter::new(data, count); + + let head = GreenNodeHead::from_child_iter(kind, children.clone()); + self.nodes + .entry(head.clone()) + .or_insert_with(|| GreenNode::from_head_and_children(head, children)) + .clone() + } + fn token(&mut self, kind: SyntaxKind, text: &str) -> GreenToken { let text_len = TextSize::try_from(text.len()).unwrap(); let text = self.interner.get_or_intern(text); diff --git a/src/green/node.rs b/src/green/node.rs index 11bf5c8..d090cd5 100644 --- a/src/green/node.rs +++ b/src/green/node.rs @@ -22,7 +22,10 @@ pub(super) struct GreenNodeHead { impl GreenNodeHead { #[inline] - pub(super) fn from_child_slice(kind: SyntaxKind, children: &[GreenElement]) -> Self { + pub(super) fn from_child_iter(kind: SyntaxKind, children: I) -> Self + where + I: Iterator, + { let mut hasher = FxHasher32::default(); let mut text_len: TextSize = 0.into(); for child in children { diff --git a/src/serde_impls.rs b/src/serde_impls.rs index 10d7d18..4c9a3a7 100644 --- a/src/serde_impls.rs +++ b/src/serde_impls.rs @@ -1,10 +1,7 @@ use serde::ser::{Serialize, SerializeMap, SerializeSeq, Serializer}; use std::fmt; -use crate::{ - api::{Language, SyntaxNode, SyntaxToken}, - NodeOrToken, -}; +use crate::{Language, NodeOrToken, SyntaxNode, SyntaxToken}; struct SerDisplay(T); impl Serialize for SerDisplay { diff --git a/vendor/servo_arc/Cargo.lock b/vendor/servo_arc/Cargo.lock new file mode 100644 index 0000000..29593f2 --- /dev/null +++ b/vendor/servo_arc/Cargo.lock @@ -0,0 +1,28 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +[[package]] +name = "nodrop" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb" + +[[package]] +name = "serde" +version = "1.0.119" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9bdd36f49e35b61d49efd8aa7fc068fd295961fd2286d0b2ee9a4c7a14e99cc3" + +[[package]] +name = "servo_arc" +version = "0.1.1" +dependencies = [ + "nodrop", + "serde", + "stable_deref_trait", +] + +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"