salsa/
interned.rs

1use std::any::TypeId;
2use std::cell::{Cell, UnsafeCell};
3use std::fmt;
4use std::hash::{BuildHasher, Hash, Hasher};
5use std::marker::PhantomData;
6use std::num::NonZeroUsize;
7use std::path::{Path, PathBuf};
8
9use crossbeam_utils::CachePadded;
10use intrusive_collections::{intrusive_adapter, LinkedList, LinkedListLink, UnsafeRef};
11use rustc_hash::FxBuildHasher;
12
13use crate::durability::Durability;
14use crate::function::{VerifyCycleHeads, VerifyResult};
15use crate::hash::{FxHashSet, FxIndexSet};
16use crate::id::{AsId, FromId};
17use crate::ingredient::Ingredient;
18use crate::plumbing::{self, Jar, ZalsaLocal};
19use crate::revision::AtomicRevision;
20use crate::sync::{Arc, Mutex, OnceLock};
21use crate::table::memo::{MemoTable, MemoTableTypes, MemoTableWithTypesMut};
22use crate::table::Slot;
23use crate::zalsa::{IngredientIndex, JarKind, Zalsa};
24use crate::zalsa_local::QueryEdge;
25use crate::{DatabaseKeyIndex, Event, EventKind, Id, Revision};
26
27/// Trait that defines the key properties of an interned struct.
28///
29/// Implemented by the `#[salsa::interned]` macro when applied to
30/// a struct.
31pub trait Configuration: Sized + 'static {
32    const LOCATION: crate::ingredient::Location;
33    const DEBUG_NAME: &'static str;
34
35    /// Whether this struct should be persisted with the database.
36    const PERSIST: bool;
37
38    // The minimum number of revisions that must pass before a stale value is garbage collected.
39    #[cfg(test)]
40    const REVISIONS: NonZeroUsize = NonZeroUsize::new(3).unwrap();
41
42    #[cfg(not(test))] // More aggressive garbage collection by default when testing.
43    const REVISIONS: NonZeroUsize = NonZeroUsize::new(1).unwrap();
44
45    /// The fields of the struct being interned.
46    type Fields<'db>: InternedData;
47
48    /// The end user struct
49    type Struct<'db>: Copy + FromId + AsId;
50
51    /// Returns the size of any heap allocations in the output value, in bytes.
52    fn heap_size(_value: &Self::Fields<'_>) -> Option<usize> {
53        None
54    }
55
56    /// Serialize the fields using `serde`.
57    ///
58    /// Panics if the value is not persistable, i.e. `Configuration::PERSIST` is `false`.
59    fn serialize<S>(value: &Self::Fields<'_>, serializer: S) -> Result<S::Ok, S::Error>
60    where
61        S: plumbing::serde::Serializer;
62
63    /// Deserialize the fields using `serde`.
64    ///
65    /// Panics if the value is not persistable, i.e. `Configuration::PERSIST` is `false`.
66    fn deserialize<'de, D>(deserializer: D) -> Result<Self::Fields<'static>, D::Error>
67    where
68        D: plumbing::serde::Deserializer<'de>;
69}
70
71pub trait InternedData: Sized + Eq + Hash + Clone + Sync + Send {}
72impl<T: Eq + Hash + Clone + Sync + Send> InternedData for T {}
73
74pub struct JarImpl<C: Configuration> {
75    phantom: PhantomData<C>,
76}
77
78/// The interned ingredient hashes values of type `C::Fields` to produce an `Id`.
79///
80/// It used to store interned structs but also to store the ID fields of a tracked struct.
81/// Interned values are garbage collected and their memory reused based on an LRU heuristic.
82pub struct IngredientImpl<C: Configuration> {
83    /// Index of this ingredient in the database (used to construct database-IDs, etc).
84    ingredient_index: IngredientIndex,
85
86    /// A hasher for the sharded ID maps.
87    hasher: FxBuildHasher,
88
89    /// A shift used to determine the shard for a given hash.
90    shift: u32,
91
92    /// Sharded data that can only be accessed through a lock.
93    shards: Box<[CachePadded<Mutex<IngredientShard<C>>>]>,
94
95    /// A queue of recent revisions in which values were interned.
96    revision_queue: RevisionQueue<C>,
97
98    memo_table_types: Arc<MemoTableTypes>,
99
100    _marker: PhantomData<fn() -> C>,
101}
102
103struct IngredientShard<C: Configuration> {
104    /// Maps from data to the existing interned ID for that data.
105    ///
106    /// This doesn't hold the fields themselves to save memory, instead it points
107    /// to the slot ID.
108    key_map: hashbrown::HashTable<Id>,
109
110    /// An intrusive linked list for LRU.
111    lru: LinkedList<ValueAdapter<C>>,
112}
113
114impl<C: Configuration> Default for IngredientShard<C> {
115    fn default() -> Self {
116        Self {
117            lru: LinkedList::default(),
118            key_map: hashbrown::HashTable::new(),
119        }
120    }
121}
122
123// SAFETY: `LinkedListLink` is `!Sync`, however, the linked list is only accessed through the
124// ingredient lock, and values are only ever linked to a single list on the ingredient.
125unsafe impl<C: Configuration> Sync for Value<C> {}
126
127intrusive_adapter!(ValueAdapter<C> = UnsafeRef<Value<C>>: Value<C> { link: LinkedListLink } where C: Configuration);
128
129/// Struct storing the interned fields.
130pub struct Value<C>
131where
132    C: Configuration,
133{
134    /// The index of the shard containing this value.
135    shard: u16,
136
137    /// An intrusive linked list for LRU.
138    link: LinkedListLink,
139
140    /// The interned fields for this value.
141    ///
142    /// These are valid for read-only access as long as the lock is held
143    /// or the value has been validated in the current revision.
144    fields: UnsafeCell<C::Fields<'static>>,
145
146    /// Memos attached to this interned value.
147    ///
148    /// This is valid for read-only access as long as the lock is held
149    /// or the value has been validated in the current revision.
150    memos: UnsafeCell<MemoTable>,
151
152    /// Data that can only be accessed while holding the lock for the
153    /// `key_map` shard containing the value ID.
154    shared: UnsafeCell<ValueShared>,
155}
156
157/// Shared value data can only be read through the lock.
158#[repr(Rust, packed)] // Allow `durability` to be stored in the padding of the outer `Value` struct.
159#[derive(Clone, Copy)]
160struct ValueShared {
161    /// The interned ID for this value.
162    ///
163    /// Storing this on the value itself is necessary to identify slots
164    /// from the LRU list, as well as keep track of the generation.
165    ///
166    /// Values that are reused increment the ID generation, as if they had
167    /// allocated a new slot. This eliminates the need for dependency edges
168    /// on queries that *read* from an interned value, as any memos dependent
169    /// on the previous value will not match the new ID.
170    ///
171    /// However, reusing a slot invalidates the previous ID, so dependency edges
172    /// on queries that *create* an interned value are still required to ensure
173    /// the value is re-interned with a new ID.
174    id: Id,
175
176    /// The revision the value was most-recently interned in.
177    last_interned_at: Revision,
178
179    /// The minimum durability of all inputs consumed by the creator
180    /// query prior to creating this interned struct. If any of those
181    /// inputs changes, then the creator query may create this struct
182    /// with different values.
183    durability: Durability,
184}
185
186impl ValueShared {
187    /// Returns `true` if this value slot can be reused when interning, and should be added to the LRU.
188    fn is_reusable<C: Configuration>(&self) -> bool {
189        // Garbage collection is disabled.
190        if C::REVISIONS == IMMORTAL {
191            return false;
192        }
193
194        // Collecting higher durability values requires invalidating the revision for their
195        // durability (see `Database::synthetic_write`, which requires a mutable reference to
196        // the database) to avoid short-circuiting calls to `maybe_changed_after`. This is
197        // necessary because `maybe_changed_after` for interned values is not "pure"; it updates
198        // the `last_interned_at` field before validating a given value to ensure that it is not
199        // reused after read in the current revision.
200        self.durability == Durability::LOW
201    }
202}
203
204impl<C> Value<C>
205where
206    C: Configuration,
207{
208    /// Fields of this interned struct.
209    #[cfg(feature = "salsa_unstable")]
210    pub fn fields(&self) -> &C::Fields<'static> {
211        // SAFETY: The fact that this function is safe is technically unsound. However, interned
212        // values are only exposed if they have been validated in the current revision, which
213        // ensures that they are not reused while being accessed.
214        unsafe { &*self.fields.get() }
215    }
216
217    /// Returns memory usage information about the interned value.
218    ///
219    /// # Safety
220    ///
221    /// The `MemoTable` must belong to a `Value` of the correct type. Additionally, the
222    /// lock must be held for the shard containing the value.
223    #[cfg(all(not(feature = "shuttle"), feature = "salsa_unstable"))]
224    unsafe fn memory_usage(&self, memo_table_types: &MemoTableTypes) -> crate::database::SlotInfo {
225        let heap_size = C::heap_size(self.fields());
226        // SAFETY: The caller guarantees we hold the lock for the shard containing the value, so we
227        // have at-least read-only access to the value's memos.
228        let memos = unsafe { &*self.memos.get() };
229        // SAFETY: The caller guarantees this is the correct types table.
230        let memos = unsafe { memo_table_types.attach_memos(memos) };
231
232        crate::database::SlotInfo {
233            debug_name: C::DEBUG_NAME,
234            size_of_metadata: std::mem::size_of::<Self>() - std::mem::size_of::<C::Fields<'_>>(),
235            size_of_fields: std::mem::size_of::<C::Fields<'_>>(),
236            heap_size_of_fields: heap_size,
237            memos: memos.memory_usage(),
238        }
239    }
240}
241
242impl<C: Configuration> Default for JarImpl<C> {
243    fn default() -> Self {
244        Self {
245            phantom: PhantomData,
246        }
247    }
248}
249
250impl<C: Configuration> Jar for JarImpl<C> {
251    fn create_ingredients(
252        _zalsa: &mut Zalsa,
253        first_index: IngredientIndex,
254    ) -> Vec<Box<dyn Ingredient>> {
255        vec![Box::new(IngredientImpl::<C>::new(first_index)) as _]
256    }
257
258    fn id_struct_type_id() -> TypeId {
259        TypeId::of::<C::Struct<'static>>()
260    }
261}
262
263impl<C> IngredientImpl<C>
264where
265    C: Configuration,
266{
267    pub fn new(ingredient_index: IngredientIndex) -> Self {
268        static SHARDS: OnceLock<usize> = OnceLock::new();
269        let shards = *SHARDS.get_or_init(|| {
270            let num_cpus = std::thread::available_parallelism()
271                .map(usize::from)
272                .unwrap_or(1);
273
274            (num_cpus * 4).next_power_of_two()
275        });
276
277        Self {
278            ingredient_index,
279            hasher: FxBuildHasher,
280            memo_table_types: Arc::new(MemoTableTypes::default()),
281            revision_queue: RevisionQueue::default(),
282            shift: usize::BITS - shards.trailing_zeros(),
283            shards: (0..shards).map(|_| Default::default()).collect(),
284            _marker: PhantomData,
285        }
286    }
287
288    /// Returns the shard for a given hash.
289    ///
290    /// Note that this value is guaranteed to be in-bounds for `self.shards`.
291    #[inline]
292    fn shard(&self, hash: u64) -> usize {
293        // https://github.com/xacrimon/dashmap/blob/366ce7e7872866a06de66eb95002fa6cf2c117a7/src/lib.rs#L421
294        ((hash as usize) << 7) >> self.shift
295    }
296
297    /// # Safety
298    ///
299    /// The `from_internal_data` function must be called to restore the correct lifetime
300    /// before access.
301    unsafe fn to_internal_data<'db>(&'db self, data: C::Fields<'db>) -> C::Fields<'static> {
302        // SAFETY: Guaranteed by caller.
303        unsafe { std::mem::transmute(data) }
304    }
305
306    fn from_internal_data<'db>(data: &'db C::Fields<'static>) -> &'db C::Fields<'db> {
307        // SAFETY: It's sound to go from `Data<'static>` to `Data<'db>`. We shrink the
308        // lifetime here to use a single lifetime in `Lookup::eq(&StructKey<'db>, &C::Data<'db>)`
309        unsafe { std::mem::transmute(data) }
310    }
311
312    /// Intern data to a unique reference.
313    ///
314    /// If `key` is already interned, returns the existing [`Id`] for the interned data without
315    /// invoking `assemble`.
316    ///
317    /// Otherwise, invokes `assemble` with the given `key` and the [`Id`] to be allocated for this
318    /// interned value. The resulting [`C::Data`] will then be interned.
319    ///
320    /// Note: Using the database within the `assemble` function may result in a deadlock if
321    /// the database ends up trying to intern or allocate a new value.
322    pub fn intern<'db, Key>(
323        &'db self,
324        zalsa: &'db Zalsa,
325        zalsa_local: &'db ZalsaLocal,
326        key: Key,
327        assemble: impl FnOnce(Id, Key) -> C::Fields<'db>,
328    ) -> C::Struct<'db>
329    where
330        Key: Hash,
331        C::Fields<'db>: HashEqLike<Key>,
332    {
333        FromId::from_id(self.intern_id(zalsa, zalsa_local, key, assemble))
334    }
335
336    /// Intern data to a unique reference.
337    ///
338    /// If `key` is already interned, returns the existing [`Id`] for the interned data without
339    /// invoking `assemble`.
340    ///
341    /// Otherwise, invokes `assemble` with the given `key` and the [`Id`] to be allocated for this
342    /// interned value. The resulting [`C::Data`] will then be interned.
343    ///
344    /// Note: Using the database within the `assemble` function may result in a deadlock if
345    /// the database ends up trying to intern or allocate a new value.
346    pub fn intern_id<'db, Key>(
347        &'db self,
348        zalsa: &'db Zalsa,
349        zalsa_local: &'db ZalsaLocal,
350        key: Key,
351        assemble: impl FnOnce(Id, Key) -> C::Fields<'db>,
352    ) -> crate::Id
353    where
354        Key: Hash,
355        // We'd want the following predicate, but this currently implies `'static` due to a rustc
356        // bug
357        // for<'db> C::Data<'db>: HashEqLike<Key>,
358        // so instead we go with this and transmute the lifetime in the `eq` closure
359        C::Fields<'db>: HashEqLike<Key>,
360    {
361        // Record the current revision as active.
362        let current_revision = zalsa.current_revision();
363        self.revision_queue.record(current_revision);
364
365        // Hash the value before acquiring the lock.
366        let hash = self.hasher.hash_one(&key);
367
368        let shard_index = self.shard(hash);
369        // SAFETY: `shard_index` is guaranteed to be in-bounds for `self.shards`.
370        let shard = unsafe { &mut *self.shards.get_unchecked(shard_index).lock() };
371
372        let found_value = Cell::new(None);
373        // SAFETY: We hold the lock for the shard containing the value.
374        let eq = |id: &_| unsafe { Self::value_eq(*id, &key, zalsa, &found_value) };
375
376        // Attempt a fast-path lookup of already interned data.
377        if let Some(&id) = shard.key_map.find(hash, eq) {
378            let value = found_value
379                .get()
380                .expect("found the interned value, so `found_value` should be set");
381
382            let index = self.database_key_index(id);
383
384            // SAFETY: We hold the lock for the shard containing the value.
385            let value_shared = unsafe { &mut *value.shared.get() };
386
387            // Validate the value in this revision to avoid reuse.
388            if { value_shared.last_interned_at } < current_revision {
389                value_shared.last_interned_at = current_revision;
390
391                zalsa.event(&|| {
392                    Event::new(EventKind::DidValidateInternedValue {
393                        key: index,
394                        revision: current_revision,
395                    })
396                });
397
398                if value_shared.is_reusable::<C>() {
399                    // Move the value to the front of the LRU list.
400                    //
401                    // SAFETY: We hold the lock for the shard containing the value, and `value` is
402                    // a reusable value that was previously interned, so is in the list.
403                    unsafe { shard.lru.cursor_mut_from_ptr(value).remove() };
404
405                    // SAFETY: The value pointer is valid for the lifetime of the database
406                    // and never accessed mutably directly.
407                    unsafe { shard.lru.push_front(UnsafeRef::from_raw(value)) };
408                }
409            }
410
411            if let Some((_, stamp)) = zalsa_local.active_query() {
412                let was_reusable = value_shared.is_reusable::<C>();
413
414                // Record the maximum durability across all queries that intern this value.
415                value_shared.durability = std::cmp::max(value_shared.durability, stamp.durability);
416
417                // If the value is no longer reusable, i.e. the durability increased, remove it
418                // from the LRU.
419                if was_reusable && !value_shared.is_reusable::<C>() {
420                    // SAFETY: We hold the lock for the shard containing the value, and `value`
421                    // was previously reusable, so is in the list.
422                    unsafe { shard.lru.cursor_mut_from_ptr(value).remove() };
423                }
424            }
425
426            // Record a dependency on the value.
427            //
428            // See `intern_id_cold` for why we need to use `current_revision` here. Note that just
429            // because this value was previously interned does not mean it was previously interned
430            // by *our query*, so the same considerations apply.
431            zalsa_local.report_tracked_read_simple(
432                index,
433                value_shared.durability,
434                current_revision,
435            );
436
437            return value_shared.id;
438        }
439
440        // Fill up the table for the first few revisions without attempting garbage collection.
441        if !self.revision_queue.is_primed() {
442            return self.intern_id_cold(
443                key,
444                zalsa,
445                zalsa_local,
446                assemble,
447                shard,
448                shard_index,
449                hash,
450            );
451        }
452
453        // Otherwise, try to reuse a stale slot.
454        let mut cursor = shard.lru.back_mut();
455
456        while let Some(value) = cursor.get() {
457            // SAFETY: We hold the lock for the shard containing the value.
458            let value_shared = unsafe { &mut *value.shared.get() };
459
460            // The value must not have been read in the current revision to be collected
461            // soundly, but we also do not want to collect values that have been read recently.
462            //
463            // Note that the list is sorted by LRU, so if the tail of the list is not stale, we
464            // will not find any stale slots.
465            if !self.revision_queue.is_stale(value_shared.last_interned_at) {
466                break;
467            }
468
469            // We should never reuse a value that was accessed in the current revision.
470            debug_assert!({ value_shared.last_interned_at } < current_revision);
471
472            // Record the durability of the current query on the interned value.
473            let (durability, last_interned_at) = zalsa_local
474                .active_query()
475                .map(|(_, stamp)| (stamp.durability, current_revision))
476                // If there is no active query this durability does not actually matter.
477                // `last_interned_at` needs to be `Revision::MAX`, see the `intern_access_in_different_revision` test.
478                .unwrap_or((Durability::MAX, Revision::max()));
479
480            let old_id = value_shared.id;
481
482            // Increment the generation of the ID, as if we allocated a new slot.
483            //
484            // If the ID is at its maximum generation, we are forced to leak the slot.
485            let Some(new_id) = value_shared.id.next_generation() else {
486                // Remove the value from the LRU list as we will never be able to
487                // collect it.
488                cursor.remove().unwrap();
489
490                // Retry with the previous element.
491                cursor = shard.lru.back_mut();
492
493                continue;
494            };
495
496            // Mark the slot as reused.
497            *value_shared = ValueShared {
498                id: new_id,
499                durability,
500                last_interned_at,
501            };
502
503            let index = self.database_key_index(value_shared.id);
504
505            // Record a dependency on the new value.
506            //
507            // See `intern_id_cold` for why we need to use `current_revision` here.
508            zalsa_local.report_tracked_read_simple(
509                index,
510                value_shared.durability,
511                current_revision,
512            );
513
514            zalsa.event(&|| {
515                Event::new(EventKind::DidReuseInternedValue {
516                    key: index,
517                    revision: current_revision,
518                })
519            });
520
521            // Remove the value from the LRU list.
522            //
523            // SAFETY: The value pointer is valid for the lifetime of the database.
524            let value = unsafe { &*UnsafeRef::into_raw(cursor.remove().unwrap()) };
525
526            // SAFETY: We hold the lock for the shard containing the value, and the
527            // value has not been interned in the current revision, so no references to
528            // it can exist.
529            let old_fields = unsafe { &mut *value.fields.get() };
530
531            // Remove the previous value from the ID map.
532            //
533            // Note that while the ID stays the same when a slot is reused, the fields,
534            // and thus the hash, will change, so we need to re-insert the value into the
535            // map. Crucially, we know that the hashes for the old and new fields both map
536            // to the same shard, because we determined the initial shard based on the new
537            // fields and only accessed the LRU list for that shard.
538            let old_hash = self.hasher.hash_one(&*old_fields);
539            shard
540                .key_map
541                .find_entry(old_hash, |found_id: &Id| *found_id == old_id)
542                .expect("interned value in LRU so must be in key_map")
543                .remove();
544
545            // Update the fields.
546            //
547            // SAFETY: We call `from_internal_data` to restore the correct lifetime before access.
548            *old_fields = unsafe { self.to_internal_data(assemble(new_id, key)) };
549
550            // SAFETY: We hold the lock for the shard containing the value.
551            let hasher = |id: &_| unsafe { self.value_hash(*id, zalsa) };
552
553            // Insert the new value into the ID map.
554            shard.key_map.insert_unique(hash, new_id, hasher);
555
556            // SAFETY: We hold the lock for the shard containing the value, and the
557            // value has not been interned in the current revision, so no references to
558            // it can exist.
559            let memo_table = unsafe { &mut *value.memos.get() };
560
561            // Free the memos associated with the previous interned value.
562            //
563            // SAFETY: The memo table belongs to a value that we allocated, so it has the
564            // correct type.
565            unsafe { self.clear_memos(zalsa, memo_table, new_id) };
566
567            if value_shared.is_reusable::<C>() {
568                // Move the value to the front of the LRU list.
569                //
570                // SAFETY: The value pointer is valid for the lifetime of the database.
571                // and never accessed mutably directly.
572                shard.lru.push_front(unsafe { UnsafeRef::from_raw(value) });
573            }
574
575            return new_id;
576        }
577
578        // If we could not find any stale slots, we are forced to allocate a new one.
579        self.intern_id_cold(key, zalsa, zalsa_local, assemble, shard, shard_index, hash)
580    }
581
582    /// The cold path for interning a value, allocating a new slot.
583    ///
584    /// Returns `true` if the current thread interned the value.
585    #[allow(clippy::too_many_arguments)]
586    fn intern_id_cold<'db, Key>(
587        &'db self,
588        key: Key,
589        zalsa: &Zalsa,
590        zalsa_local: &ZalsaLocal,
591        assemble: impl FnOnce(Id, Key) -> C::Fields<'db>,
592        shard: &mut IngredientShard<C>,
593        shard_index: usize,
594        hash: u64,
595    ) -> crate::Id
596    where
597        Key: Hash,
598        C::Fields<'db>: HashEqLike<Key>,
599    {
600        let current_revision = zalsa.current_revision();
601
602        // Record the durability of the current query on the interned value.
603        let (durability, last_interned_at) = zalsa_local
604            .active_query()
605            .map(|(_, stamp)| (stamp.durability, current_revision))
606            // If there is no active query this durability does not actually matter.
607            // `last_interned_at` needs to be `Revision::MAX`, see the `intern_access_in_different_revision` test.
608            .unwrap_or((Durability::MAX, Revision::max()));
609
610        // Allocate the value slot.
611        let (id, value) = zalsa_local.allocate(zalsa, self.ingredient_index, |id| Value::<C> {
612            shard: shard_index as u16,
613            link: LinkedListLink::new(),
614            // SAFETY: We only ever access the memos of a value that we allocated through
615            // our `MemoTableTypes`.
616            memos: UnsafeCell::new(unsafe { MemoTable::new(self.memo_table_types()) }),
617            // SAFETY: We call `from_internal_data` to restore the correct lifetime before access.
618            fields: UnsafeCell::new(unsafe { self.to_internal_data(assemble(id, key)) }),
619            shared: UnsafeCell::new(ValueShared {
620                id,
621                durability,
622                last_interned_at,
623            }),
624        });
625
626        // Insert the newly allocated ID.
627        self.insert_id(id, zalsa, shard, hash, value);
628
629        let index = self.database_key_index(id);
630
631        // Record a dependency on the newly interned value.
632        //
633        // Note that the ID is unique to this use of the interned slot, so it seems logical to use
634        // `Revision::start()` here. However, it is possible that the ID we read is different from
635        // the previous execution of this query if the previous slot has been reused. In that case,
636        // the query has changed without a corresponding input changing. Using `current_revision`
637        // for dependencies on interned values encodes the fact that interned IDs are not stable
638        // across revisions.
639        zalsa_local.report_tracked_read_simple(index, durability, current_revision);
640
641        zalsa.event(&|| {
642            Event::new(EventKind::DidInternValue {
643                key: index,
644                revision: current_revision,
645            })
646        });
647
648        id
649    }
650
651    /// Inserts a newly interned value ID into the LRU list and key map.
652    fn insert_id(
653        &self,
654        id: Id,
655        zalsa: &Zalsa,
656        shard: &mut IngredientShard<C>,
657        hash: u64,
658        value: &Value<C>,
659    ) {
660        // SAFETY: We hold the lock for the shard containing the value.
661        let value_shared = unsafe { &mut *value.shared.get() };
662
663        if value_shared.is_reusable::<C>() {
664            // Add the value to the front of the LRU list.
665            //
666            // SAFETY: The value pointer is valid for the lifetime of the database
667            // and never accessed mutably directly.
668            shard.lru.push_front(unsafe { UnsafeRef::from_raw(value) });
669        }
670
671        // SAFETY: We hold the lock for the shard containing the value.
672        let hasher = |id: &_| unsafe { self.value_hash(*id, zalsa) };
673
674        // Insert the value into the ID map.
675        shard.key_map.insert_unique(hash, id, hasher);
676
677        debug_assert_eq!(hash, {
678            let value = zalsa.table().get::<Value<C>>(id);
679
680            // SAFETY: We hold the lock for the shard containing the value.
681            unsafe { self.hasher.hash_one(&*value.fields.get()) }
682        });
683    }
684
685    /// Clears the given memo table.
686    ///
687    /// # Safety
688    ///
689    /// The `MemoTable` must belong to a `Value` of the correct type.
690    pub(crate) unsafe fn clear_memos(&self, zalsa: &Zalsa, memo_table: &mut MemoTable, id: Id) {
691        // SAFETY: The caller guarantees this is the correct types table.
692        let table = unsafe { self.memo_table_types.attach_memos_mut(memo_table) };
693
694        // `Database::salsa_event` is a user supplied callback which may panic
695        // in that case we need a drop guard to free the memo table
696        struct TableDropGuard<'a>(MemoTableWithTypesMut<'a>);
697
698        impl Drop for TableDropGuard<'_> {
699            fn drop(&mut self) {
700                // SAFETY: We have `&mut MemoTable`, so no more references to these memos exist and we are good
701                // to drop them.
702                unsafe { self.0.drop() };
703            }
704        }
705
706        let mut table_guard = TableDropGuard(table);
707
708        // SAFETY: We have `&mut MemoTable`, so no more references to these memos exist and we are good
709        // to drop them.
710        unsafe {
711            table_guard.0.take_memos(|memo_ingredient_index, memo| {
712                let ingredient_index =
713                    zalsa.ingredient_index_for_memo(self.ingredient_index, memo_ingredient_index);
714
715                let executor = DatabaseKeyIndex::new(ingredient_index, id);
716
717                zalsa.event(&|| Event::new(EventKind::DidDiscard { key: executor }));
718
719                memo.remove_outputs(zalsa, executor);
720            })
721        };
722
723        std::mem::forget(table_guard);
724
725        // Reset the table after having dropped any memos.
726        memo_table.reset();
727    }
728
729    // Hashes the value by its fields.
730    //
731    // # Safety
732    //
733    // The lock must be held for the shard containing the value.
734    unsafe fn value_hash<'db>(&'db self, id: Id, zalsa: &'db Zalsa) -> u64 {
735        // This closure is only called if the table is resized. So while it's expensive
736        // to lookup all values, it will only happen rarely.
737        let value = zalsa.table().get::<Value<C>>(id);
738
739        // SAFETY: We hold the lock for the shard containing the value.
740        unsafe { self.hasher.hash_one(&*value.fields.get()) }
741    }
742
743    // Compares the value by its fields to the given key.
744    //
745    // # Safety
746    //
747    // The lock must be held for the shard containing the value.
748    unsafe fn value_eq<'db, Key>(
749        id: Id,
750        key: &Key,
751        zalsa: &'db Zalsa,
752        found_value: &Cell<Option<&'db Value<C>>>,
753    ) -> bool
754    where
755        C::Fields<'db>: HashEqLike<Key>,
756    {
757        let value = zalsa.table().get::<Value<C>>(id);
758        found_value.set(Some(value));
759
760        // SAFETY: We hold the lock for the shard containing the value.
761        let fields = unsafe { &*value.fields.get() };
762
763        HashEqLike::eq(Self::from_internal_data(fields), key)
764    }
765
766    /// Returns the database key index for an interned value with the given id.
767    #[inline]
768    pub fn database_key_index(&self, id: Id) -> DatabaseKeyIndex {
769        DatabaseKeyIndex::new(self.ingredient_index, id)
770    }
771
772    /// Lookup the data for an interned value based on its ID.
773    pub fn data<'db>(&'db self, zalsa: &'db Zalsa, id: Id) -> &'db C::Fields<'db> {
774        let value = zalsa.table().get::<Value<C>>(id);
775
776        debug_assert!(
777            {
778                let _shard = self.shards[value.shard as usize].lock();
779
780                // SAFETY: We hold the lock for the shard containing the value.
781                let value_shared = unsafe { &mut *value.shared.get() };
782
783                let last_changed_revision = zalsa.last_changed_revision(value_shared.durability);
784                ({ value_shared.last_interned_at }) >= last_changed_revision
785            },
786            "Data was not interned in the latest revision for its durability."
787        );
788
789        // SAFETY: Interned values are only exposed if they have been validated in the
790        // current revision, as checked by the assertion above, which ensures that they
791        // are not reused while being accessed.
792        unsafe { Self::from_internal_data(&*value.fields.get()) }
793    }
794
795    /// Lookup the fields from an interned struct.
796    ///
797    /// Note that this is not "leaking" since no dependency edge is required.
798    pub fn fields<'db>(&'db self, zalsa: &'db Zalsa, s: C::Struct<'db>) -> &'db C::Fields<'db> {
799        self.data(zalsa, AsId::as_id(&s))
800    }
801
802    pub fn reset(&mut self, zalsa_mut: &mut Zalsa) {
803        _ = zalsa_mut;
804
805        for shard in self.shards.iter_mut() {
806            // We can clear the key maps now that we have cancelled all other handles.
807            shard.get_mut().key_map.clear();
808        }
809    }
810
811    /// Returns all data corresponding to the interned struct.
812    pub fn entries<'db>(&'db self, zalsa: &'db Zalsa) -> impl Iterator<Item = StructEntry<'db, C>> {
813        // SAFETY: `should_lock` is `true`
814        unsafe { self.entries_inner(true, zalsa) }
815    }
816
817    /// Returns all data corresponding to the interned struct.
818    ///
819    /// # Safety
820    ///
821    /// If `should_lock` is `false`, the caller *must* hold the locks for all shards
822    /// of the key map.
823    unsafe fn entries_inner<'db>(
824        &'db self,
825        should_lock: bool,
826        zalsa: &'db Zalsa,
827    ) -> impl Iterator<Item = StructEntry<'db, C>> {
828        // TODO: Grab all locks eagerly.
829        zalsa.table().slots_of::<Value<C>>().map(move |(_, value)| {
830            if should_lock {
831                // SAFETY: `value.shard` is guaranteed to be in-bounds for `self.shards`.
832                let _shard = unsafe { self.shards.get_unchecked(value.shard as usize) }.lock();
833            }
834
835            // SAFETY: The caller guarantees we hold the lock for the shard containing the value.
836            //
837            // Note that this ID includes the generation, unlike the ID provided by the table.
838            let id = unsafe { (*value.shared.get()).id };
839
840            StructEntry {
841                value,
842                key: self.database_key_index(id),
843            }
844        })
845    }
846}
847
848/// An interned struct entry.
849pub struct StructEntry<'db, C>
850where
851    C: Configuration,
852{
853    value: &'db Value<C>,
854    key: DatabaseKeyIndex,
855}
856
857impl<'db, C> StructEntry<'db, C>
858where
859    C: Configuration,
860{
861    /// Returns the `DatabaseKeyIndex` for this entry.
862    pub fn key(&self) -> DatabaseKeyIndex {
863        self.key
864    }
865
866    /// Returns the interned struct.
867    pub fn as_struct(&self) -> C::Struct<'_> {
868        FromId::from_id(self.key.key_index())
869    }
870
871    #[cfg(feature = "salsa_unstable")]
872    pub fn value(&self) -> &'db Value<C> {
873        self.value
874    }
875}
876
877impl<C> Ingredient for IngredientImpl<C>
878where
879    C: Configuration,
880{
881    fn location(&self) -> &'static crate::ingredient::Location {
882        &C::LOCATION
883    }
884
885    fn ingredient_index(&self) -> IngredientIndex {
886        self.ingredient_index
887    }
888
889    unsafe fn maybe_changed_after(
890        &self,
891        zalsa: &crate::zalsa::Zalsa,
892        _db: crate::database::RawDatabase<'_>,
893        input: Id,
894        _revision: Revision,
895        _cycle_heads: &mut VerifyCycleHeads,
896    ) -> VerifyResult {
897        // Record the current revision as active.
898        let current_revision = zalsa.current_revision();
899        self.revision_queue.record(current_revision);
900
901        let value = zalsa.table().get::<Value<C>>(input);
902
903        // SAFETY: `value.shard` is guaranteed to be in-bounds for `self.shards`.
904        let _shard = unsafe { self.shards.get_unchecked(value.shard as usize) }.lock();
905
906        // SAFETY: We hold the lock for the shard containing the value.
907        let value_shared = unsafe { &mut *value.shared.get() };
908
909        // The slot was reused.
910        if value_shared.id.generation() > input.generation() {
911            return VerifyResult::changed();
912        }
913
914        // Validate the value for the current revision to avoid reuse.
915        value_shared.last_interned_at = current_revision;
916
917        zalsa.event(&|| {
918            let index = self.database_key_index(input);
919
920            Event::new(EventKind::DidValidateInternedValue {
921                key: index,
922                revision: current_revision,
923            })
924        });
925
926        // Any change to an interned value results in a new ID generation.
927        VerifyResult::unchanged()
928    }
929
930    fn collect_minimum_serialized_edges(
931        &self,
932        _zalsa: &Zalsa,
933        edge: QueryEdge,
934        serialized_edges: &mut FxIndexSet<QueryEdge>,
935        _visited_edges: &mut FxHashSet<QueryEdge>,
936    ) {
937        if C::PERSIST && C::REVISIONS != IMMORTAL {
938            // If the interned struct is being persisted, it may be reachable through transitive queries.
939            // Additionally, interned struct dependencies are impure in that garbage collection can
940            // invalidate a dependency without a base input necessarily being updated. Thus, we must
941            // preserve the transitive dependency on the interned struct, if garbage collection is
942            // enabled.
943            serialized_edges.insert(edge);
944        }
945
946        // Otherwise, the dependency is covered by the base inputs.
947    }
948
949    fn debug_name(&self) -> &'static str {
950        C::DEBUG_NAME
951    }
952
953    fn jar_kind(&self) -> JarKind {
954        JarKind::Struct
955    }
956
957    fn memo_table_types(&self) -> &Arc<MemoTableTypes> {
958        &self.memo_table_types
959    }
960
961    fn memo_table_types_mut(&mut self) -> &mut Arc<MemoTableTypes> {
962        &mut self.memo_table_types
963    }
964
965    /// Returns memory usage information about any interned values.
966    #[cfg(all(not(feature = "shuttle"), feature = "salsa_unstable"))]
967    fn memory_usage(&self, db: &dyn crate::Database) -> Option<Vec<crate::database::SlotInfo>> {
968        use parking_lot::lock_api::RawMutex;
969
970        for shard in self.shards.iter() {
971            // SAFETY: We do not hold any active mutex guards.
972            unsafe { shard.raw().lock() };
973        }
974
975        // SAFETY: We hold the locks for all shards.
976        let entries = unsafe { self.entries_inner(false, db.zalsa()) };
977
978        let memory_usage = entries
979            // SAFETY: The memo table belongs to a value that we allocated, so it
980            // has the correct type. Additionally, we are holding the locks for all shards.
981            .map(|entry| unsafe { entry.value.memory_usage(&self.memo_table_types) })
982            .collect();
983
984        for shard in self.shards.iter() {
985            // SAFETY: We acquired the locks for all shards.
986            unsafe { shard.raw().unlock() };
987        }
988
989        Some(memory_usage)
990    }
991
992    fn is_persistable(&self) -> bool {
993        C::PERSIST
994    }
995
996    fn should_serialize(&self, zalsa: &Zalsa) -> bool {
997        C::PERSIST && self.entries(zalsa).next().is_some()
998    }
999
1000    #[cfg(feature = "persistence")]
1001    unsafe fn serialize<'db>(
1002        &'db self,
1003        zalsa: &'db Zalsa,
1004        f: &mut dyn FnMut(&dyn erased_serde::Serialize),
1005    ) {
1006        f(&persistence::SerializeIngredient {
1007            zalsa,
1008            ingredient: self,
1009        })
1010    }
1011
1012    #[cfg(feature = "persistence")]
1013    fn deserialize(
1014        &mut self,
1015        zalsa: &mut Zalsa,
1016        deserializer: &mut dyn erased_serde::Deserializer,
1017    ) -> Result<(), erased_serde::Error> {
1018        let deserialize = persistence::DeserializeIngredient {
1019            zalsa,
1020            ingredient: self,
1021        };
1022
1023        serde::de::DeserializeSeed::deserialize(deserialize, deserializer)
1024    }
1025}
1026
1027impl<C> std::fmt::Debug for IngredientImpl<C>
1028where
1029    C: Configuration,
1030{
1031    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1032        f.debug_struct(std::any::type_name::<Self>())
1033            .field("index", &self.ingredient_index)
1034            .finish()
1035    }
1036}
1037
1038// SAFETY: `Value<C>` is our private type branded over the unique configuration `C`.
1039unsafe impl<C> Slot for Value<C>
1040where
1041    C: Configuration,
1042{
1043    #[inline(always)]
1044    unsafe fn memos(&self, _current_revision: Revision) -> &MemoTable {
1045        // SAFETY: The fact that we have a reference to the `Value` means it must
1046        // have been interned, and thus validated, in the current revision.
1047        unsafe { &*self.memos.get() }
1048    }
1049
1050    #[inline(always)]
1051    fn memos_mut(&mut self) -> &mut MemoTable {
1052        self.memos.get_mut()
1053    }
1054}
1055
1056/// Keep track of revisions in which interned values were read, to determine staleness.
1057///
1058/// An interned value is considered stale if it has not been read in the past `REVS`
1059/// revisions. However, we only consider revisions in which interned values were actually
1060/// read, as revisions may be created in bursts.
1061struct RevisionQueue<C> {
1062    lock: Mutex<()>,
1063    // Once `feature(generic_const_exprs)` is stable this can just be an array.
1064    revisions: Box<[AtomicRevision]>,
1065    _configuration: PhantomData<fn() -> C>,
1066}
1067
1068// `#[salsa::interned(revisions = usize::MAX)]` disables garbage collection.
1069const IMMORTAL: NonZeroUsize = NonZeroUsize::MAX;
1070
1071impl<C: Configuration> Default for RevisionQueue<C> {
1072    fn default() -> RevisionQueue<C> {
1073        let revisions = if C::REVISIONS == IMMORTAL {
1074            Box::default()
1075        } else {
1076            (0..C::REVISIONS.get())
1077                .map(|_| AtomicRevision::start())
1078                .collect()
1079        };
1080
1081        RevisionQueue {
1082            lock: Mutex::new(()),
1083            revisions,
1084            _configuration: PhantomData,
1085        }
1086    }
1087}
1088
1089impl<C: Configuration> RevisionQueue<C> {
1090    /// Record the given revision as active.
1091    #[inline]
1092    fn record(&self, revision: Revision) {
1093        // Garbage collection is disabled.
1094        if C::REVISIONS == IMMORTAL {
1095            return;
1096        }
1097
1098        // Fast-path: We already recorded this revision.
1099        if self.revisions[0].load() >= revision {
1100            return;
1101        }
1102
1103        self.record_cold(revision);
1104    }
1105
1106    #[cold]
1107    fn record_cold(&self, revision: Revision) {
1108        let _lock = self.lock.lock();
1109
1110        // Otherwise, update the queue, maintaining sorted order.
1111        //
1112        // Note that this should only happen once per revision.
1113        for i in (1..C::REVISIONS.get()).rev() {
1114            self.revisions[i].store(self.revisions[i - 1].load());
1115        }
1116
1117        self.revisions[0].store(revision);
1118    }
1119
1120    /// Returns `true` if the given revision is old enough to be considered stale.
1121    #[inline]
1122    fn is_stale(&self, revision: Revision) -> bool {
1123        // Garbage collection is disabled.
1124        if C::REVISIONS == IMMORTAL {
1125            return false;
1126        }
1127
1128        let oldest = self.revisions[C::REVISIONS.get() - 1].load();
1129
1130        // If we have not recorded `REVS` revisions yet, nothing can be stale.
1131        if oldest == Revision::start() {
1132            return false;
1133        }
1134
1135        revision < oldest
1136    }
1137
1138    /// Returns `true` if `C::REVISIONS` revisions have been recorded as active,
1139    /// i.e. enough data has been recorded to start garbage collection.
1140    #[inline]
1141    fn is_primed(&self) -> bool {
1142        // Garbage collection is disabled.
1143        if C::REVISIONS == IMMORTAL {
1144            return false;
1145        }
1146
1147        self.revisions[C::REVISIONS.get() - 1].load() > Revision::start()
1148    }
1149}
1150
1151/// A trait for types that hash and compare like `O`.
1152pub trait HashEqLike<O> {
1153    fn hash<H: Hasher>(&self, h: &mut H);
1154    fn eq(&self, data: &O) -> bool;
1155}
1156
1157/// The `Lookup` trait is a more flexible variant on [`std::borrow::Borrow`]
1158/// and [`std::borrow::ToOwned`].
1159///
1160/// It is implemented by "some type that can be used as the lookup key for `O`".
1161/// This means that `self` can be hashed and compared for equality with values
1162/// of type `O` without actually creating an owned value. It `self` needs to be interned,
1163/// it can be converted into an equivalent value of type `O`.
1164///
1165/// The canonical example is `&str: Lookup<String>`. However, this example
1166/// alone can be handled by [`std::borrow::Borrow`][]. In our case, we may have
1167/// multiple keys accumulated into a struct, like `ViewStruct: Lookup<(K1, ...)>`,
1168/// where `struct ViewStruct<L1: Lookup<K1>...>(K1...)`. The `Borrow` trait
1169/// requires that `&(K1...)` be convertible to `&ViewStruct` which just isn't
1170/// possible. `Lookup` instead offers direct `hash` and `eq` methods.
1171pub trait Lookup<O> {
1172    fn into_owned(self) -> O;
1173}
1174
1175impl<T> Lookup<T> for T {
1176    fn into_owned(self) -> T {
1177        self
1178    }
1179}
1180
1181impl<T> HashEqLike<T> for T
1182where
1183    T: Hash + Eq,
1184{
1185    fn hash<H: Hasher>(&self, h: &mut H) {
1186        Hash::hash(self, &mut *h);
1187    }
1188
1189    fn eq(&self, data: &T) -> bool {
1190        self == data
1191    }
1192}
1193
1194impl<T> HashEqLike<T> for &T
1195where
1196    T: Hash + Eq,
1197{
1198    fn hash<H: Hasher>(&self, h: &mut H) {
1199        Hash::hash(*self, &mut *h);
1200    }
1201
1202    fn eq(&self, data: &T) -> bool {
1203        **self == *data
1204    }
1205}
1206
1207impl<T> HashEqLike<&T> for T
1208where
1209    T: Hash + Eq,
1210{
1211    fn hash<H: Hasher>(&self, h: &mut H) {
1212        Hash::hash(self, &mut *h);
1213    }
1214
1215    fn eq(&self, data: &&T) -> bool {
1216        *self == **data
1217    }
1218}
1219
1220impl<T> Lookup<T> for &T
1221where
1222    T: Clone,
1223{
1224    fn into_owned(self) -> T {
1225        Clone::clone(self)
1226    }
1227}
1228
1229impl<'a, T> HashEqLike<&'a T> for Box<T>
1230where
1231    T: ?Sized + Hash + Eq,
1232    Box<T>: From<&'a T>,
1233{
1234    fn hash<H: Hasher>(&self, h: &mut H) {
1235        Hash::hash(self, &mut *h)
1236    }
1237    fn eq(&self, data: &&T) -> bool {
1238        **self == **data
1239    }
1240}
1241
1242impl<'a, T> Lookup<Box<T>> for &'a T
1243where
1244    T: ?Sized + Hash + Eq,
1245    Box<T>: From<&'a T>,
1246{
1247    fn into_owned(self) -> Box<T> {
1248        Box::from(self)
1249    }
1250}
1251
1252impl<'a, T> HashEqLike<&'a T> for Arc<T>
1253where
1254    T: ?Sized + Hash + Eq,
1255    Arc<T>: From<&'a T>,
1256{
1257    fn hash<H: Hasher>(&self, h: &mut H) {
1258        Hash::hash(&**self, &mut *h)
1259    }
1260    fn eq(&self, data: &&T) -> bool {
1261        **self == **data
1262    }
1263}
1264
1265impl<'a, T> Lookup<Arc<T>> for &'a T
1266where
1267    T: ?Sized + Hash + Eq,
1268    Arc<T>: From<&'a T>,
1269{
1270    fn into_owned(self) -> Arc<T> {
1271        Arc::from(self)
1272    }
1273}
1274
1275impl Lookup<String> for &str {
1276    fn into_owned(self) -> String {
1277        self.to_owned()
1278    }
1279}
1280
1281#[cfg(feature = "compact_str")]
1282impl Lookup<compact_str::CompactString> for &str {
1283    fn into_owned(self) -> compact_str::CompactString {
1284        compact_str::CompactString::new(self)
1285    }
1286}
1287
1288impl HashEqLike<&str> for String {
1289    fn hash<H: Hasher>(&self, h: &mut H) {
1290        Hash::hash(self, &mut *h)
1291    }
1292
1293    fn eq(&self, data: &&str) -> bool {
1294        self == *data
1295    }
1296}
1297
1298#[cfg(feature = "compact_str")]
1299impl HashEqLike<&str> for compact_str::CompactString {
1300    fn hash<H: Hasher>(&self, h: &mut H) {
1301        Hash::hash(self, &mut *h)
1302    }
1303
1304    fn eq(&self, data: &&str) -> bool {
1305        self == *data
1306    }
1307}
1308
1309impl<A, T: Hash + Eq + PartialEq<A>> HashEqLike<&[A]> for Vec<T> {
1310    fn hash<H: Hasher>(&self, h: &mut H) {
1311        Hash::hash(self, h);
1312    }
1313
1314    fn eq(&self, data: &&[A]) -> bool {
1315        self.len() == data.len() && data.iter().enumerate().all(|(i, a)| &self[i] == a)
1316    }
1317}
1318
1319impl<A: Hash + Eq + PartialEq<T> + Clone + Lookup<T>, T> Lookup<Vec<T>> for &[A] {
1320    fn into_owned(self) -> Vec<T> {
1321        self.iter().map(|a| Lookup::into_owned(a.clone())).collect()
1322    }
1323}
1324
1325impl<const N: usize, A, T: Hash + Eq + PartialEq<A>> HashEqLike<[A; N]> for Vec<T> {
1326    fn hash<H: Hasher>(&self, h: &mut H) {
1327        Hash::hash(self, h);
1328    }
1329
1330    fn eq(&self, data: &[A; N]) -> bool {
1331        self.len() == data.len() && data.iter().enumerate().all(|(i, a)| &self[i] == a)
1332    }
1333}
1334
1335impl<const N: usize, A: Hash + Eq + PartialEq<T> + Clone + Lookup<T>, T> Lookup<Vec<T>> for [A; N] {
1336    fn into_owned(self) -> Vec<T> {
1337        self.into_iter()
1338            .map(|a| Lookup::into_owned(a.clone()))
1339            .collect()
1340    }
1341}
1342
1343impl HashEqLike<&Path> for PathBuf {
1344    fn hash<H: Hasher>(&self, h: &mut H) {
1345        Hash::hash(self, h);
1346    }
1347
1348    fn eq(&self, data: &&Path) -> bool {
1349        self == data
1350    }
1351}
1352
1353impl Lookup<PathBuf> for &Path {
1354    fn into_owned(self) -> PathBuf {
1355        self.to_owned()
1356    }
1357}
1358
1359#[cfg(feature = "persistence")]
1360mod persistence {
1361    use std::cell::UnsafeCell;
1362    use std::fmt;
1363    use std::hash::BuildHasher;
1364
1365    use intrusive_collections::LinkedListLink;
1366    use serde::ser::{SerializeMap, SerializeStruct};
1367    use serde::{de, Deserialize};
1368
1369    use super::{Configuration, IngredientImpl, Value, ValueShared};
1370    use crate::plumbing::Ingredient;
1371    use crate::table::memo::MemoTable;
1372    use crate::zalsa::Zalsa;
1373    use crate::{Durability, Id, Revision};
1374
1375    pub struct SerializeIngredient<'db, C>
1376    where
1377        C: Configuration,
1378    {
1379        pub zalsa: &'db Zalsa,
1380        pub ingredient: &'db IngredientImpl<C>,
1381    }
1382
1383    impl<C> serde::Serialize for SerializeIngredient<'_, C>
1384    where
1385        C: Configuration,
1386    {
1387        fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
1388        where
1389            S: serde::Serializer,
1390        {
1391            let Self { zalsa, ingredient } = *self;
1392
1393            let count = ingredient
1394                .shards
1395                .iter()
1396                .map(|shard| shard.lock().key_map.len())
1397                .sum();
1398
1399            let mut map = serializer.serialize_map(Some(count))?;
1400
1401            for (_, value) in zalsa.table().slots_of::<Value<C>>() {
1402                // SAFETY: The safety invariant of `Ingredient::serialize` ensures we have exclusive access
1403                // to the database.
1404                let id = unsafe { (*value.shared.get()).id };
1405
1406                map.serialize_entry(&id.as_bits(), value)?;
1407            }
1408
1409            map.end()
1410        }
1411    }
1412
1413    impl<C> serde::Serialize for Value<C>
1414    where
1415        C: Configuration,
1416    {
1417        fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
1418        where
1419            S: serde::Serializer,
1420        {
1421            let mut value = serializer.serialize_struct("Value,", 3)?;
1422
1423            let Value {
1424                fields,
1425                shared,
1426                shard: _,
1427                link: _,
1428                memos: _,
1429            } = self;
1430
1431            // SAFETY: The safety invariant of `Ingredient::serialize` ensures we have exclusive access
1432            // to the database.
1433            let fields = unsafe { &*fields.get() };
1434
1435            // SAFETY: The safety invariant of `Ingredient::serialize` ensures we have exclusive access
1436            // to the database.
1437            let ValueShared {
1438                durability,
1439                last_interned_at,
1440                id: _,
1441            } = unsafe { *shared.get() };
1442
1443            value.serialize_field("durability", &durability)?;
1444            value.serialize_field("last_interned_at", &last_interned_at)?;
1445            value.serialize_field("fields", &SerializeFields::<C>(fields))?;
1446
1447            value.end()
1448        }
1449    }
1450
1451    struct SerializeFields<'db, C: Configuration>(&'db C::Fields<'static>);
1452
1453    impl<C> serde::Serialize for SerializeFields<'_, C>
1454    where
1455        C: Configuration,
1456    {
1457        fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
1458        where
1459            S: serde::Serializer,
1460        {
1461            C::serialize(self.0, serializer)
1462        }
1463    }
1464
1465    pub struct DeserializeIngredient<'db, C>
1466    where
1467        C: Configuration,
1468    {
1469        pub zalsa: &'db mut Zalsa,
1470        pub ingredient: &'db mut IngredientImpl<C>,
1471    }
1472
1473    impl<'de, C> de::DeserializeSeed<'de> for DeserializeIngredient<'_, C>
1474    where
1475        C: Configuration,
1476    {
1477        type Value = ();
1478
1479        fn deserialize<D>(self, deserializer: D) -> Result<Self::Value, D::Error>
1480        where
1481            D: serde::Deserializer<'de>,
1482        {
1483            deserializer.deserialize_map(self)
1484        }
1485    }
1486
1487    impl<'de, C> de::Visitor<'de> for DeserializeIngredient<'_, C>
1488    where
1489        C: Configuration,
1490    {
1491        type Value = ();
1492
1493        fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
1494            formatter.write_str("a map")
1495        }
1496
1497        fn visit_map<M>(self, mut access: M) -> Result<Self::Value, M::Error>
1498        where
1499            M: de::MapAccess<'de>,
1500        {
1501            let DeserializeIngredient { zalsa, ingredient } = self;
1502
1503            while let Some((id, value)) = access.next_entry::<u64, DeserializeValue<C>>()? {
1504                let id = Id::from_bits(id);
1505                let (page_idx, _) = crate::table::split_id(id);
1506
1507                // Determine the value shard.
1508                let hash = ingredient.hasher.hash_one(&value.fields.0);
1509                let shard_index = ingredient.shard(hash);
1510
1511                // SAFETY: `shard_index` is guaranteed to be in-bounds for `self.shards`.
1512                let shard = unsafe { &mut *ingredient.shards.get_unchecked(shard_index).lock() };
1513
1514                let value = Value::<C> {
1515                    shard: shard_index as u16,
1516                    link: LinkedListLink::new(),
1517                    // SAFETY: We only ever access the memos of a value that we allocated through
1518                    // our `MemoTableTypes`.
1519                    memos: UnsafeCell::new(unsafe {
1520                        MemoTable::new(ingredient.memo_table_types())
1521                    }),
1522                    fields: UnsafeCell::new(value.fields.0),
1523                    shared: UnsafeCell::new(ValueShared {
1524                        id,
1525                        durability: value.durability,
1526                        last_interned_at: value.last_interned_at,
1527                    }),
1528                };
1529
1530                // Force initialize the relevant page.
1531                zalsa.table_mut().force_page::<Value<C>>(
1532                    page_idx,
1533                    ingredient.ingredient_index(),
1534                    ingredient.memo_table_types(),
1535                );
1536
1537                // Initialize the slot.
1538                //
1539                // SAFETY: We have a mutable reference to the database.
1540                let (allocated_id, value) = unsafe {
1541                    zalsa
1542                        .table()
1543                        .page(page_idx)
1544                        .allocate(page_idx, |_| value)
1545                        .unwrap_or_else(|_| panic!("serialized an invalid `Id`: {id:?}"))
1546                };
1547
1548                assert_eq!(
1549                    allocated_id.index(),
1550                    id.index(),
1551                    "values are serialized in allocation order"
1552                );
1553
1554                // Insert the newly allocated ID into our ingredient.
1555                ingredient.insert_id(id, zalsa, shard, hash, value);
1556            }
1557
1558            Ok(())
1559        }
1560    }
1561
1562    #[derive(Deserialize)]
1563    #[serde(rename = "Value")]
1564    pub struct DeserializeValue<C: Configuration> {
1565        durability: Durability,
1566        last_interned_at: Revision,
1567        #[serde(bound = "C: Configuration")]
1568        fields: DeserializeFields<C>,
1569    }
1570
1571    struct DeserializeFields<C: Configuration>(C::Fields<'static>);
1572
1573    impl<'de, C> serde::Deserialize<'de> for DeserializeFields<C>
1574    where
1575        C: Configuration,
1576    {
1577        fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1578        where
1579            D: serde::Deserializer<'de>,
1580        {
1581            C::deserialize(deserializer)
1582                .map(DeserializeFields)
1583                .map_err(de::Error::custom)
1584        }
1585    }
1586}