miden_rowan/green/
node_cache.rs1use alloc::vec::Vec;
2use core::hash::{BuildHasherDefault, Hash, Hasher};
3use hashbrown::hash_map::RawEntryMut;
4use rustc_hash::FxHasher;
5
6use crate::{
7 GreenNode, GreenNodeData, GreenToken, GreenTokenData, NodeOrToken, SyntaxKind,
8 green::GreenElementRef,
9};
10
11use super::element::GreenElement;
12
13type HashMap<K, V> = hashbrown::HashMap<K, V, BuildHasherDefault<FxHasher>>;
14
15#[derive(Debug)]
16struct NoHash<T>(T);
17
18#[derive(Default, Debug)]
38pub struct NodeCache {
39 nodes: HashMap<NoHash<GreenNode>, ()>,
40 tokens: HashMap<NoHash<GreenToken>, ()>,
41}
42
43fn token_hash(token: &GreenTokenData) -> u64 {
44 let mut h = FxHasher::default();
45 token.kind().hash(&mut h);
46 token.text().hash(&mut h);
47 h.finish()
48}
49
50fn node_hash(node: &GreenNodeData) -> u64 {
51 let mut h = FxHasher::default();
52 node.kind().hash(&mut h);
53 for child in node.children() {
54 match child {
55 NodeOrToken::Node(it) => node_hash(it),
56 NodeOrToken::Token(it) => token_hash(it),
57 }
58 .hash(&mut h)
59 }
60 h.finish()
61}
62
63fn element_id(elem: GreenElementRef<'_>) -> *const () {
64 match elem {
65 NodeOrToken::Node(it) => it as *const GreenNodeData as *const (),
66 NodeOrToken::Token(it) => it as *const GreenTokenData as *const (),
67 }
68}
69
70impl NodeCache {
71 pub(crate) fn node(
72 &mut self,
73 kind: SyntaxKind,
74 children: &mut Vec<(u64, GreenElement)>,
75 first_child: usize,
76 ) -> (u64, GreenNode) {
77 let build_node = move |children: &mut Vec<(u64, GreenElement)>| {
78 GreenNode::new(kind, children.drain(first_child..).map(|(_, it)| it))
79 };
80
81 let children_ref = &children[first_child..];
82 if children_ref.len() > 3 {
83 let node = build_node(children);
84 return (0, node);
85 }
86
87 let hash = {
88 let mut h = FxHasher::default();
89 kind.hash(&mut h);
90 for &(hash, _) in children_ref {
91 if hash == 0 {
92 let node = build_node(children);
93 return (0, node);
94 }
95 hash.hash(&mut h);
96 }
97 h.finish()
98 };
99
100 let entry = self.nodes.raw_entry_mut().from_hash(hash, |node| {
108 node.0.kind() == kind && node.0.children().len() == children_ref.len() && {
109 let lhs = node.0.children();
110 let rhs = children_ref.iter().map(|(_, it)| it.as_deref());
111
112 let lhs = lhs.map(element_id);
113 let rhs = rhs.map(element_id);
114
115 lhs.eq(rhs)
116 }
117 });
118
119 let node = match entry {
120 RawEntryMut::Occupied(entry) => {
121 drop(children.drain(first_child..));
122 entry.key().0.clone()
123 }
124 RawEntryMut::Vacant(entry) => {
125 let node = build_node(children);
126 entry.insert_with_hasher(hash, NoHash(node.clone()), (), |n| node_hash(&n.0));
127 node
128 }
129 };
130
131 (hash, node)
132 }
133
134 pub(crate) fn token(&mut self, kind: SyntaxKind, text: &str) -> (u64, GreenToken) {
135 let hash = {
136 let mut h = FxHasher::default();
137 kind.hash(&mut h);
138 text.hash(&mut h);
139 h.finish()
140 };
141
142 let entry = self
143 .tokens
144 .raw_entry_mut()
145 .from_hash(hash, |token| token.0.kind() == kind && token.0.text() == text);
146
147 let token = match entry {
148 RawEntryMut::Occupied(entry) => entry.key().0.clone(),
149 RawEntryMut::Vacant(entry) => {
150 let token = GreenToken::new(kind, text);
151 entry.insert_with_hasher(hash, NoHash(token.clone()), (), |t| token_hash(&t.0));
152 token
153 }
154 };
155
156 (hash, token)
157 }
158}