Skip to main content

navi_vm\snapshot/
restore.rs

1//! `Instance::restore_state` and [`RestoreBuilder`] implementation.
2
3use std::{collections::HashMap, sync::Arc};
4
5use gc_arena::Arena;
6
7use crate::{
8    Instance, OutputMode,
9    data_provider::{DataProvider, InternalProvider},
10    gc_serde::{SerializedRawValue, build_gc_objects, fill_gc_objects},
11    objects::{NaviRef, string::NaviString},
12    raw_value::{RawValue, ToRawValue},
13    rollback::RollbackAction,
14    series::Series,
15    snapshot::types::{
16        InstanceSnapshot, SNAPSHOT_VERSION, SerializedRollbackAction, SerializedVariableValue,
17        SnapshotError,
18    },
19    state::{InputValue, SeriesVariable, State, VariableValue},
20};
21
22/// Builder returned by [`Instance::restore_state`] for configuring
23/// optional parameters before completing the restore.
24pub struct RestoreBuilder {
25    snapshot: InstanceSnapshot,
26    data_provider: Option<Box<dyn InternalProvider>>,
27}
28
29impl std::fmt::Debug for RestoreBuilder {
30    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
31        f.debug_struct("RestoreBuilder")
32            .field("version", &self.snapshot.version)
33            .finish_non_exhaustive()
34    }
35}
36
37impl RestoreBuilder {
38    /// Injects a [`DataProvider`] to enable `request.security()` and to
39    /// supply symbol metadata.
40    #[must_use]
41    pub fn with_data_provider<P>(mut self, provider: P) -> Self
42    where
43        P: DataProvider + 'static,
44    {
45        self.data_provider = Some(Box::new(provider) as Box<dyn InternalProvider>);
46        self
47    }
48
49    /// Finishes restoring the instance from the snapshot.
50    ///
51    /// # Errors
52    ///
53    /// Returns [`SnapshotError`] if the object table contains invalid
54    /// references or objects cannot be reconstructed.
55    pub fn build(self) -> Result<Instance, SnapshotError> {
56        let snap = self.snapshot;
57
58        // Extract Instance-level fields before moving GC-related data
59        // into the Arena closure.
60        let program = snap.program;
61        let timeframe = snap.timeframe;
62        let symbol_info = snap.symbol_info;
63        let main_symbol = symbol_info.symbol().as_str().to_owned();
64        let last_info = snap.last_info;
65        let last_bar_time = snap.last_bar_time;
66        let bar_index = snap.bar_index;
67        let input_index = snap.input_index;
68        let script_info = Arc::new(snap.script_info);
69        let chart = snap.chart;
70        let events = snap.events;
71        let input_sessions = snap.input_sessions;
72        let strategy_state = snap.strategy_state;
73        let execution_limits = snap.execution_limits;
74
75        // These are consumed inside the arena closure.
76        let object_table = snap.object_table;
77        let ser_variables = snap.variables;
78        let ser_inputs = snap.inputs;
79        let ser_rollback_actions = snap.rollback_actions;
80        let var_initialized = snap.var_initialized;
81
82        // Clone program for State (the arena closure takes ownership of
83        // the clone; the original stays for Instance).
84        let state_program = program.clone();
85
86        // Create a new arena and reconstruct all GC state.
87        let arena = Arena::new(move |mc| {
88            let gc_objects = build_gc_objects(mc, &object_table, &state_program);
89
90            fill_gc_objects(&object_table, &gc_objects, |srv| match srv {
91                SerializedRawValue::Scalar(f) => f.to_raw_value(),
92                SerializedRawValue::Reference(id) => gc_objects[*id as usize],
93            });
94
95            let resolve = |srv: &SerializedRawValue| -> RawValue {
96                match srv {
97                    SerializedRawValue::Scalar(f) => f.to_raw_value(),
98                    SerializedRawValue::Reference(id) => gc_objects[*id as usize],
99                }
100            };
101
102            let variables: Vec<VariableValue> = ser_variables
103                .iter()
104                .map(|sv| match sv {
105                    SerializedVariableValue::Series {
106                        values,
107                        offset,
108                        length,
109                        max_bars_back,
110                        observed_max_lookback,
111                    } => {
112                        let queue = values.iter().map(&resolve).collect();
113                        VariableValue::Series(SeriesVariable {
114                            values: Series::from_queue(queue),
115                            offset: *offset,
116                            length: *length,
117                            max_bars_back: *max_bars_back,
118                            observed_max_lookback: *observed_max_lookback,
119                        })
120                    }
121                    SerializedVariableValue::Simple(srv) => VariableValue::Simple(resolve(srv)),
122                })
123                .collect();
124
125            let inputs: Vec<InputValue> = ser_inputs
126                .iter()
127                .map(|si| InputValue {
128                    value: resolve(&si.value),
129                    is_reference_type: si.is_reference_type,
130                    value_type: si.value_type.clone(),
131                })
132                .collect();
133
134            let rollback_actions: Vec<RollbackAction> = ser_rollback_actions
135                .iter()
136                .map(|sra| match sra {
137                    SerializedRollbackAction::Var {
138                        var_id,
139                        value,
140                        is_reference_type,
141                    } => RollbackAction::Var {
142                        var_id: *var_id,
143                        value: resolve(value),
144                        is_reference_type: *is_reference_type,
145                    },
146                    SerializedRollbackAction::UdtField {
147                        udt,
148                        field_id,
149                        value,
150                        is_reference_type,
151                    } => RollbackAction::UdtField {
152                        udt: resolve(udt),
153                        field_id: *field_id,
154                        value: resolve(value),
155                        is_reference_type: *is_reference_type,
156                    },
157                    SerializedRollbackAction::RemoveGraph { id } => {
158                        RollbackAction::RemoveGraph { id: *id }
159                    }
160                    SerializedRollbackAction::RestoreGraph { id, graph } => {
161                        RollbackAction::RestoreGraph {
162                            id: *id,
163                            graph: graph.clone(),
164                        }
165                    }
166                })
167                .collect();
168
169            let string_constants = state_program
170                .string_constants()
171                .iter()
172                .map(|s| NaviRef::new(mc, NaviString::new(s.as_str())).to_raw_value())
173                .collect();
174
175            State {
176                program: state_program,
177                inputs,
178                variables,
179                rollback_actions,
180                string_constants,
181                var_initialized,
182                locals: Vec::new(),
183            }
184        });
185
186        Ok(Instance {
187            program,
188            locale: "en".to_owned(),
189            arena,
190            timeframe,
191            main_timeframe: timeframe,
192            symbol_info,
193            main_symbol,
194            candlesticks: Series::new(),
195            last_info,
196            bar_index,
197            input_index,
198            script_info,
199            chart,
200            events,
201            input_sessions,
202            strategy_state,
203            strategy_config_override: None,
204            aux_series_buffers: HashMap::new(),
205            execution_limits,
206            data_provider: self.data_provider,
207            candlestick_buffers: HashMap::new(),
208            security_sub_states: HashMap::new(),
209            security_lower_tf_sub_states: HashMap::new(),
210            // The snapshot requires all bars to be confirmed before saving,
211            // so the restored instance always starts in confirmed state.
212            last_bar_confirmed: true,
213            pending_security_capture: None,
214
215            output_mode: OutputMode::default(),
216            candlestick_offset: bar_index,
217            script_max_bars_back: None,
218            candlestick_max_bars_back: None,
219            candlestick_observed_max_lookback: usize::MAX,
220            global_observed_max_lookback: usize::MAX,
221            last_bar_time,
222            mtf_wait: std::time::Duration::ZERO,
223            exec_state_pool: Vec::new(),
224            #[cfg(feature = "jit")]
225            jit_code: None,
226            #[cfg(feature = "jit")]
227            jit_state: None,
228        })
229    }
230}
231
232impl Instance {
233    /// Deserializes a snapshot and returns a [`RestoreBuilder`] for
234    /// configuring optional parameters before completing the restore.
235    ///
236    /// # Errors
237    ///
238    /// Returns [`SnapshotError::Decode`] if decoding fails, or
239    /// [`SnapshotError::VersionMismatch`] if the snapshot version does not
240    /// match the current build.
241    pub fn restore_state(data: &[u8]) -> Result<RestoreBuilder, SnapshotError> {
242        let snapshot = postcard::from_bytes::<InstanceSnapshot>(data)
243            .map_err(|e| SnapshotError::Decode(e.to_string()))?;
244
245        if snapshot.version != SNAPSHOT_VERSION {
246            return Err(SnapshotError::VersionMismatch {
247                expected: SNAPSHOT_VERSION,
248                got: snapshot.version,
249            });
250        }
251
252        Ok(RestoreBuilder {
253            snapshot,
254            data_provider: None,
255        })
256    }
257}