inc_complete/db/
mod.rs

1use crate::cell::CellData;
2use crate::{Cell, Computation};
3use petgraph::graph::DiGraph;
4
5mod handle;
6mod tests;
7
8pub use handle::DbHandle;
9
10const START_VERSION: u32 = 1;
11
12#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
13pub struct Db<C: Computation> {
14    cells: DiGraph<CellData, ()>,
15    version: u32,
16    storage: C::Storage,
17}
18
19impl<C: Computation> Db<C>
20where
21    C::Storage: Default,
22{
23    pub fn new() -> Self {
24        Self {
25            cells: DiGraph::default(),
26            version: START_VERSION,
27            storage: Default::default(),
28        }
29    }
30}
31
32impl<C: Computation> Db<C> {
33    /// True if a given input is stale and needs to be re-computed.
34    /// Inputs which have never been computed are also considered stale.
35    ///
36    /// This does not actually re-compute the input.
37    pub fn is_stale<Concrete: Computation>(&self, input: &Concrete) -> bool {
38        // If the cell doesn't exist, it is definitely stale
39        let Some(cell) = self.get_cell(input) else {
40            return true;
41        };
42        self.is_stale_cell(cell)
43    }
44
45    /// True if a given cell is stale and needs to be re-computed.
46    /// This does not actually re-compute the input.
47    pub fn is_stale_cell(&self, cell: Cell) -> bool {
48        let computation_id = self.cells[cell.index()].computation_id;
49        if C::output_is_unset::<C>(cell, computation_id, computation_id, self) {
50            return true;
51        }
52
53        let neighbors = self.cells.neighbors(cell.index()).collect::<Vec<_>>();
54
55        // if any dependency may have changed, this cell is stale
56        neighbors.into_iter().any(|dependency_id| {
57            let dependency = &self.cells[dependency_id];
58            let cell = &self.cells[cell.index()];
59
60            dependency.last_verified_version != self.version
61                || dependency.last_updated_version > cell.last_verified_version
62        })
63    }
64
65    /// Return the corresponding Cell for a given input, if it exists.
66    ///
67    /// This will not update any values.
68    pub fn get_cell<ConcreteC: Computation>(&self, input: &ConcreteC) -> Option<Cell> {
69        C::dispatch_input_to_cell(input, &self.storage)
70    }
71
72    pub fn get_or_insert_cell<ConcreteC>(&mut self, input: ConcreteC) -> Cell
73    where
74        ConcreteC: Computation,
75    {
76        if let Some(cell) = C::dispatch_input_to_cell(&input, &self.storage) {
77            cell
78        } else {
79            let computation_id = C::computation_id_of::<ConcreteC>();
80
81            let new_id = self.cells.add_node(CellData::new(computation_id));
82            let cell = Cell::new(new_id);
83            C::dispatch_insert_new_cell(cell, input, &mut self.storage);
84            cell
85        }
86    }
87
88    /// Updates an input with a new value
89    ///
90    /// May panic in Debug mode if the input is not an input - ie. it has at least 1 dependency.
91    /// Note that this step is skipped when compiling in Release mode.
92    pub fn update_input<ConcreteC: Computation>(
93        &mut self,
94        input: ConcreteC,
95        new_value: ConcreteC::Output,
96    ) where
97        ConcreteC: std::fmt::Debug,
98        C::Output: Eq,
99    {
100        let debug = format!("{input:?}");
101        let cell_id = self.get_or_insert_cell(input);
102        debug_assert!(
103            self.is_input(cell_id),
104            "`{debug:?}` is not an input - inputs must have 0 dependencies",
105        );
106
107        // need to split dispatch_run into dispatch_run and dispatch_update
108        let cell = &self.cells[cell_id.index()];
109        let computation_id = cell.computation_id;
110
111        let changed = C::dispatch_update_output::<ConcreteC, C>(
112            cell_id,
113            computation_id,
114            computation_id,
115            new_value,
116            self,
117        );
118        let cell = &mut self.cells[cell_id.index()];
119
120        if changed {
121            self.version += 1;
122            cell.last_updated_version = self.version;
123            cell.last_verified_version = self.version;
124        } else {
125            cell.last_verified_version = self.version;
126        }
127    }
128
129    fn is_input(&self, cell: Cell) -> bool {
130        self.cells.neighbors(cell.index()).count() == 0
131    }
132
133    pub(crate) fn handle(&mut self, cell: Cell) -> DbHandle<C> {
134        DbHandle::new(self, cell)
135    }
136
137    pub fn storage(&self) -> &C::Storage {
138        &self.storage
139    }
140
141    pub fn storage_mut(&mut self) -> &mut C::Storage {
142        &mut self.storage
143    }
144
145    #[cfg(test)]
146    pub(crate) fn unwrap_cell_value<Concrete: Computation>(&self, input: &Concrete) -> &CellData
147    where
148        Concrete: std::fmt::Debug,
149    {
150        let cell = self
151            .get_cell(input)
152            .unwrap_or_else(|| panic!("unwrap_cell_value: Expected cell for `{input:?}` to exist"));
153        &self.cells[cell.index()]
154    }
155}
156
157impl<C: Computation + Clone> Db<C>
158where
159    C::Output: Eq,
160{
161    /// Similar to `update_input` but runs the compute function
162    /// instead of accepting a given value. This also will not update
163    /// `self.version`
164    fn run_compute_function(&mut self, cell_id: Cell)
165    where
166        C::Output: Eq,
167    {
168        let cell = &self.cells[cell_id.index()];
169        let computation_id = cell.computation_id;
170
171        let changed = C::dispatch_run::<C>(cell_id, computation_id, computation_id, self);
172
173        let cell = &mut self.cells[cell_id.index()];
174        cell.last_verified_version = self.version;
175
176        if changed {
177            cell.last_updated_version = self.version;
178        }
179    }
180
181    /// Trigger an update of the given cell, recursively checking and re-running any out of date
182    /// dependencies.
183    fn update_cell(&mut self, cell_id: Cell) {
184        let cell = &self.cells[cell_id.index()];
185
186        if cell.last_verified_version != self.version {
187            // if any dependency may have changed, update
188            if self.is_stale_cell(cell_id) {
189                self.run_compute_function(cell_id);
190            } else {
191                let cell = &mut self.cells[cell_id.index()];
192                cell.last_verified_version = self.version;
193            }
194        }
195    }
196
197    /// Retrieves the up to date value for the given computation, re-running any dependencies as
198    /// necessary.
199    ///
200    /// This function can panic if the dynamic type of the value returned by `compute.run(..)` is not `T`.
201    pub fn get<Concrete: Computation>(&mut self, compute: Concrete) -> &Concrete::Output {
202        let cell_id = self.get_or_insert_cell(compute);
203        self.get_with_cell::<Concrete>(cell_id)
204    }
205
206    /// Retrieves the up to date value for the given cell, re-running any dependencies as
207    /// necessary.
208    ///
209    /// This function can panic if the dynamic type of the value returned by `compute.run(..)` is not `T`.
210    pub fn get_with_cell<Concrete: Computation>(&mut self, cell_id: Cell) -> &Concrete::Output {
211        self.update_cell(cell_id);
212
213        let computation_id = self.cells[cell_id.index()].computation_id;
214        let container = C::get_storage_mut::<Concrete>(computation_id, self.storage_mut());
215        Concrete::get_function_and_output(cell_id, container)
216            .1
217            .expect("cell result should have been computed already")
218    }
219}