1use 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
22pub 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 #[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 pub fn build(self) -> Result<Instance, SnapshotError> {
56 let snap = self.snapshot;
57
58 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 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 let state_program = program.clone();
85
86 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 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 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}