inc_complete/computation/
mod.rs

1use crate::{Cell, Db, DbHandle};
2use std::any::{Any, TypeId};
3
4mod btreemapped;
5mod hashmapped;
6mod input;
7mod intermediate;
8mod singleton;
9mod tuple_impls;
10
11#[macro_use]
12mod macros;
13
14pub use btreemapped::BTreeMapStorage;
15pub use hashmapped::HashMapStorage;
16pub use input::{Input, OutputTypeForInput};
17pub use intermediate::{Intermediate, Run};
18pub use singleton::SingletonStorage;
19
20pub trait Computation: 'static + Sized + Clone {
21    type Storage;
22    type Output: Eq;
23
24    fn run(&self, handle: &mut DbHandle<impl Computation>) -> Self::Output;
25
26    fn input_to_cell(input: &Self, storage: &Self::Storage) -> Option<Cell>;
27    fn insert_new_cell(cell: Cell, function: Self, storage: &mut Self::Storage);
28
29    fn get_function_and_output(
30        cell: Cell,
31        storage: &Self::Storage,
32    ) -> (&Self, Option<&Self::Output>);
33    fn set_output(cell: Cell, output: Self::Output, storage: &mut Self::Storage);
34
35    // We could use TypeIds but those are not stable across rustc updates.
36    // Default to 0 here and have each pair impl increment the rhs to decide IDs based
37    // on their position in the computation tuple.
38    #[inline(always)]
39    fn computation_id_of<T: Computation>() -> u32 {
40        0
41    }
42
43    fn get_storage<Concrete: Computation + 'static>(
44        computation_id: u32,
45        container: &Self::Storage,
46    ) -> &Concrete::Storage {
47        assert_eq!(computation_id, 0, "Type dispatch failed for get_storage");
48
49        assert_eq!(
50            TypeId::of::<Concrete::Storage>(),
51            TypeId::of::<Self::Storage>(),
52            "Type dispatch failed for get_storage storage type:\n    {}\n != {}",
53            std::any::type_name::<Concrete::Storage>(),
54            std::any::type_name::<Self::Storage>(),
55        );
56
57        // Safety: We confirmed above Concrete::Storage == Self::Storage and thus `&mut Concrete::Storage == &mut Self::Storage`
58        unsafe { std::mem::transmute(container) }
59    }
60
61    fn get_storage_mut<Concrete: Computation + 'static>(
62        computation_id: u32,
63        container: &mut Self::Storage,
64    ) -> &mut Concrete::Storage {
65        assert_eq!(
66            computation_id, 0,
67            "Type dispatch failed for get_storage_mut container"
68        );
69
70        assert_eq!(
71            TypeId::of::<Concrete::Storage>(),
72            TypeId::of::<Self::Storage>(),
73            "Type dispatch failed for get_storage_mut container type"
74        );
75
76        // Safety: We confirmed above Concrete::Storage == Self::Storage and thus `&mut Concrete::Storage == &mut Self::Storage`
77        unsafe { std::mem::transmute(container) }
78    }
79
80    /// True if this has any cached output
81    fn output_is_unset<FullComputation: Computation>(
82        cell: Cell,
83        computation_id: u32,
84        original_computation_id: u32,
85        db: &Db<FullComputation>,
86    ) -> bool {
87        assert_eq!(
88            computation_id, 0,
89            "Type dispatch failed for output_is_unset"
90        );
91
92        let container = FullComputation::get_storage::<Self>(original_computation_id, db.storage());
93        Self::get_function_and_output(cell, container).1.is_none()
94    }
95
96    /// Given a Cell, TypeId pair dispatch to the correct run function
97    /// and return true if the value has changed. This should also cache
98    /// the new value if it has changed.
99    /// Note that in dispatch functions `Self` is always the concrete, non-tuple type.
100    fn dispatch_run<FullComputation: Computation>(
101        cell: Cell,
102        computation_id: u32,
103        original_computation_id: u32,
104        db: &mut Db<FullComputation>,
105    ) -> bool
106    where
107        Self: Clone,
108        Self::Output: Eq,
109    {
110        assert_eq!(computation_id, 0, "Type dispatch failed for dispatch_run");
111
112        let container =
113            FullComputation::get_storage_mut::<Self>(original_computation_id, db.storage_mut());
114        let function = Self::get_function_and_output(cell, container).0.clone();
115        let output = function.run(&mut db.handle(cell));
116        FullComputation::dispatch_update_output::<Self, FullComputation>(
117            cell,
118            original_computation_id,
119            original_computation_id,
120            output,
121            db,
122        )
123    }
124
125    /// Dispatch to the correct update_output function to cache the new output
126    /// and return true if the value has changed.
127    /// Note that in dispatch functions `Self` is the current type being dispatched,
128    /// `Concrete`, if present, is the non-tuple type of the target computation,
129    /// and `FullComputation` is the type of the `Db` computation parameter which is
130    /// usually a tuple of every possible computation.
131    fn dispatch_update_output<Concrete, FullComputation>(
132        cell: Cell,
133        computation_id: u32,
134        original_computation_id: u32,
135        output: Concrete::Output,
136        db: &mut Db<FullComputation>,
137    ) -> bool
138    where
139        Concrete: Computation,
140        FullComputation: Computation,
141        Self::Output: Eq,
142    {
143        assert_eq!(
144            computation_id, 0,
145            "Type dispatch failed for dispatch_update_output"
146        );
147        assert_eq!(
148            TypeId::of::<Concrete::Output>(),
149            TypeId::of::<Self::Output>(),
150            "Type dispatch failed for dispatch_update_output"
151        );
152
153        // Safety: We just checked T::Output == Self::Output above, we should be copying
154        // the same type.
155        let output2: Self::Output = unsafe { std::mem::transmute_copy(&output) };
156        std::mem::forget(output);
157
158        let container =
159            FullComputation::get_storage_mut::<Self>(original_computation_id, db.storage_mut());
160
161        let (_, previous_value) = Self::get_function_and_output(cell, container);
162        let changed = previous_value.map_or(true, |previous| output2 != *previous);
163
164        if changed {
165            Self::set_output(cell, output2, container);
166        }
167
168        changed
169    }
170
171    fn dispatch_input_to_cell<Concrete>(input: &Concrete, container: &Self::Storage) -> Option<Cell>
172    where
173        Concrete: 'static + Computation + Any,
174    {
175        assert_eq!(TypeId::of::<Concrete>(), TypeId::of::<Self>());
176        let input = (input as &dyn Any).downcast_ref().expect("T == Self");
177        Self::input_to_cell(input, container)
178    }
179
180    fn dispatch_insert_new_cell<Concrete>(cell: Cell, input: Concrete, storage: &mut Self::Storage)
181    where
182        Concrete: 'static + Computation + Any,
183        Concrete::Storage: 'static,
184    {
185        let input = transmute_copy_checked::<Concrete, Self>(input);
186        Self::insert_new_cell(cell, input, storage)
187    }
188}
189
190fn transmute_copy_checked<A: 'static, B: 'static>(x: A) -> B {
191    assert_eq!(TypeId::of::<A>(), TypeId::of::<B>());
192    // Safety: We confirmed above A == B
193    let x2: B = unsafe { std::mem::transmute_copy(&x) };
194    // x2 is copied byte by byte, we need to ensure the destructor is not run twice.
195    std::mem::forget(x);
196    x2
197}