libsurfer/cxxrtl/
query_container.rs
1use std::{
2 collections::{BTreeMap, HashMap},
3 sync::Arc,
4};
5
6use base64::{prelude::BASE64_STANDARD, Engine as _};
7use futures::executor::block_on;
8use num::{bigint::ToBigInt as _, BigInt, BigUint};
9use rayon::iter::{IntoParallelRefIterator as _, ParallelIterator as _};
10use surfer_translation_types::VariableValue;
11use tokio::sync::RwLock;
12
13use crate::{
14 cxxrtl_container::CxxrtlItem,
15 message::Message,
16 wave_container::{QueryResult, VariableRef},
17 EGUI_CONTEXT,
18};
19
20use super::sc_message::CxxrtlSample;
21
22type ValueList = Arc<RwLock<BTreeMap<BigInt, HashMap<VariableRef, VariableValue>>>>;
23
24pub struct QueryContainer {
25 variable_values: ValueList,
26}
27
28impl QueryContainer {
29 pub fn empty() -> Self {
30 QueryContainer {
31 variable_values: Arc::new(RwLock::new(BTreeMap::new())),
32 }
33 }
34
35 pub fn populate(
36 &mut self,
37 variables: Vec<VariableRef>,
38 item_info: Arc<HashMap<VariableRef, CxxrtlItem>>,
39 data: Vec<CxxrtlSample>,
40 msg_sender: std::sync::mpsc::Sender<Message>,
41 ) {
42 let variable_values = self.variable_values.clone();
43
44 let task = fill_variable_values(variables, item_info, data, variable_values, msg_sender);
45 #[cfg(target_arch = "wasm32")]
46 wasm_bindgen_futures::spawn_local(task);
47 #[cfg(not(target_arch = "wasm32"))]
48 tokio::task::spawn(task);
49 }
50
51 pub fn query(&self, var: &VariableRef, query_time: BigInt) -> QueryResult {
52 let values = block_on(self.variable_values.read());
53
54 if let Some((time, value_map)) = values.range(..query_time.clone()).next_back() {
55 match (time.to_biguint(), value_map.get(var)) {
56 (Some(time), Some(value)) => {
57 let next = values
58 .range(query_time..)
59 .next()
60 .and_then(|(k, _)| k.to_biguint());
61 QueryResult {
62 current: Some((time.clone(), value.clone())),
63 next,
64 }
65 }
66 _ => QueryResult::default(),
67 }
68 } else {
69 QueryResult::default()
70 }
71 }
72}
73
74async fn fill_variable_values(
75 variables: Vec<VariableRef>,
76 item_info: Arc<HashMap<VariableRef, CxxrtlItem>>,
77 data: Vec<CxxrtlSample>,
78 variable_values: ValueList,
79 msg_sender: std::sync::mpsc::Sender<Message>,
80) {
81 let work = move || {
82 let mut offset = 0;
86 let mut ranges = vec![];
87 for variable in &variables {
88 let this_size_bits = &item_info[variable].width;
89 let this_size_u32 = 1 + ((this_size_bits - 1) / 32);
90 ranges.push((offset * 4) as usize..((offset + this_size_u32) * 4) as usize);
91 offset += this_size_u32;
92 }
93
94 data.par_iter().for_each(|sample| {
95 let u8s = BASE64_STANDARD
96 .decode(&sample.item_values)
97 .map_err(|e| {
98 panic!(
99 "Got non-base64 data from cxxrtl at time {}. {e}",
100 sample.time
101 )
102 })
103 .unwrap();
104
105 let values = ranges
106 .iter()
107 .zip(&variables)
108 .map(|(range, var)| {
109 let value = BigUint::from_bytes_le(&u8s[range.clone()]);
110
111 (var.clone(), VariableValue::BigUint(value))
114 })
115 .collect::<HashMap<_, _>>();
116
117 block_on(variable_values.write())
118 .insert(sample.time.as_femtoseconds().to_bigint().unwrap(), values);
119 msg_sender
120 .send(Message::InvalidateDrawCommands)
121 .expect("Message receiver disconnected");
122 });
123
124 if let Some(ctx) = EGUI_CONTEXT.read().unwrap().as_ref() {
125 ctx.request_repaint();
126 }
127 };
128 #[cfg(target_arch = "wasm32")]
132 work();
133 #[cfg(not(target_arch = "wasm32"))]
134 tokio::task::spawn_blocking(work);
135}