8
8
use std:: {
9
9
any:: type_name,
10
10
fmt,
11
- hash:: { Hash , Hasher } ,
11
+ hash:: { BuildHasher , BuildHasherDefault , Hash , Hasher } ,
12
12
marker:: PhantomData ,
13
13
} ;
14
14
15
15
use la_arena:: { Arena , Idx } ;
16
16
use profile:: Count ;
17
- use rustc_hash:: FxHashMap ;
17
+ use rustc_hash:: FxHasher ;
18
18
use syntax:: { ast, match_ast, AstNode , AstPtr , SyntaxNode , SyntaxNodePtr } ;
19
19
20
20
/// `AstId` points to an AST node in a specific file.
@@ -63,11 +63,10 @@ type ErasedFileAstId = Idx<SyntaxNodePtr>;
63
63
/// Maps items' `SyntaxNode`s to `ErasedFileAstId`s and back.
64
64
#[ derive( Default ) ]
65
65
pub struct AstIdMap {
66
+ /// Maps stable id to unstable ptr.
66
67
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 > , ( ) , ( ) > ,
71
70
_c : Count < Self > ,
72
71
}
73
72
@@ -107,26 +106,34 @@ impl AstIdMap {
107
106
}
108
107
}
109
108
} ) ;
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
+ }
110
119
res
111
120
}
112
- fn map ( & self ) -> & FxHashMap < SyntaxNodePtr , ErasedFileAstId > {
113
- self . map . get_or_init ( || self . arena . iter ( ) . map ( |( idx, ptr) | ( ptr. clone ( ) , idx) ) . collect ( ) )
114
- }
115
121
116
122
pub fn ast_id < N : AstNode > ( & self , item : & N ) -> FileAstId < N > {
117
123
let raw = self . erased_ast_id ( item. syntax ( ) ) ;
118
124
FileAstId { raw, _ty : PhantomData }
119
125
}
120
-
121
126
fn erased_ast_id ( & self , item : & SyntaxNode ) -> ErasedFileAstId {
122
127
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 ! (
125
132
"Can't find {:?} in AstIdMap:\n {:?}" ,
126
133
item,
127
134
self . arena. iter( ) . map( |( _id, i) | i) . collect:: <Vec <_>>( ) ,
128
- )
129
- } )
135
+ ) ,
136
+ }
130
137
}
131
138
132
139
pub fn get < N : AstNode > ( & self , id : FileAstId < N > ) -> AstPtr < N > {
@@ -138,6 +145,12 @@ impl AstIdMap {
138
145
}
139
146
}
140
147
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
+
141
154
/// Walks the subtree in bdfs order, calling `f` for each node. What is bdfs
142
155
/// order? It is a mix of breadth-first and depth first orders. Nodes for which
143
156
/// `f` returns true are visited breadth-first, all the other nodes are explored
0 commit comments