1use crate::debug::TableEntry;
2use crate::durability::Durability;
3use crate::intern_id::InternId;
4use crate::plumbing::CycleRecoveryStrategy;
5use crate::plumbing::HasQueryGroup;
6use crate::plumbing::QueryStorageMassOps;
7use crate::plumbing::QueryStorageOps;
8use crate::revision::Revision;
9use crate::Query;
10use crate::{Database, DatabaseKeyIndex, QueryDb};
11use parking_lot::RwLock;
12use rustc_hash::FxHashMap;
13use std::collections::hash_map::Entry;
14use std::convert::From;
15use std::fmt::Debug;
16use std::hash::Hash;
17use triomphe::Arc;
18
19const INTERN_DURABILITY: Durability = Durability::HIGH;
20
21pub struct InternedStorage<Q>
24where
25 Q: Query,
26 Q::Value: InternKey,
27{
28 group_index: u16,
29 tables: RwLock<InternTables<Q::Key>>,
30}
31
32pub struct LookupInternedStorage<Q, IQ>
34where
35 Q: Query,
36 Q::Key: InternKey,
37 Q::Value: Eq + Hash,
38{
39 phantom: std::marker::PhantomData<(Q::Key, IQ)>,
40}
41
42struct InternTables<K> {
43 map: FxHashMap<K, InternId>,
45
46 values: Vec<Arc<Slot<K>>>,
48}
49
50pub trait InternKey {
54 fn from_intern_id(v: InternId) -> Self;
56
57 fn as_intern_id(&self) -> InternId;
59}
60
61impl InternKey for InternId {
62 fn from_intern_id(v: InternId) -> InternId {
63 v
64 }
65
66 fn as_intern_id(&self) -> InternId {
67 *self
68 }
69}
70
71#[derive(Debug)]
72struct Slot<K> {
73 database_key_index: DatabaseKeyIndex,
75
76 value: K,
78
79 interned_at: Revision,
83}
84
85impl<Q> std::panic::RefUnwindSafe for InternedStorage<Q>
86where
87 Q: Query,
88 Q::Key: std::panic::RefUnwindSafe,
89 Q::Value: InternKey,
90 Q::Value: std::panic::RefUnwindSafe,
91{
92}
93
94impl<K: Debug + Hash + Eq> InternTables<K> {
95 fn slot_for_key(&self, key: &K) -> Option<(Arc<Slot<K>>, InternId)> {
97 let &index = self.map.get(key)?;
98 Some((self.slot_for_index(index), index))
99 }
100
101 fn slot_for_index(&self, index: InternId) -> Arc<Slot<K>> {
103 let slot = &self.values[index.as_usize()];
104 slot.clone()
105 }
106}
107
108impl<K> Default for InternTables<K>
109where
110 K: Eq + Hash,
111{
112 fn default() -> Self {
113 Self {
114 map: Default::default(),
115 values: Default::default(),
116 }
117 }
118}
119
120impl<Q> InternedStorage<Q>
121where
122 Q: Query,
123 Q::Key: Eq + Hash + Clone,
124 Q::Value: InternKey,
125{
126 fn intern_index(
128 &self,
129 db: &<Q as QueryDb<'_>>::DynDb,
130 key: &Q::Key,
131 ) -> (Arc<Slot<Q::Key>>, InternId) {
132 if let Some(i) = self.intern_check(key) {
133 return i;
134 }
135
136 let owned_key1 = key.to_owned();
137 let owned_key2 = owned_key1.clone();
138 let revision_now = db.salsa_runtime().current_revision();
139
140 let mut tables = self.tables.write();
141 let tables = &mut *tables;
142 let entry = match tables.map.entry(owned_key1) {
143 Entry::Vacant(entry) => entry,
144 Entry::Occupied(entry) => {
145 let index = *entry.get();
150 let slot = &tables.values[index.as_usize()];
151 debug_assert_eq!(owned_key2, slot.value);
152 return (slot.clone(), index);
153 }
154 };
155
156 let create_slot = |index: InternId| {
157 let database_key_index = DatabaseKeyIndex {
158 group_index: self.group_index,
159 query_index: Q::QUERY_INDEX,
160 key_index: index.as_u32(),
161 };
162 Arc::new(Slot {
163 database_key_index,
164 value: owned_key2,
165 interned_at: revision_now,
166 })
167 };
168
169 let (slot, index);
170 index = InternId::from(tables.values.len());
171 slot = create_slot(index);
172 tables.values.push(slot.clone());
173 entry.insert(index);
174
175 (slot, index)
176 }
177
178 fn intern_check(&self, key: &Q::Key) -> Option<(Arc<Slot<Q::Key>>, InternId)> {
179 self.tables.read().slot_for_key(key)
180 }
181
182 fn lookup_value(&self, index: InternId) -> Arc<Slot<Q::Key>> {
185 self.tables.read().slot_for_index(index)
186 }
187}
188
189impl<Q> QueryStorageOps<Q> for InternedStorage<Q>
190where
191 Q: Query,
192 Q::Value: InternKey,
193{
194 const CYCLE_STRATEGY: crate::plumbing::CycleRecoveryStrategy = CycleRecoveryStrategy::Panic;
195
196 fn new(group_index: u16) -> Self {
197 InternedStorage {
198 group_index,
199 tables: RwLock::new(InternTables::default()),
200 }
201 }
202
203 fn fmt_index(
204 &self,
205 _db: &<Q as QueryDb<'_>>::DynDb,
206 index: DatabaseKeyIndex,
207 fmt: &mut std::fmt::Formatter<'_>,
208 ) -> std::fmt::Result {
209 assert_eq!(index.group_index, self.group_index);
210 assert_eq!(index.query_index, Q::QUERY_INDEX);
211 let intern_id = InternId::from(index.key_index);
212 let slot = self.lookup_value(intern_id);
213 write!(fmt, "{}({:?})", Q::QUERY_NAME, slot.value)
214 }
215
216 fn maybe_changed_after(
217 &self,
218 db: &<Q as QueryDb<'_>>::DynDb,
219 input: DatabaseKeyIndex,
220 revision: Revision,
221 ) -> bool {
222 assert_eq!(input.group_index, self.group_index);
223 assert_eq!(input.query_index, Q::QUERY_INDEX);
224 debug_assert!(revision < db.salsa_runtime().current_revision());
225 let intern_id = InternId::from(input.key_index);
226 let slot = self.lookup_value(intern_id);
227 slot.maybe_changed_after(revision)
228 }
229
230 fn fetch(&self, db: &<Q as QueryDb<'_>>::DynDb, key: &Q::Key) -> Q::Value {
231 db.unwind_if_cancelled();
232 let (slot, index) = self.intern_index(db, key);
233 let changed_at = slot.interned_at;
234 db.salsa_runtime()
235 .report_query_read_and_unwind_if_cycle_resulted(
236 slot.database_key_index,
237 INTERN_DURABILITY,
238 changed_at,
239 );
240 <Q::Value>::from_intern_id(index)
241 }
242
243 fn durability(&self, _db: &<Q as QueryDb<'_>>::DynDb, _key: &Q::Key) -> Durability {
244 INTERN_DURABILITY
245 }
246
247 fn entries<C>(&self, _db: &<Q as QueryDb<'_>>::DynDb) -> C
248 where
249 C: std::iter::FromIterator<TableEntry<Q::Key, Q::Value>>,
250 {
251 let tables = self.tables.read();
252 tables
253 .map
254 .iter()
255 .map(|(key, index)| {
256 TableEntry::new(key.clone(), Some(<Q::Value>::from_intern_id(*index)))
257 })
258 .collect()
259 }
260}
261
262impl<Q> QueryStorageMassOps for InternedStorage<Q>
263where
264 Q: Query,
265 Q::Value: InternKey,
266{
267 fn purge(&self) {
268 *self.tables.write() = Default::default();
269 }
270}
271
272#[doc(hidden)]
283pub trait EqualDynDb<'d, IQ>: QueryDb<'d>
284where
285 IQ: QueryDb<'d>,
286{
287 fn convert_db(d: &Self::DynDb) -> &IQ::DynDb;
288 fn convert_group_storage(d: &Self::GroupStorage) -> &IQ::GroupStorage;
289}
290
291impl<'d, IQ, Q> EqualDynDb<'d, IQ> for Q
292where
293 Q: QueryDb<'d, DynDb = IQ::DynDb, Group = IQ::Group, GroupStorage = IQ::GroupStorage>,
294 Q::DynDb: HasQueryGroup<Q::Group>,
295 IQ: QueryDb<'d>,
296{
297 fn convert_db(d: &Self::DynDb) -> &IQ::DynDb {
298 d
299 }
300 fn convert_group_storage(d: &Self::GroupStorage) -> &IQ::GroupStorage {
301 d
302 }
303}
304
305impl<Q, IQ> QueryStorageOps<Q> for LookupInternedStorage<Q, IQ>
306where
307 Q: Query,
308 Q::Key: InternKey,
309 Q::Value: Eq + Hash,
310 IQ: Query<Key = Q::Value, Value = Q::Key, Storage = InternedStorage<IQ>>,
311 for<'d> Q: EqualDynDb<'d, IQ>,
312{
313 const CYCLE_STRATEGY: CycleRecoveryStrategy = CycleRecoveryStrategy::Panic;
314
315 fn new(_group_index: u16) -> Self {
316 LookupInternedStorage {
317 phantom: std::marker::PhantomData,
318 }
319 }
320
321 fn fmt_index(
322 &self,
323 db: &<Q as QueryDb<'_>>::DynDb,
324 index: DatabaseKeyIndex,
325 fmt: &mut std::fmt::Formatter<'_>,
326 ) -> std::fmt::Result {
327 let group_storage =
328 <<Q as QueryDb<'_>>::DynDb as HasQueryGroup<Q::Group>>::group_storage(db);
329 let interned_storage = IQ::query_storage(Q::convert_group_storage(group_storage));
330 interned_storage.fmt_index(Q::convert_db(db), index, fmt)
331 }
332
333 fn maybe_changed_after(
334 &self,
335 db: &<Q as QueryDb<'_>>::DynDb,
336 input: DatabaseKeyIndex,
337 revision: Revision,
338 ) -> bool {
339 let group_storage =
340 <<Q as QueryDb<'_>>::DynDb as HasQueryGroup<Q::Group>>::group_storage(db);
341 let interned_storage = IQ::query_storage(Q::convert_group_storage(group_storage));
342 interned_storage.maybe_changed_after(Q::convert_db(db), input, revision)
343 }
344
345 fn fetch(&self, db: &<Q as QueryDb<'_>>::DynDb, key: &Q::Key) -> Q::Value {
346 let index = key.as_intern_id();
347 let group_storage =
348 <<Q as QueryDb<'_>>::DynDb as HasQueryGroup<Q::Group>>::group_storage(db);
349 let interned_storage = IQ::query_storage(Q::convert_group_storage(group_storage));
350 let slot = interned_storage.lookup_value(index);
351 let value = slot.value.clone();
352 let interned_at = slot.interned_at;
353 db.salsa_runtime()
354 .report_query_read_and_unwind_if_cycle_resulted(
355 slot.database_key_index,
356 INTERN_DURABILITY,
357 interned_at,
358 );
359 value
360 }
361
362 fn durability(&self, _db: &<Q as QueryDb<'_>>::DynDb, _key: &Q::Key) -> Durability {
363 INTERN_DURABILITY
364 }
365
366 fn entries<C>(&self, db: &<Q as QueryDb<'_>>::DynDb) -> C
367 where
368 C: std::iter::FromIterator<TableEntry<Q::Key, Q::Value>>,
369 {
370 let group_storage =
371 <<Q as QueryDb<'_>>::DynDb as HasQueryGroup<Q::Group>>::group_storage(db);
372 let interned_storage = IQ::query_storage(Q::convert_group_storage(group_storage));
373 let tables = interned_storage.tables.read();
374 tables
375 .map
376 .iter()
377 .map(|(key, index)| {
378 TableEntry::new(<Q::Key>::from_intern_id(*index), Some(key.clone()))
379 })
380 .collect()
381 }
382}
383
384impl<Q, IQ> QueryStorageMassOps for LookupInternedStorage<Q, IQ>
385where
386 Q: Query,
387 Q::Key: InternKey,
388 Q::Value: Eq + Hash,
389 IQ: Query<Key = Q::Value, Value = Q::Key>,
390{
391 fn purge(&self) {}
392}
393
394impl<K> Slot<K> {
395 fn maybe_changed_after(&self, revision: Revision) -> bool {
396 self.interned_at > revision
397 }
398}
399
400#[allow(dead_code)]
404fn check_send_sync<K>()
405where
406 K: Send + Sync,
407{
408 fn is_send_sync<T: Send + Sync>() {}
409 is_send_sync::<Slot<K>>();
410}
411
412#[allow(dead_code)]
416fn check_static<K>()
417where
418 K: 'static,
419{
420 fn is_static<T: 'static>() {}
421 is_static::<Slot<K>>();
422}