1use dashmap::SharedValue;
2
3use crate::accumulator::accumulated_map::InputAccumulatedValues;
4use crate::durability::Durability;
5use crate::ingredient::{fmt_index, MaybeChangedAfter};
6use crate::key::InputDependencyIndex;
7use crate::plumbing::{IngredientIndices, Jar};
8use crate::table::memo::MemoTable;
9use crate::table::sync::SyncTable;
10use crate::table::Slot;
11use crate::zalsa::{IngredientIndex, Zalsa};
12use crate::zalsa_local::QueryOrigin;
13use crate::{Database, DatabaseKeyIndex, Id};
14use std::any::TypeId;
15use std::fmt;
16use std::hash::{BuildHasher, Hash, Hasher};
17use std::marker::PhantomData;
18use std::path::{Path, PathBuf};
19
20use super::hash::FxDashMap;
21use super::ingredient::Ingredient;
22use super::Revision;
23
24pub trait Configuration: Sized + 'static {
25 const DEBUG_NAME: &'static str;
26
27 type Fields<'db>: InternedData;
29
30 type Struct<'db>: Copy;
32
33 fn struct_from_id<'db>(id: Id) -> Self::Struct<'db>;
40
41 fn deref_struct(s: Self::Struct<'_>) -> Id;
43}
44
45pub trait InternedData: Sized + Eq + Hash + Clone + Sync + Send {}
46impl<T: Eq + Hash + Clone + Sync + Send> InternedData for T {}
47
48pub struct JarImpl<C: Configuration> {
49 phantom: PhantomData<C>,
50}
51
52pub struct IngredientImpl<C: Configuration> {
57 ingredient_index: IngredientIndex,
59
60 key_map: FxDashMap<C::Fields<'static>, Id>,
64
65 reset_at: Revision,
70}
71
72pub struct Value<C>
74where
75 C: Configuration,
76{
77 fields: C::Fields<'static>,
78 memos: MemoTable,
79 syncs: SyncTable,
80}
81
82impl<C> Value<C>
83where
84 C: Configuration,
85{
86 #[cfg(feature = "salsa_unstable")]
88 pub fn fields(&self) -> &C::Fields<'static> {
89 &self.fields
90 }
91}
92
93impl<C: Configuration> Default for JarImpl<C> {
94 fn default() -> Self {
95 Self {
96 phantom: PhantomData,
97 }
98 }
99}
100
101impl<C: Configuration> Jar for JarImpl<C> {
102 fn create_ingredients(
103 _zalsa: &Zalsa,
104 first_index: IngredientIndex,
105 _dependencies: IngredientIndices,
106 ) -> Vec<Box<dyn Ingredient>> {
107 vec![Box::new(IngredientImpl::<C>::new(first_index)) as _]
108 }
109
110 fn id_struct_type_id() -> TypeId {
111 TypeId::of::<C::Struct<'static>>()
112 }
113}
114
115impl<C> IngredientImpl<C>
116where
117 C: Configuration,
118{
119 pub fn new(ingredient_index: IngredientIndex) -> Self {
120 Self {
121 ingredient_index,
122 key_map: Default::default(),
123 reset_at: Revision::start(),
124 }
125 }
126
127 unsafe fn to_internal_data<'db>(&'db self, data: C::Fields<'db>) -> C::Fields<'static> {
128 unsafe { std::mem::transmute(data) }
129 }
130
131 unsafe fn from_internal_data<'db>(data: &'db C::Fields<'static>) -> &'db C::Fields<'db> {
132 unsafe { std::mem::transmute(data) }
133 }
134
135 pub fn intern<'db, Key>(
145 &'db self,
146 db: &'db dyn crate::Database,
147 key: Key,
148 assemble: impl FnOnce(Id, Key) -> C::Fields<'db>,
149 ) -> C::Struct<'db>
150 where
151 Key: Hash,
152 C::Fields<'db>: HashEqLike<Key>,
153 {
154 C::struct_from_id(self.intern_id(db, key, assemble))
155 }
156
157 pub fn intern_id<'db, Key>(
167 &'db self,
168 db: &'db dyn crate::Database,
169 key: Key,
170 assemble: impl FnOnce(Id, Key) -> C::Fields<'db>,
171 ) -> crate::Id
172 where
173 Key: Hash,
174 C::Fields<'db>: HashEqLike<Key>,
179 {
180 let zalsa_local = db.zalsa_local();
181 zalsa_local.report_tracked_read(
182 InputDependencyIndex::for_table(self.ingredient_index),
183 Durability::MAX,
184 self.reset_at,
185 InputAccumulatedValues::Empty,
186 );
187
188 let data_hash = self.key_map.hasher().hash_one(&key);
190 let shard = &self.key_map.shards()[self.key_map.determine_shard(data_hash as _)];
191 let eq = |(data, _): &_| {
192 let data: &C::Fields<'db> = unsafe { std::mem::transmute(data) };
195 HashEqLike::eq(data, &key)
196 };
197
198 {
199 let lock = shard.read();
200 if let Some(bucket) = lock.find(data_hash, eq) {
201 return unsafe { *bucket.as_ref().1.get() };
203 }
204 }
205
206 let mut lock = shard.write();
207 match lock.find_or_find_insert_slot(data_hash, eq, |(element, _)| {
208 self.key_map.hasher().hash_one(element)
209 }) {
210 Ok(slot) => unsafe { *slot.as_ref().1.get() },
212 Err(slot) => {
214 let zalsa = db.zalsa();
215 let table = zalsa.table();
216 let id = zalsa_local.allocate(table, self.ingredient_index, |id| Value::<C> {
217 fields: unsafe { self.to_internal_data(assemble(id, key)) },
218 memos: Default::default(),
219 syncs: Default::default(),
220 });
221 unsafe {
222 lock.insert_in_slot(
223 data_hash,
224 slot,
225 (
226 table.get::<Value<C>>(id).fields.clone(),
227 SharedValue::new(id),
228 ),
229 )
230 };
231 debug_assert_eq!(
232 data_hash,
233 self.key_map
234 .hasher()
235 .hash_one(table.get::<Value<C>>(id).fields.clone())
236 );
237 id
238 }
239 }
240 }
241
242 pub fn data<'db>(&'db self, db: &'db dyn Database, id: Id) -> &'db C::Fields<'db> {
246 let internal_data = db.zalsa().table().get::<Value<C>>(id);
247 unsafe { Self::from_internal_data(&internal_data.fields) }
248 }
249
250 pub fn fields<'db>(&'db self, db: &'db dyn Database, s: C::Struct<'db>) -> &'db C::Fields<'db> {
253 self.data(db, C::deref_struct(s))
254 }
255
256 #[cfg(feature = "salsa_unstable")]
257 pub fn entries<'db>(
259 &'db self,
260 db: &'db dyn crate::Database,
261 ) -> impl Iterator<Item = &'db Value<C>> {
262 db.zalsa()
263 .table()
264 .pages
265 .iter()
266 .filter_map(|(_, page)| page.cast_type::<crate::table::Page<Value<C>>>())
267 .flat_map(|page| page.slots())
268 }
269
270 pub fn reset(&mut self, revision: Revision) {
271 assert!(revision > self.reset_at);
272 self.reset_at = revision;
273 self.key_map.clear();
274 }
275}
276
277impl<C> Ingredient for IngredientImpl<C>
278where
279 C: Configuration,
280{
281 fn ingredient_index(&self) -> IngredientIndex {
282 self.ingredient_index
283 }
284
285 unsafe fn maybe_changed_after(
286 &self,
287 _db: &dyn Database,
288 _input: Id,
289 revision: Revision,
290 ) -> MaybeChangedAfter {
291 MaybeChangedAfter::from(revision < self.reset_at)
292 }
293
294 fn cycle_recovery_strategy(&self) -> crate::cycle::CycleRecoveryStrategy {
295 crate::cycle::CycleRecoveryStrategy::Panic
296 }
297
298 fn origin(&self, _db: &dyn Database, _key_index: crate::Id) -> Option<QueryOrigin> {
299 None
300 }
301
302 fn mark_validated_output(
303 &self,
304 _db: &dyn Database,
305 executor: DatabaseKeyIndex,
306 output_key: crate::Id,
307 ) {
308 unreachable!(
309 "mark_validated_output({:?}, {:?}): input cannot be the output of a tracked function",
310 executor, output_key
311 );
312 }
313
314 fn remove_stale_output(
315 &self,
316 _db: &dyn Database,
317 executor: DatabaseKeyIndex,
318 stale_output_key: crate::Id,
319 ) {
320 unreachable!(
321 "remove_stale_output({:?}, {:?}): interned ids are not outputs",
322 executor, stale_output_key
323 );
324 }
325
326 fn requires_reset_for_new_revision(&self) -> bool {
330 false
331 }
332
333 fn fmt_index(&self, index: Option<crate::Id>, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
334 fmt_index(C::DEBUG_NAME, index, fmt)
335 }
336
337 fn debug_name(&self) -> &'static str {
338 C::DEBUG_NAME
339 }
340}
341
342impl<C> std::fmt::Debug for IngredientImpl<C>
343where
344 C: Configuration,
345{
346 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
347 f.debug_struct(std::any::type_name::<Self>())
348 .field("index", &self.ingredient_index)
349 .finish()
350 }
351}
352
353impl<C> Slot for Value<C>
354where
355 C: Configuration,
356{
357 unsafe fn memos(&self, _current_revision: Revision) -> &MemoTable {
358 &self.memos
359 }
360
361 fn memos_mut(&mut self) -> &mut MemoTable {
362 &mut self.memos
363 }
364
365 unsafe fn syncs(&self, _current_revision: Revision) -> &crate::table::sync::SyncTable {
366 &self.syncs
367 }
368}
369
370pub trait HashEqLike<O> {
372 fn hash<H: Hasher>(&self, h: &mut H);
373 fn eq(&self, data: &O) -> bool;
374}
375
376pub trait Lookup<O> {
391 fn into_owned(self) -> O;
392}
393
394impl<T> Lookup<T> for T {
395 fn into_owned(self) -> T {
396 self
397 }
398}
399
400impl<T> HashEqLike<T> for T
401where
402 T: Hash + Eq,
403{
404 fn hash<H: Hasher>(&self, h: &mut H) {
405 Hash::hash(self, &mut *h);
406 }
407
408 fn eq(&self, data: &T) -> bool {
409 self == data
410 }
411}
412
413impl<T> HashEqLike<T> for &T
414where
415 T: Hash + Eq,
416{
417 fn hash<H: Hasher>(&self, h: &mut H) {
418 Hash::hash(*self, &mut *h);
419 }
420
421 fn eq(&self, data: &T) -> bool {
422 **self == *data
423 }
424}
425
426impl<T> HashEqLike<&T> for T
427where
428 T: Hash + Eq,
429{
430 fn hash<H: Hasher>(&self, h: &mut H) {
431 Hash::hash(self, &mut *h);
432 }
433
434 fn eq(&self, data: &&T) -> bool {
435 *self == **data
436 }
437}
438
439impl<T> Lookup<T> for &T
440where
441 T: Clone,
442{
443 fn into_owned(self) -> T {
444 Clone::clone(self)
445 }
446}
447
448impl<'a, T> HashEqLike<&'a T> for Box<T>
449where
450 T: ?Sized + Hash + Eq,
451 Box<T>: From<&'a T>,
452{
453 fn hash<H: Hasher>(&self, h: &mut H) {
454 Hash::hash(self, &mut *h)
455 }
456 fn eq(&self, data: &&T) -> bool {
457 **self == **data
458 }
459}
460
461impl<'a, T> Lookup<Box<T>> for &'a T
462where
463 T: ?Sized + Hash + Eq,
464 Box<T>: From<&'a T>,
465{
466 fn into_owned(self) -> Box<T> {
467 Box::from(self)
468 }
469}
470
471impl Lookup<String> for &str {
472 fn into_owned(self) -> String {
473 self.to_owned()
474 }
475}
476impl HashEqLike<&str> for String {
477 fn hash<H: Hasher>(&self, h: &mut H) {
478 Hash::hash(self, &mut *h)
479 }
480
481 fn eq(&self, data: &&str) -> bool {
482 self == *data
483 }
484}
485
486impl<A, T: Hash + Eq + PartialEq<A>> HashEqLike<&[A]> for Vec<T> {
487 fn hash<H: Hasher>(&self, h: &mut H) {
488 Hash::hash(self, h);
489 }
490
491 fn eq(&self, data: &&[A]) -> bool {
492 self.len() == data.len() && data.iter().enumerate().all(|(i, a)| &self[i] == a)
493 }
494}
495impl<A: Hash + Eq + PartialEq<T> + Clone + Lookup<T>, T> Lookup<Vec<T>> for &[A] {
496 fn into_owned(self) -> Vec<T> {
497 self.iter().map(|a| Lookup::into_owned(a.clone())).collect()
498 }
499}
500
501impl<const N: usize, A, T: Hash + Eq + PartialEq<A>> HashEqLike<[A; N]> for Vec<T> {
502 fn hash<H: Hasher>(&self, h: &mut H) {
503 Hash::hash(self, h);
504 }
505
506 fn eq(&self, data: &[A; N]) -> bool {
507 self.len() == data.len() && data.iter().enumerate().all(|(i, a)| &self[i] == a)
508 }
509}
510impl<const N: usize, A: Hash + Eq + PartialEq<T> + Clone + Lookup<T>, T> Lookup<Vec<T>> for [A; N] {
511 fn into_owned(self) -> Vec<T> {
512 self.into_iter()
513 .map(|a| Lookup::into_owned(a.clone()))
514 .collect()
515 }
516}
517
518impl HashEqLike<&Path> for PathBuf {
519 fn hash<H: Hasher>(&self, h: &mut H) {
520 Hash::hash(self, h);
521 }
522
523 fn eq(&self, data: &&Path) -> bool {
524 self == data
525 }
526}
527impl Lookup<PathBuf> for &Path {
528 fn into_owned(self) -> PathBuf {
529 self.to_owned()
530 }
531}