1mod decode;
30mod hasher;
31
32use crate::{ByteOrder, Dbc, Message, Result};
33use decode::{DecodePlan, SignalDecode};
34use hasher::FxHashMap;
35use std::collections::HashMap;
36use std::path::Path;
37use std::sync::Arc;
38
39const MAX_STANDARD_ID: usize = 2048;
41
42#[derive(Clone)]
58pub struct FastDbc {
59 inner: Arc<FastDbcInner>,
60}
61
62struct FastDbcInner {
63 dbc: Dbc,
65 standard_ids: Box<[usize; MAX_STANDARD_ID]>,
68 extended_ids: FxHashMap<u32, usize>,
70 decode_plans: Vec<DecodePlan>,
72 max_signals: usize,
74 total_signals: usize,
76}
77
78impl std::fmt::Debug for FastDbc {
79 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
80 f.debug_struct("FastDbc")
81 .field("message_count", &self.message_count())
82 .field("max_signals", &self.max_signals())
83 .field("total_signals", &self.total_signals())
84 .finish()
85 }
86}
87
88impl FastDbc {
89 pub fn from_file<P: AsRef<Path>>(path: P) -> Result<Self> {
91 let dbc = Dbc::from_file(path)?;
92 Ok(Self::new(dbc))
93 }
94
95 pub fn new(dbc: Dbc) -> Self {
99 let mut standard_ids = Box::new([usize::MAX; MAX_STANDARD_ID]);
100 let mut extended_ids: FxHashMap<u32, usize> =
101 HashMap::with_capacity_and_hasher(16, Default::default());
102 let mut decode_plans = Vec::with_capacity(dbc.messages().len());
103 let mut max_signals = 0;
104 let mut total_signals = 0;
105
106 for (msg_idx, msg) in dbc.messages().iter().enumerate() {
107 let plan_idx = decode_plans.len();
108
109 let signals: Vec<SignalDecode> =
111 msg.signals().iter().map(SignalDecode::from_signal).collect();
112
113 let sig_count = signals.len();
114 max_signals = max_signals.max(sig_count);
115 total_signals += sig_count;
116
117 decode_plans.push(DecodePlan {
118 message_index: msg_idx,
119 min_bytes: msg.min_bytes_required(),
120 signals,
121 });
122
123 let id = msg.id_with_flag();
125 if !msg.is_extended() && id < MAX_STANDARD_ID as u32 {
126 standard_ids[id as usize] = plan_idx;
127 } else {
128 extended_ids.insert(id, plan_idx);
129 }
130 }
131
132 Self {
133 inner: Arc::new(FastDbcInner {
134 dbc,
135 standard_ids,
136 extended_ids,
137 decode_plans,
138 max_signals,
139 total_signals,
140 }),
141 }
142 }
143
144 #[inline(always)]
150 fn get_plan_index(&self, id: u32) -> Option<usize> {
151 if id < MAX_STANDARD_ID as u32 {
152 let idx = self.inner.standard_ids[id as usize];
154 if idx != usize::MAX { Some(idx) } else { None }
155 } else {
156 self.inner.extended_ids.get(&id).copied()
158 }
159 }
160
161 #[inline(always)]
163 fn get_plan_index_extended(&self, id: u32) -> Option<usize> {
164 let extended_id = id | Message::EXTENDED_ID_FLAG;
165 self.inner.extended_ids.get(&extended_id).copied()
166 }
167
168 #[inline]
170 pub fn get(&self, id: u32) -> Option<&Message> {
171 self.get_plan_index(id)
172 .map(|idx| &self.inner.decode_plans[idx])
173 .and_then(|plan| self.inner.dbc.messages().at(plan.message_index))
174 }
175
176 #[inline]
178 pub fn get_extended(&self, id: u32) -> Option<&Message> {
179 self.get_plan_index_extended(id)
180 .map(|idx| &self.inner.decode_plans[idx])
181 .and_then(|plan| self.inner.dbc.messages().at(plan.message_index))
182 }
183
184 #[inline]
186 pub fn get_any(&self, id: u32) -> Option<&Message> {
187 self.get(id).or_else(|| self.get_extended(id))
188 }
189
190 #[inline]
210 pub fn decode_into(&self, id: u32, data: &[u8], out: &mut [f64]) -> Option<usize> {
211 let plan_idx = self.get_plan_index(id)?;
212 let plan = &self.inner.decode_plans[plan_idx];
213
214 if data.len() < plan.min_bytes as usize {
215 return None;
216 }
217
218 Some(self.decode_with_plan(plan, data, out))
219 }
220
221 #[inline]
223 pub fn decode_extended_into(&self, id: u32, data: &[u8], out: &mut [f64]) -> Option<usize> {
224 let plan_idx = self.get_plan_index_extended(id)?;
225 let plan = &self.inner.decode_plans[plan_idx];
226
227 if data.len() < plan.min_bytes as usize {
228 return None;
229 }
230
231 Some(self.decode_with_plan(plan, data, out))
232 }
233
234 #[inline]
236 pub fn decode_raw_into(&self, id: u32, data: &[u8], out: &mut [i64]) -> Option<usize> {
237 let plan_idx = self.get_plan_index(id)?;
238 let plan = &self.inner.decode_plans[plan_idx];
239
240 if data.len() < plan.min_bytes as usize {
241 return None;
242 }
243
244 Some(self.decode_raw_with_plan(plan, data, out))
245 }
246
247 #[inline(always)]
253 fn decode_with_plan(&self, plan: &DecodePlan, data: &[u8], out: &mut [f64]) -> usize {
254 let mut count = 0;
255 for (out_val, sig) in out.iter_mut().zip(plan.signals.iter()) {
256 let raw = self.extract_raw(*sig, data);
257 *out_val = self.apply_scaling(*sig, raw);
258 count += 1;
259 }
260 count
261 }
262
263 #[inline(always)]
265 fn decode_raw_with_plan(&self, plan: &DecodePlan, data: &[u8], out: &mut [i64]) -> usize {
266 let mut count = 0;
267 for (out_val, sig) in out.iter_mut().zip(plan.signals.iter()) {
268 *out_val = self.extract_raw(*sig, data);
269 count += 1;
270 }
271 count
272 }
273
274 #[inline(always)]
276 fn extract_raw(&self, sig: SignalDecode, data: &[u8]) -> i64 {
277 let byte_order = if sig.is_little_endian() {
278 ByteOrder::LittleEndian
279 } else {
280 ByteOrder::BigEndian
281 };
282
283 let start_bit = sig.byte_start as usize * 8 + sig.bit_offset as usize;
284 let raw_bits = byte_order.extract_bits(data, start_bit, sig.length as usize);
285
286 if sig.is_unsigned() {
287 raw_bits as i64
288 } else {
289 Self::sign_extend(raw_bits, sig.length as usize)
290 }
291 }
292
293 #[inline(always)]
295 fn apply_scaling(&self, sig: SignalDecode, raw: i64) -> f64 {
296 if sig.is_identity() {
297 raw as f64
298 } else {
299 (raw as f64) * sig.factor + sig.offset
300 }
301 }
302
303 #[inline(always)]
305 fn sign_extend(value: u64, bits: usize) -> i64 {
306 let sign_bit = 1u64 << (bits - 1);
307 if (value & sign_bit) != 0 {
308 let mask = !((1u64 << bits) - 1);
309 (value | mask) as i64
310 } else {
311 value as i64
312 }
313 }
314
315 #[inline]
323 pub fn max_signals(&self) -> usize {
324 self.inner.max_signals
325 }
326
327 #[inline]
329 pub fn total_signals(&self) -> usize {
330 self.inner.total_signals
331 }
332
333 #[inline]
335 pub fn message_count(&self) -> usize {
336 self.inner.decode_plans.len()
337 }
338
339 #[inline]
341 pub fn contains(&self, id: u32) -> bool {
342 self.get_plan_index(id).is_some()
343 }
344
345 #[inline]
347 pub fn contains_extended(&self, id: u32) -> bool {
348 self.get_plan_index_extended(id).is_some()
349 }
350
351 #[inline]
353 pub fn dbc(&self) -> &Dbc {
354 &self.inner.dbc
355 }
356
357 #[inline]
361 pub fn into_dbc(self) -> Dbc {
362 match Arc::try_unwrap(self.inner) {
363 Ok(inner) => inner.dbc,
364 Err(arc) => arc.dbc.clone(),
365 }
366 }
367
368 pub fn ids(&self) -> impl Iterator<Item = u32> + '_ {
370 let standard = self
372 .inner
373 .standard_ids
374 .iter()
375 .enumerate()
376 .filter(|(_, idx)| **idx != usize::MAX)
377 .map(|(id, _)| id as u32);
378
379 let extended = self.inner.extended_ids.keys().copied();
381
382 standard.chain(extended)
383 }
384}
385
386impl From<Dbc> for FastDbc {
387 fn from(dbc: Dbc) -> Self {
388 Self::new(dbc)
389 }
390}
391
392#[cfg(test)]
397mod tests {
398 use super::*;
399
400 #[test]
401 fn test_fast_dbc_basic() {
402 let dbc = Dbc::parse(
403 r#"VERSION "1.0"
404
405BU_: ECM
406
407BO_ 256 Engine : 8 ECM
408 SG_ RPM : 0|16@1+ (0.25,0) [0|8000] "rpm" *
409 SG_ Temp : 16|8@1- (1,-40) [-40|215] "C" *
410"#,
411 )
412 .unwrap();
413
414 let fast = FastDbc::new(dbc);
415
416 assert_eq!(fast.message_count(), 1);
417 assert_eq!(fast.max_signals(), 2);
418 assert_eq!(fast.total_signals(), 2);
419 assert!(fast.contains(256));
420 assert!(!fast.contains(512));
421
422 let msg = fast.get(256).unwrap();
423 assert_eq!(msg.name(), "Engine");
424 }
425
426 #[test]
427 fn test_fast_dbc_decode_into() {
428 let dbc = Dbc::parse(
429 r#"VERSION "1.0"
430
431BU_: ECM
432
433BO_ 256 Engine : 8 ECM
434 SG_ RPM : 0|16@1+ (0.25,0) [0|8000] "rpm" *
435 SG_ Temp : 16|8@1- (1,-40) [-40|215] "C" *
436"#,
437 )
438 .unwrap();
439
440 let fast = FastDbc::new(dbc);
441
442 let payload = [0x40, 0x1F, 0x5A, 0x00, 0x00, 0x00, 0x00, 0x00];
444 let mut values = vec![0.0f64; fast.max_signals()];
445
446 let count = fast.decode_into(256, &payload, &mut values).unwrap();
447
448 assert_eq!(count, 2);
449 assert_eq!(values[0], 2000.0);
450 assert_eq!(values[1], 50.0);
451 }
452
453 #[test]
454 fn test_fast_dbc_identity_transform() {
455 let dbc = Dbc::parse(
456 r#"VERSION "1.0"
457
458BU_: ECM
459
460BO_ 256 Engine : 8 ECM
461 SG_ RawValue : 0|16@1+ (1,0) [0|65535] "" *
462"#,
463 )
464 .unwrap();
465
466 let fast = FastDbc::new(dbc);
467
468 let payload = [0x39, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00];
470 let mut values = [0.0f64; 1];
471
472 fast.decode_into(256, &payload, &mut values);
473
474 assert_eq!(values[0], 12345.0);
475 }
476
477 #[test]
478 fn test_fast_dbc_message_not_found() {
479 let dbc = Dbc::parse(
480 r#"VERSION "1.0"
481
482BU_: ECM
483
484BO_ 256 Engine : 8 ECM
485 SG_ RPM : 0|16@1+ (1,0) [0|8000] "rpm" *
486"#,
487 )
488 .unwrap();
489
490 let fast = FastDbc::new(dbc);
491 let payload = [0x00; 8];
492 let mut values = [0.0f64; 8];
493
494 assert!(fast.decode_into(512, &payload, &mut values).is_none());
495 }
496
497 #[test]
498 fn test_fast_dbc_extended_id() {
499 let dbc = Dbc::parse(
500 r#"VERSION "1.0"
501
502BU_: ECM
503
504BO_ 2147484672 ExtendedMsg : 8 ECM
505 SG_ Speed : 0|16@1+ (0.1,0) [0|6553.5] "km/h" *
506"#,
507 )
508 .unwrap();
509 let fast = FastDbc::new(dbc);
512
513 assert!(!fast.contains(0x400));
515 assert!(fast.get(0x400).is_none());
516
517 assert!(fast.contains_extended(0x400));
519 let msg = fast.get_extended(0x400).unwrap();
520 assert_eq!(msg.name(), "ExtendedMsg");
521
522 let payload = [0xE8, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00];
524 let mut values = [0.0f64; 8];
525
526 let count = fast.decode_extended_into(0x400, &payload, &mut values).unwrap();
527 assert_eq!(count, 1);
528 assert_eq!(values[0], 100.0); }
530
531 #[test]
532 fn test_fast_dbc_multiple_messages() {
533 let dbc = Dbc::parse(
534 r#"VERSION "1.0"
535
536BU_: ECM
537
538BO_ 256 Msg1 : 8 ECM
539 SG_ Sig1 : 0|8@1+ (1,0) [0|255] "" *
540 SG_ Sig2 : 8|8@1+ (1,0) [0|255] "" *
541
542BO_ 512 Msg2 : 8 ECM
543 SG_ SigA : 0|16@1+ (1,0) [0|65535] "" *
544
545BO_ 768 Msg3 : 8 ECM
546 SG_ SigX : 0|8@1+ (1,0) [0|255] "" *
547 SG_ SigY : 8|8@1+ (1,0) [0|255] "" *
548 SG_ SigZ : 16|8@1+ (1,0) [0|255] "" *
549"#,
550 )
551 .unwrap();
552
553 let fast = FastDbc::new(dbc);
554
555 assert_eq!(fast.message_count(), 3);
556 assert_eq!(fast.max_signals(), 3); assert_eq!(fast.total_signals(), 6);
558
559 assert!(fast.contains(256));
560 assert!(fast.contains(512));
561 assert!(fast.contains(768));
562 }
563
564 #[test]
565 fn test_fast_dbc_large_id() {
566 let dbc = Dbc::parse(
568 r#"VERSION "1.0"
569
570BU_: ECM
571
572BO_ 3000 LargeId : 8 ECM
573 SG_ Value : 0|16@1+ (1,0) [0|65535] "" *
574"#,
575 )
576 .unwrap();
577
578 let fast = FastDbc::new(dbc);
579
580 assert!(fast.contains(3000));
581 assert!(!fast.contains(256));
582
583 let payload = [0x39, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00];
584 let mut values = [0.0f64; 1];
585
586 let count = fast.decode_into(3000, &payload, &mut values).unwrap();
587 assert_eq!(count, 1);
588 assert_eq!(values[0], 12345.0);
589 }
590
591 #[test]
592 fn test_fast_dbc_from_trait() {
593 let dbc = Dbc::parse(
594 r#"VERSION "1.0"
595
596BU_: ECM
597
598BO_ 256 Engine : 8 ECM
599"#,
600 )
601 .unwrap();
602
603 let fast: FastDbc = dbc.into();
604 assert_eq!(fast.message_count(), 1);
605 }
606
607 #[test]
608 fn test_fast_dbc_into_dbc() {
609 let dbc = Dbc::parse(
610 r#"VERSION "1.0"
611
612BU_: ECM
613
614BO_ 256 Engine : 8 ECM
615"#,
616 )
617 .unwrap();
618
619 let fast = FastDbc::new(dbc);
620 let dbc_back = fast.into_dbc();
621
622 assert_eq!(dbc_back.messages().len(), 1);
623 }
624
625 #[test]
626 fn test_fast_dbc_ids_iterator() {
627 let dbc = Dbc::parse(
628 r#"VERSION "1.0"
629
630BU_: ECM
631
632BO_ 100 Msg1 : 8 ECM
633BO_ 200 Msg2 : 8 ECM
634BO_ 3000 LargeId : 8 ECM
635"#,
636 )
637 .unwrap();
638
639 let fast = FastDbc::new(dbc);
640 let ids: Vec<u32> = fast.ids().collect();
641
642 assert_eq!(ids.len(), 3);
643 assert!(ids.contains(&100));
644 assert!(ids.contains(&200));
645 assert!(ids.contains(&3000));
646 }
647
648 #[test]
649 fn test_fast_dbc_decode_raw_into() {
650 let dbc = Dbc::parse(
651 r#"VERSION "1.0"
652
653BU_: ECM
654
655BO_ 256 Engine : 8 ECM
656 SG_ RPM : 0|16@1+ (0.25,0) [0|8000] "rpm" *
657"#,
658 )
659 .unwrap();
660
661 let fast = FastDbc::new(dbc);
662
663 let payload = [0x40, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00];
664 let mut raw_values = [0i64; 8];
665
666 let count = fast.decode_raw_into(256, &payload, &mut raw_values).unwrap();
667
668 assert_eq!(count, 1);
669 assert_eq!(raw_values[0], 8000); }
671}