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