1use crate::{Dbc, Message, Result};
30use std::collections::HashMap;
31use std::path::Path;
32use std::sync::Arc;
33
34#[derive(Debug, Clone)]
41pub struct FastDbc {
42 inner: Arc<FastDbcInner>,
44}
45
46#[derive(Debug)]
48struct FastDbcInner {
49 dbc: Dbc,
51 index: HashMap<u32, usize>,
53 max_signals: usize,
55 total_signals: usize,
57}
58
59impl FastDbc {
60 pub fn from_file<P: AsRef<Path>>(path: P) -> Result<Self> {
74 let dbc = Dbc::from_file(path)?;
75 Ok(Self::new(dbc))
76 }
77
78 pub fn new(dbc: Dbc) -> Self {
82 let mut index = HashMap::with_capacity(dbc.messages().len());
83 let mut max_signals = 0;
84 let mut total_signals = 0;
85
86 for (i, msg) in dbc.messages().iter().enumerate() {
87 index.insert(msg.id_with_flag(), i);
89 let sig_count = msg.signals().len();
90 max_signals = max_signals.max(sig_count);
91 total_signals += sig_count;
92 }
93
94 Self {
95 inner: Arc::new(FastDbcInner {
96 dbc,
97 index,
98 max_signals,
99 total_signals,
100 }),
101 }
102 }
103
104 #[inline]
111 pub fn get(&self, id: u32) -> Option<&Message> {
112 self.inner.index.get(&id).and_then(|&idx| self.inner.dbc.messages().at(idx))
113 }
114
115 #[inline]
119 pub fn get_extended(&self, id: u32) -> Option<&Message> {
120 let extended_id = id | Message::EXTENDED_ID_FLAG;
121 self.inner
122 .index
123 .get(&extended_id)
124 .and_then(|&idx| self.inner.dbc.messages().at(idx))
125 }
126
127 #[inline]
131 pub fn get_any(&self, id: u32) -> Option<&Message> {
132 self.inner
134 .index
135 .get(&id)
136 .or_else(|| self.inner.index.get(&(id | Message::EXTENDED_ID_FLAG)))
137 .and_then(|&idx| self.inner.dbc.messages().at(idx))
138 }
139
140 #[inline]
155 pub fn decode_into(&self, id: u32, data: &[u8], out: &mut [f64]) -> Option<usize> {
156 let msg = self.get(id)?;
157 let count = msg.decode_into(data, out);
158 if count > 0 { Some(count) } else { None }
159 }
160
161 #[inline]
163 pub fn decode_extended_into(&self, id: u32, data: &[u8], out: &mut [f64]) -> Option<usize> {
164 let msg = self.get_extended(id)?;
165 let count = msg.decode_into(data, out);
166 if count > 0 { Some(count) } else { None }
167 }
168
169 #[inline]
171 pub fn decode_raw_into(&self, id: u32, data: &[u8], out: &mut [i64]) -> Option<usize> {
172 let msg = self.get(id)?;
173 let count = msg.decode_raw_into(data, out);
174 if count > 0 { Some(count) } else { None }
175 }
176
177 #[inline]
181 pub fn max_signals(&self) -> usize {
182 self.inner.max_signals
183 }
184
185 #[inline]
187 pub fn total_signals(&self) -> usize {
188 self.inner.total_signals
189 }
190
191 #[inline]
193 pub fn message_count(&self) -> usize {
194 self.inner.dbc.messages().len()
195 }
196
197 #[inline]
199 pub fn contains(&self, id: u32) -> bool {
200 self.inner.index.contains_key(&id)
201 }
202
203 #[inline]
205 pub fn contains_extended(&self, id: u32) -> bool {
206 self.inner.index.contains_key(&(id | Message::EXTENDED_ID_FLAG))
207 }
208
209 #[inline]
211 pub fn dbc(&self) -> &Dbc {
212 &self.inner.dbc
213 }
214
215 #[inline]
219 pub fn into_dbc(self) -> Dbc {
220 match Arc::try_unwrap(self.inner) {
221 Ok(inner) => inner.dbc,
222 Err(arc) => arc.dbc.clone(),
223 }
224 }
225
226 pub fn ids(&self) -> impl Iterator<Item = u32> + '_ {
228 self.inner.index.keys().copied()
229 }
230}
231
232impl From<Dbc> for FastDbc {
233 fn from(dbc: Dbc) -> Self {
234 Self::new(dbc)
235 }
236}
237
238#[cfg(test)]
239mod tests {
240 use super::*;
241
242 #[test]
243 fn test_fast_dbc_basic() {
244 let dbc = Dbc::parse(
245 r#"VERSION "1.0"
246
247BU_: ECM
248
249BO_ 256 Engine : 8 ECM
250 SG_ RPM : 0|16@1+ (0.25,0) [0|8000] "rpm" *
251 SG_ Temp : 16|8@1- (1,-40) [-40|215] "C" *
252"#,
253 )
254 .unwrap();
255
256 let fast = FastDbc::new(dbc);
257
258 assert_eq!(fast.message_count(), 1);
259 assert_eq!(fast.max_signals(), 2);
260 assert_eq!(fast.total_signals(), 2);
261 assert!(fast.contains(256));
262 assert!(!fast.contains(512));
263
264 let msg = fast.get(256).unwrap();
265 assert_eq!(msg.name(), "Engine");
266 }
267
268 #[test]
269 fn test_fast_dbc_decode_into() {
270 let dbc = Dbc::parse(
271 r#"VERSION "1.0"
272
273BU_: ECM
274
275BO_ 256 Engine : 8 ECM
276 SG_ RPM : 0|16@1+ (0.25,0) [0|8000] "rpm" *
277 SG_ Temp : 16|8@1- (1,-40) [-40|215] "C" *
278"#,
279 )
280 .unwrap();
281
282 let fast = FastDbc::new(dbc);
283
284 let payload = [0x40, 0x1F, 0x5A, 0x00, 0x00, 0x00, 0x00, 0x00];
286 let mut values = vec![0.0f64; fast.max_signals()];
287
288 let count = fast.decode_into(256, &payload, &mut values).unwrap();
289
290 assert_eq!(count, 2);
291 assert_eq!(values[0], 2000.0);
292 assert_eq!(values[1], 50.0);
293 }
294
295 #[test]
296 fn test_fast_dbc_message_not_found() {
297 let dbc = Dbc::parse(
298 r#"VERSION "1.0"
299
300BU_: ECM
301
302BO_ 256 Engine : 8 ECM
303 SG_ RPM : 0|16@1+ (1,0) [0|8000] "rpm" *
304"#,
305 )
306 .unwrap();
307
308 let fast = FastDbc::new(dbc);
309 let payload = [0x00; 8];
310 let mut values = [0.0f64; 8];
311
312 assert!(fast.decode_into(512, &payload, &mut values).is_none());
313 }
314
315 #[test]
316 fn test_fast_dbc_extended_id() {
317 let dbc = Dbc::parse(
318 r#"VERSION "1.0"
319
320BU_: ECM
321
322BO_ 2147484672 ExtendedMsg : 8 ECM
323 SG_ Speed : 0|16@1+ (0.1,0) [0|6553.5] "km/h" *
324"#,
325 )
326 .unwrap();
327 let fast = FastDbc::new(dbc);
330
331 assert!(!fast.contains(0x400));
333 assert!(fast.get(0x400).is_none());
334
335 assert!(fast.contains_extended(0x400));
337 let msg = fast.get_extended(0x400).unwrap();
338 assert_eq!(msg.name(), "ExtendedMsg");
339
340 let payload = [0xE8, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00];
342 let mut values = [0.0f64; 8];
343
344 let count = fast.decode_extended_into(0x400, &payload, &mut values).unwrap();
345 assert_eq!(count, 1);
346 assert_eq!(values[0], 100.0); }
348
349 #[test]
350 fn test_fast_dbc_multiple_messages() {
351 let dbc = Dbc::parse(
352 r#"VERSION "1.0"
353
354BU_: ECM
355
356BO_ 256 Msg1 : 8 ECM
357 SG_ Sig1 : 0|8@1+ (1,0) [0|255] "" *
358 SG_ Sig2 : 8|8@1+ (1,0) [0|255] "" *
359
360BO_ 512 Msg2 : 8 ECM
361 SG_ SigA : 0|16@1+ (1,0) [0|65535] "" *
362
363BO_ 768 Msg3 : 8 ECM
364 SG_ SigX : 0|8@1+ (1,0) [0|255] "" *
365 SG_ SigY : 8|8@1+ (1,0) [0|255] "" *
366 SG_ SigZ : 16|8@1+ (1,0) [0|255] "" *
367"#,
368 )
369 .unwrap();
370
371 let fast = FastDbc::new(dbc);
372
373 assert_eq!(fast.message_count(), 3);
374 assert_eq!(fast.max_signals(), 3); assert_eq!(fast.total_signals(), 6);
376
377 assert!(fast.contains(256));
378 assert!(fast.contains(512));
379 assert!(fast.contains(768));
380 }
381
382 #[test]
383 fn test_fast_dbc_from_trait() {
384 let dbc = Dbc::parse(
385 r#"VERSION "1.0"
386
387BU_: ECM
388
389BO_ 256 Engine : 8 ECM
390"#,
391 )
392 .unwrap();
393
394 let fast: FastDbc = dbc.into();
395 assert_eq!(fast.message_count(), 1);
396 }
397
398 #[test]
399 fn test_fast_dbc_into_dbc() {
400 let dbc = Dbc::parse(
401 r#"VERSION "1.0"
402
403BU_: ECM
404
405BO_ 256 Engine : 8 ECM
406"#,
407 )
408 .unwrap();
409
410 let fast = FastDbc::new(dbc);
411 let dbc_back = fast.into_dbc();
412
413 assert_eq!(dbc_back.messages().len(), 1);
414 }
415}