use std::hash::{BuildHasherDefault, Hash, Hasher};
use hashbrown::hash_map::RawEntryMut;
use rustc_hash::FxHasher;
use crate::{
cow_mut::CowMut,
green::{GreenElement, GreenNode, GreenToken, SyntaxKind},
NodeOrToken, SmolStr,
};
type HashMap<K, V> = hashbrown::HashMap<K, V, BuildHasherDefault<FxHasher>>;
#[derive(Default, Debug)]
pub struct NodeCache {
nodes: HashMap<GreenNode, ()>,
tokens: HashMap<GreenToken, ()>,
}
impl NodeCache {
fn node(
&mut self,
kind: SyntaxKind,
children: &mut Vec<(u64, GreenElement)>,
first_child: usize,
) -> (u64, GreenNode) {
let build_node = move |children: &mut Vec<(u64, GreenElement)>| {
GreenNode::new(kind, children.drain(first_child..).map(|(_, it)| it))
};
let children_ref = &children[first_child..];
if children_ref.len() > 3 {
let node = build_node(children);
return (0, node);
}
let hash = {
let mut h = FxHasher::default();
kind.hash(&mut h);
for &(hash, _) in children_ref {
if hash == 0 {
let node = build_node(children);
return (0, node);
}
hash.hash(&mut h);
}
h.finish()
};
let entry = self.nodes.raw_entry_mut().from_hash(hash, |node| {
node.kind() == kind
&& node.children().len() == children_ref.len()
&& node.children().eq(children_ref.iter().map(|(_, it)| it.as_ref()))
});
let node = match entry {
RawEntryMut::Occupied(entry) => {
drop(children.drain(first_child..));
entry.key().clone()
}
RawEntryMut::Vacant(entry) => {
let node = build_node(children);
entry.insert_hashed_nocheck(hash, node.clone(), ());
node
}
};
(hash, node)
}
fn token(&mut self, kind: SyntaxKind, text: SmolStr) -> (u64, GreenToken) {
let hash = {
let mut h = FxHasher::default();
kind.hash(&mut h);
text.as_str().hash(&mut h);
h.finish()
};
let entry = self
.tokens
.raw_entry_mut()
.from_hash(hash, |token| token.kind() == kind && token.text() == &text);
let token = match entry {
RawEntryMut::Occupied(entry) => entry.key().clone(),
RawEntryMut::Vacant(entry) => {
let token = GreenToken::new(kind, text);
entry.insert_hashed_nocheck(hash, token.clone(), ());
token
}
};
(hash, token)
}
}
#[derive(Clone, Copy, Debug)]
pub struct Checkpoint(usize);
#[derive(Default, Debug)]
pub struct GreenNodeBuilder<'cache> {
cache: CowMut<'cache, NodeCache>,
parents: Vec<(SyntaxKind, usize)>,
children: Vec<(u64, GreenElement)>,
}
impl GreenNodeBuilder<'_> {
pub fn new() -> GreenNodeBuilder<'static> {
GreenNodeBuilder::default()
}
pub fn with_cache(cache: &mut NodeCache) -> GreenNodeBuilder<'_> {
GreenNodeBuilder {
cache: CowMut::Borrowed(cache),
parents: Vec::new(),
children: Vec::new(),
}
}
#[inline]
pub fn token(&mut self, kind: SyntaxKind, text: SmolStr) {
let (hash, token) = self.cache.token(kind, text);
self.children.push((hash, token.into()));
}
#[inline]
pub fn start_node(&mut self, kind: SyntaxKind) {
let len = self.children.len();
self.parents.push((kind, len));
}
#[inline]
pub fn finish_node(&mut self) {
let (kind, first_child) = self.parents.pop().unwrap();
let (hash, node) = self.cache.node(kind, &mut self.children, first_child);
self.children.push((hash, node.into()));
}
#[inline]
pub fn checkpoint(&self) -> Checkpoint {
Checkpoint(self.children.len())
}
#[inline]
pub fn start_node_at(&mut self, checkpoint: Checkpoint, kind: SyntaxKind) {
let Checkpoint(checkpoint) = checkpoint;
assert!(
checkpoint <= self.children.len(),
"checkpoint no longer valid, was finish_node called early?"
);
if let Some(&(_, first_child)) = self.parents.last() {
assert!(
checkpoint >= first_child,
"checkpoint no longer valid, was an unmatched start_node_at called?"
);
}
self.parents.push((kind, checkpoint));
}
#[inline]
pub fn finish(mut self) -> GreenNode {
assert_eq!(self.children.len(), 1);
match self.children.pop().unwrap().1 {
NodeOrToken::Node(node) => node,
NodeOrToken::Token(_) => panic!(),
}
}
}