inc_complete/
computation.rs

1use crate::{Cell, Db, DbHandle};
2use std::any::{Any, TypeId};
3
4mod tuple_impls;
5mod input;
6mod intermediate;
7mod singleton;
8mod hashmapped;
9mod btreemapped;
10
11#[macro_use]
12mod macros;
13
14pub use input::{ Input, OutputTypeForInput };
15pub use intermediate::{ Intermediate, Run };
16pub use singleton::SingletonStorage;
17pub use hashmapped::HashMapStorage;
18pub use btreemapped::BTreeMapStorage;
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        let unset = Self::get_function_and_output(cell, container).1.is_none();
94        println!("{} output is unset: {unset}", std::any::type_name::<Self>());
95        unset
96    }
97
98    /// Given a Cell, TypeId pair dispatch to the correct run function
99    /// and return true if the value has changed. This should also cache
100    /// the new value if it has changed.
101    /// Note that in dispatch functions `Self` is always the concrete, non-tuple type.
102    fn dispatch_run<FullComputation: Computation>(
103        cell: Cell,
104        computation_id: u32,
105        original_computation_id: u32,
106        db: &mut Db<FullComputation>,
107    ) -> bool
108    where
109        Self: Clone,
110        Self::Output: Eq,
111    {
112        assert_eq!(computation_id, 0, "Type dispatch failed for dispatch_run");
113
114        let container =
115            FullComputation::get_storage_mut::<Self>(original_computation_id, db.storage_mut());
116        let function = Self::get_function_and_output(cell, container).0.clone();
117        let output = function.run(&mut db.handle(cell));
118        FullComputation::dispatch_update_output::<Self, FullComputation>(
119            cell,
120            original_computation_id,
121            original_computation_id,
122            output,
123            db,
124        )
125    }
126
127    /// Dispatch to the correct update_output function to cache the new output
128    /// and return true if the value has changed.
129    /// Note that in dispatch functions `Self` is the current type being dispatched,
130    /// `Concrete`, if present, is the non-tuple type of the target computation,
131    /// and `FullComputation` is the type of the `Db` computation parameter which is
132    /// usually a tuple of every possible computation.
133    fn dispatch_update_output<Concrete, FullComputation>(
134        cell: Cell,
135        computation_id: u32,
136        original_computation_id: u32,
137        output: Concrete::Output,
138        db: &mut Db<FullComputation>,
139    ) -> bool
140    where
141        Concrete: Computation,
142        FullComputation: Computation,
143        Self::Output: Eq,
144    {
145        assert_eq!(
146            computation_id, 0,
147            "Type dispatch failed for dispatch_update_output"
148        );
149        assert_eq!(
150            TypeId::of::<Concrete::Output>(),
151            TypeId::of::<Self::Output>(),
152            "Type dispatch failed for dispatch_update_output"
153        );
154
155        // Safety: We just checked T::Output == Self::Output above, we should be copying
156        // the same type.
157        let output2: Self::Output = unsafe { std::mem::transmute_copy(&output) };
158        std::mem::forget(output);
159
160        let container =
161            FullComputation::get_storage_mut::<Self>(original_computation_id, db.storage_mut());
162
163        let (_, previous_value) = Self::get_function_and_output(cell, container);
164        let changed = previous_value.map_or(true, |previous| output2 != *previous);
165
166        if changed {
167            Self::set_output(cell, output2, container);
168        }
169
170        changed
171    }
172
173    fn dispatch_input_to_cell<Concrete>(input: &Concrete, container: &Self::Storage) -> Option<Cell>
174    where
175        Concrete: 'static + Computation + Any,
176    {
177        assert_eq!(TypeId::of::<Concrete>(), TypeId::of::<Self>());
178        let input = (input as &dyn Any).downcast_ref().expect("T == Self");
179        Self::input_to_cell(input, container)
180    }
181
182    fn dispatch_insert_new_cell<Concrete>(cell: Cell, input: Concrete, storage: &mut Self::Storage)
183    where
184        Concrete: 'static + Computation + Any,
185        Concrete::Storage: 'static,
186    {
187        let input = transmute_copy_checked::<Concrete, Self>(input);
188        Self::insert_new_cell(cell, input, storage)
189    }
190}
191
192fn transmute_copy_checked<A: 'static, B: 'static>(x: A) -> B {
193    assert_eq!(TypeId::of::<A>(), TypeId::of::<B>());
194    // Safety: We confirmed above A == B
195    let x2: B = unsafe { std::mem::transmute_copy(&x) };
196    // x2 is copied byte by byte, we need to ensure the destructor is not run twice.
197    std::mem::forget(x);
198    x2
199}