Skip to content

Commit 278e7c3

Browse files
committed
more frugal map
1 parent 4f3fc6f commit 278e7c3

File tree

3 files changed

+29
-16
lines changed

3 files changed

+29
-16
lines changed

Cargo.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/hir_expand/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ either = "1.5.3"
1616
rustc-hash = "1.0.0"
1717
la-arena = { version = "0.3.0", path = "../../lib/arena" }
1818
itertools = "0.10.0"
19-
once_cell = "1"
19+
hashbrown = { version = "0.11", features = ["inline-more"], default-features = false }
2020

2121
base_db = { path = "../base_db", version = "0.0.0" }
2222
cfg = { path = "../cfg", version = "0.0.0" }

crates/hir_expand/src/ast_id_map.rs

Lines changed: 27 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,13 @@
88
use std::{
99
any::type_name,
1010
fmt,
11-
hash::{Hash, Hasher},
11+
hash::{BuildHasher, BuildHasherDefault, Hash, Hasher},
1212
marker::PhantomData,
1313
};
1414

1515
use la_arena::{Arena, Idx};
1616
use profile::Count;
17-
use rustc_hash::FxHashMap;
17+
use rustc_hash::FxHasher;
1818
use syntax::{ast, match_ast, AstNode, AstPtr, SyntaxNode, SyntaxNodePtr};
1919

2020
/// `AstId` points to an AST node in a specific file.
@@ -63,11 +63,10 @@ type ErasedFileAstId = Idx<SyntaxNodePtr>;
6363
/// Maps items' `SyntaxNode`s to `ErasedFileAstId`s and back.
6464
#[derive(Default)]
6565
pub struct AstIdMap {
66+
/// Maps stable id to unstable ptr.
6667
arena: Arena<SyntaxNodePtr>,
67-
/// Reversed mapping lazily derived from [`self.arena`].
68-
///
69-
/// FIXE: Do not store `SyntaxNodePtr` twice.
70-
map: once_cell::sync::OnceCell<FxHashMap<SyntaxNodePtr, ErasedFileAstId>>,
68+
/// Reverse: map ptr to id.
69+
map: hashbrown::HashMap<Idx<SyntaxNodePtr>, (), ()>,
7170
_c: Count<Self>,
7271
}
7372

@@ -107,26 +106,34 @@ impl AstIdMap {
107106
}
108107
}
109108
});
109+
res.map = hashbrown::HashMap::with_capacity_and_hasher(res.arena.len(), ());
110+
for (idx, ptr) in res.arena.iter() {
111+
let hash = hash_ptr(ptr);
112+
match res.map.raw_entry_mut().from_hash(hash, |idx2| *idx2 == idx) {
113+
hashbrown::hash_map::RawEntryMut::Occupied(_) => unreachable!(),
114+
hashbrown::hash_map::RawEntryMut::Vacant(entry) => {
115+
entry.insert_with_hasher(hash, idx, (), |&idx| hash_ptr(&res.arena[idx]));
116+
}
117+
}
118+
}
110119
res
111120
}
112-
fn map(&self) -> &FxHashMap<SyntaxNodePtr, ErasedFileAstId> {
113-
self.map.get_or_init(|| self.arena.iter().map(|(idx, ptr)| (ptr.clone(), idx)).collect())
114-
}
115121

116122
pub fn ast_id<N: AstNode>(&self, item: &N) -> FileAstId<N> {
117123
let raw = self.erased_ast_id(item.syntax());
118124
FileAstId { raw, _ty: PhantomData }
119125
}
120-
121126
fn erased_ast_id(&self, item: &SyntaxNode) -> ErasedFileAstId {
122127
let ptr = SyntaxNodePtr::new(item);
123-
*self.map().get(&ptr).unwrap_or_else(|| {
124-
panic!(
128+
let hash = hash_ptr(&ptr);
129+
match self.map.raw_entry().from_hash(hash, |&idx| self.arena[idx] == ptr) {
130+
Some((&idx, &())) => idx,
131+
None => panic!(
125132
"Can't find {:?} in AstIdMap:\n{:?}",
126133
item,
127134
self.arena.iter().map(|(_id, i)| i).collect::<Vec<_>>(),
128-
)
129-
})
135+
),
136+
}
130137
}
131138

132139
pub fn get<N: AstNode>(&self, id: FileAstId<N>) -> AstPtr<N> {
@@ -138,6 +145,12 @@ impl AstIdMap {
138145
}
139146
}
140147

148+
fn hash_ptr(ptr: &SyntaxNodePtr) -> u64 {
149+
let mut hasher = BuildHasherDefault::<FxHasher>::default().build_hasher();
150+
ptr.hash(&mut hasher);
151+
hasher.finish()
152+
}
153+
141154
/// Walks the subtree in bdfs order, calling `f` for each node. What is bdfs
142155
/// order? It is a mix of breadth-first and depth first orders. Nodes for which
143156
/// `f` returns true are visited breadth-first, all the other nodes are explored

0 commit comments

Comments
 (0)