1mod fast_dbc_inner;
30pub(crate) use fast_dbc_inner::FastDbcInner;
31
32mod decode;
33mod hasher;
34
35use crate::{ByteOrder, Dbc, Message, Result};
36use decode::{DecodePlan, SignalDecode};
37use hasher::FxHashMap;
38use std::collections::HashMap;
39use std::path::Path;
40use std::sync::Arc;
41
42const MAX_STANDARD_ID: usize = 2048;
44
45#[derive(Clone)]
61pub struct FastDbc {
62 inner: Arc<FastDbcInner>,
63}
64
65impl std::fmt::Debug for FastDbc {
66 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
67 f.debug_struct("FastDbc")
68 .field("message_count", &self.message_count())
69 .field("max_signals", &self.max_signals())
70 .field("total_signals", &self.total_signals())
71 .finish()
72 }
73}
74
75impl FastDbc {
76 pub fn from_file<P: AsRef<Path>>(path: P) -> Result<Self> {
78 let dbc = Dbc::from_file(path)?;
79 Ok(Self::new(dbc))
80 }
81
82 pub fn new(dbc: Dbc) -> Self {
86 let mut standard_ids = Box::new([usize::MAX; MAX_STANDARD_ID]);
87 let mut extended_ids: FxHashMap<u32, usize> =
88 HashMap::with_capacity_and_hasher(16, Default::default());
89 let mut decode_plans = Vec::with_capacity(dbc.messages().len());
90 let mut max_signals = 0;
91 let mut total_signals = 0;
92
93 for (msg_idx, msg) in dbc.messages().iter().enumerate() {
94 let plan_idx = decode_plans.len();
95
96 let signals: Vec<SignalDecode> =
98 msg.signals().iter().map(SignalDecode::from_signal).collect();
99
100 let sig_count = signals.len();
101 max_signals = max_signals.max(sig_count);
102 total_signals += sig_count;
103
104 decode_plans.push(DecodePlan {
105 message_index: msg_idx,
106 min_bytes: msg.min_bytes_required(),
107 signals,
108 });
109
110 let id = msg.id_with_flag();
112 if !msg.is_extended() && id < MAX_STANDARD_ID as u32 {
113 standard_ids[id as usize] = plan_idx;
114 } else {
115 extended_ids.insert(id, plan_idx);
116 }
117 }
118
119 Self {
120 inner: Arc::new(FastDbcInner {
121 dbc,
122 standard_ids,
123 extended_ids,
124 decode_plans,
125 max_signals,
126 total_signals,
127 }),
128 }
129 }
130
131 #[inline(always)]
137 fn get_plan_index(&self, id: u32) -> Option<usize> {
138 if id < MAX_STANDARD_ID as u32 {
139 let idx = self.inner.standard_ids[id as usize];
141 if idx != usize::MAX { Some(idx) } else { None }
142 } else {
143 self.inner.extended_ids.get(&id).copied()
145 }
146 }
147
148 #[inline(always)]
150 fn get_plan_index_extended(&self, id: u32) -> Option<usize> {
151 let extended_id = id | Message::EXTENDED_ID_FLAG;
152 self.inner.extended_ids.get(&extended_id).copied()
153 }
154
155 #[inline]
157 pub fn get(&self, id: u32) -> Option<&Message> {
158 self.get_plan_index(id)
159 .map(|idx| &self.inner.decode_plans[idx])
160 .and_then(|plan| self.inner.dbc.messages().at(plan.message_index))
161 }
162
163 #[inline]
165 pub fn get_extended(&self, id: u32) -> Option<&Message> {
166 self.get_plan_index_extended(id)
167 .map(|idx| &self.inner.decode_plans[idx])
168 .and_then(|plan| self.inner.dbc.messages().at(plan.message_index))
169 }
170
171 #[inline]
173 pub fn get_any(&self, id: u32) -> Option<&Message> {
174 self.get(id).or_else(|| self.get_extended(id))
175 }
176
177 #[inline]
197 pub fn decode_into(&self, id: u32, data: &[u8], out: &mut [f64]) -> Option<usize> {
198 let plan_idx = self.get_plan_index(id)?;
199 let plan = &self.inner.decode_plans[plan_idx];
200
201 if data.len() < plan.min_bytes as usize {
202 return None;
203 }
204
205 Some(self.decode_with_plan(plan, data, out))
206 }
207
208 #[inline]
210 pub fn decode_extended_into(&self, id: u32, data: &[u8], out: &mut [f64]) -> Option<usize> {
211 let plan_idx = self.get_plan_index_extended(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]
235 pub fn decode<'a>(
236 &'a self,
237 id: u32,
238 data: &'a [u8],
239 is_extended: bool,
240 ) -> Option<impl Iterator<Item = (&'a str, f64)> + 'a> {
241 let plan_idx = if is_extended {
242 self.get_plan_index_extended(id)
243 } else {
244 self.get_plan_index(id)
245 };
246 self.decode_named_with_plan(plan_idx?, data)
247 }
248
249 #[inline]
251 pub fn decode_raw_into(&self, id: u32, data: &[u8], out: &mut [i64]) -> Option<usize> {
252 let plan_idx = self.get_plan_index(id)?;
253 let plan = &self.inner.decode_plans[plan_idx];
254
255 if data.len() < plan.min_bytes as usize {
256 return None;
257 }
258
259 Some(self.decode_raw_with_plan(plan, data, out))
260 }
261
262 #[inline]
268 fn decode_named_with_plan<'a>(
269 &'a self,
270 plan_idx: usize,
271 data: &'a [u8],
272 ) -> Option<impl Iterator<Item = (&'a str, f64)> + 'a> {
273 let plan = &self.inner.decode_plans[plan_idx];
274
275 if data.len() < plan.min_bytes as usize {
276 return None;
277 }
278
279 let message = self.inner.dbc.messages().at(plan.message_index)?;
280 Some(
281 message
282 .signals()
283 .iter()
284 .zip(plan.signals.iter())
285 .map(move |(signal, sig)| (signal.name(), self.decode_physical(*sig, data))),
286 )
287 }
288
289 #[inline(always)]
291 fn decode_with_plan(&self, plan: &DecodePlan, data: &[u8], out: &mut [f64]) -> usize {
292 let mut count = 0;
293 for (out_val, sig) in out.iter_mut().zip(plan.signals.iter()) {
294 *out_val = self.decode_physical(*sig, data);
295 count += 1;
296 }
297 count
298 }
299
300 #[inline(always)]
302 fn decode_physical(&self, sig: SignalDecode, data: &[u8]) -> f64 {
303 let raw_bits = self.extract_bits(sig, data);
307 let raw = if sig.is_unsigned() {
308 raw_bits as f64
309 } else {
310 Self::sign_extend(raw_bits, sig.length as usize) as f64
311 };
312 self.apply_scaling(sig, raw)
313 }
314
315 #[inline(always)]
317 fn decode_raw_with_plan(&self, plan: &DecodePlan, data: &[u8], out: &mut [i64]) -> usize {
318 let mut count = 0;
319 for (out_val, sig) in out.iter_mut().zip(plan.signals.iter()) {
320 *out_val = self.extract_raw(*sig, data);
321 count += 1;
322 }
323 count
324 }
325
326 #[inline(always)]
328 fn extract_bits(&self, sig: SignalDecode, data: &[u8]) -> u64 {
329 let byte_order = if sig.is_little_endian() {
330 ByteOrder::LittleEndian
331 } else {
332 ByteOrder::BigEndian
333 };
334
335 let start_bit = sig.byte_start as usize * 8 + sig.bit_offset as usize;
336 byte_order.extract_bits(data, start_bit, sig.length as usize)
337 }
338
339 #[inline(always)]
341 fn extract_raw(&self, sig: SignalDecode, data: &[u8]) -> i64 {
342 let raw_bits = self.extract_bits(sig, data);
343
344 if sig.is_unsigned() {
345 raw_bits as i64
346 } else {
347 Self::sign_extend(raw_bits, sig.length as usize)
348 }
349 }
350
351 #[inline(always)]
353 fn apply_scaling(&self, sig: SignalDecode, raw: f64) -> f64 {
354 if sig.is_identity() {
355 raw
356 } else {
357 raw * sig.factor + sig.offset
358 }
359 }
360
361 #[inline(always)]
363 fn sign_extend(value: u64, bits: usize) -> i64 {
364 if bits >= 64 {
367 return value as i64;
368 }
369 let sign_bit = 1u64 << (bits - 1);
370 if (value & sign_bit) != 0 {
371 let mask = !((1u64 << bits) - 1);
372 (value | mask) as i64
373 } else {
374 value as i64
375 }
376 }
377
378 #[inline]
386 pub fn max_signals(&self) -> usize {
387 self.inner.max_signals
388 }
389
390 #[inline]
392 pub fn total_signals(&self) -> usize {
393 self.inner.total_signals
394 }
395
396 #[inline]
398 pub fn message_count(&self) -> usize {
399 self.inner.decode_plans.len()
400 }
401
402 #[inline]
404 pub fn contains(&self, id: u32) -> bool {
405 self.get_plan_index(id).is_some()
406 }
407
408 #[inline]
410 pub fn contains_extended(&self, id: u32) -> bool {
411 self.get_plan_index_extended(id).is_some()
412 }
413
414 #[inline]
416 pub fn dbc(&self) -> &Dbc {
417 &self.inner.dbc
418 }
419
420 #[inline]
424 pub fn into_dbc(self) -> Dbc {
425 match Arc::try_unwrap(self.inner) {
426 Ok(inner) => inner.dbc,
427 Err(arc) => arc.dbc.clone(),
428 }
429 }
430
431 pub fn ids(&self) -> impl Iterator<Item = u32> + '_ {
433 let standard = self
435 .inner
436 .standard_ids
437 .iter()
438 .enumerate()
439 .filter(|(_, idx)| **idx != usize::MAX)
440 .map(|(id, _)| id as u32);
441
442 let extended = self.inner.extended_ids.keys().copied();
444
445 standard.chain(extended)
446 }
447}
448
449impl From<Dbc> for FastDbc {
450 fn from(dbc: Dbc) -> Self {
451 Self::new(dbc)
452 }
453}
454
455#[cfg(test)]
460mod tests {
461 use super::*;
462
463 #[test]
464 fn test_fast_dbc_basic() {
465 let dbc = Dbc::parse(
466 r#"VERSION "1.0"
467
468BU_: ECM
469
470BO_ 256 Engine : 8 ECM
471 SG_ RPM : 0|16@1+ (0.25,0) [0|8000] "rpm" *
472 SG_ Temp : 16|8@1- (1,-40) [-40|215] "C" *
473"#,
474 )
475 .unwrap();
476
477 let fast = FastDbc::new(dbc);
478
479 assert_eq!(fast.message_count(), 1);
480 assert_eq!(fast.max_signals(), 2);
481 assert_eq!(fast.total_signals(), 2);
482 assert!(fast.contains(256));
483 assert!(!fast.contains(512));
484
485 let msg = fast.get(256).unwrap();
486 assert_eq!(msg.name(), "Engine");
487 }
488
489 #[test]
490 fn test_fast_dbc_decode_named() {
491 let dbc = Dbc::parse(
492 r#"VERSION "1.0"
493
494BU_: ECM
495
496BO_ 256 Engine : 8 ECM
497 SG_ RPM : 0|16@1+ (0.25,0) [0|8000] "rpm" *
498 SG_ Temp : 16|8@1- (1,-40) [-40|215] "C" *
499"#,
500 )
501 .unwrap();
502
503 let fast = FastDbc::new(dbc);
504
505 let payload = [0x40, 0x1F, 0x5A, 0x00, 0x00, 0x00, 0x00, 0x00];
507 let pairs: Vec<(&str, f64)> = fast.decode(256, &payload, false).unwrap().collect();
508 assert_eq!(pairs, vec![("RPM", 2000.0), ("Temp", 50.0)]);
509
510 assert!(fast.decode(512, &payload, false).is_none());
512 assert!(fast.decode(256, &[0u8; 1], false).is_none());
513 }
514
515 #[test]
516 fn test_fast_dbc_decode_named_extended() {
517 let dbc = Dbc::parse(
519 r#"VERSION "1.0"
520
521BU_: ECM
522
523BO_ 2147483904 Engine : 8 ECM
524 SG_ RPM : 0|16@1+ (0.25,0) [0|8000] "rpm" *
525"#,
526 )
527 .unwrap();
528
529 let fast = FastDbc::new(dbc);
530
531 let payload = [0x40, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00];
532 let pairs: Vec<(&str, f64)> = fast.decode(256, &payload, true).unwrap().collect();
533 assert_eq!(pairs, vec![("RPM", 2000.0)]);
534
535 assert!(fast.decode(256, &payload, false).is_none());
537 }
538
539 #[test]
540 fn test_fast_dbc_unsigned_64bit_msb_not_negative() {
541 let dbc = Dbc::parse(
546 r#"VERSION "1.0"
547
548BU_: ECM
549
550BO_ 256 Wide : 8 ECM
551 SG_ Serial : 0|64@1+ (1,0) [0|1] "" *
552
553BO_ 257 WideSigned : 8 ECM
554 SG_ Signed : 0|64@1- (1,0) [-1|1] "" *
555"#,
556 )
557 .unwrap();
558
559 let fast = FastDbc::new(dbc);
560 let mut values = vec![0.0f64; fast.max_signals()];
561
562 let payload = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80];
564 assert_eq!(fast.decode_into(256, &payload, &mut values), Some(1));
565 assert_eq!(values[0], 9223372036854775808.0);
566 assert_eq!(fast.decode_into(257, &payload, &mut values), Some(1));
567 assert_eq!(values[0], -9223372036854775808.0);
568
569 let payload = [0xFF; 8];
571 assert_eq!(fast.decode_into(256, &payload, &mut values), Some(1));
572 assert_eq!(values[0], u64::MAX as f64);
573 assert_eq!(fast.decode_into(257, &payload, &mut values), Some(1));
574 assert_eq!(values[0], -1.0);
575 }
576
577 #[test]
578 fn test_fast_dbc_decode_into() {
579 let dbc = Dbc::parse(
580 r#"VERSION "1.0"
581
582BU_: ECM
583
584BO_ 256 Engine : 8 ECM
585 SG_ RPM : 0|16@1+ (0.25,0) [0|8000] "rpm" *
586 SG_ Temp : 16|8@1- (1,-40) [-40|215] "C" *
587"#,
588 )
589 .unwrap();
590
591 let fast = FastDbc::new(dbc);
592
593 let payload = [0x40, 0x1F, 0x5A, 0x00, 0x00, 0x00, 0x00, 0x00];
595 let mut values = vec![0.0f64; fast.max_signals()];
596
597 let count = fast.decode_into(256, &payload, &mut values).unwrap();
598
599 assert_eq!(count, 2);
600 assert_eq!(values[0], 2000.0);
601 assert_eq!(values[1], 50.0);
602 }
603
604 #[test]
605 fn test_fast_dbc_identity_transform() {
606 let dbc = Dbc::parse(
607 r#"VERSION "1.0"
608
609BU_: ECM
610
611BO_ 256 Engine : 8 ECM
612 SG_ RawValue : 0|16@1+ (1,0) [0|65535] "" *
613"#,
614 )
615 .unwrap();
616
617 let fast = FastDbc::new(dbc);
618
619 let payload = [0x39, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00];
621 let mut values = [0.0f64; 1];
622
623 fast.decode_into(256, &payload, &mut values);
624
625 assert_eq!(values[0], 12345.0);
626 }
627
628 #[test]
629 fn test_fast_dbc_message_not_found() {
630 let dbc = Dbc::parse(
631 r#"VERSION "1.0"
632
633BU_: ECM
634
635BO_ 256 Engine : 8 ECM
636 SG_ RPM : 0|16@1+ (1,0) [0|8000] "rpm" *
637"#,
638 )
639 .unwrap();
640
641 let fast = FastDbc::new(dbc);
642 let payload = [0x00; 8];
643 let mut values = [0.0f64; 8];
644
645 assert!(fast.decode_into(512, &payload, &mut values).is_none());
646 }
647
648 #[test]
649 fn test_fast_dbc_extended_id() {
650 let dbc = Dbc::parse(
651 r#"VERSION "1.0"
652
653BU_: ECM
654
655BO_ 2147484672 ExtendedMsg : 8 ECM
656 SG_ Speed : 0|16@1+ (0.1,0) [0|6553.5] "km/h" *
657"#,
658 )
659 .unwrap();
660 let fast = FastDbc::new(dbc);
663
664 assert!(!fast.contains(0x400));
666 assert!(fast.get(0x400).is_none());
667
668 assert!(fast.contains_extended(0x400));
670 let msg = fast.get_extended(0x400).unwrap();
671 assert_eq!(msg.name(), "ExtendedMsg");
672
673 let payload = [0xE8, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00];
675 let mut values = [0.0f64; 8];
676
677 let count = fast.decode_extended_into(0x400, &payload, &mut values).unwrap();
678 assert_eq!(count, 1);
679 assert_eq!(values[0], 100.0); }
681
682 #[test]
683 fn test_fast_dbc_multiple_messages() {
684 let dbc = Dbc::parse(
685 r#"VERSION "1.0"
686
687BU_: ECM
688
689BO_ 256 Msg1 : 8 ECM
690 SG_ Sig1 : 0|8@1+ (1,0) [0|255] "" *
691 SG_ Sig2 : 8|8@1+ (1,0) [0|255] "" *
692
693BO_ 512 Msg2 : 8 ECM
694 SG_ SigA : 0|16@1+ (1,0) [0|65535] "" *
695
696BO_ 768 Msg3 : 8 ECM
697 SG_ SigX : 0|8@1+ (1,0) [0|255] "" *
698 SG_ SigY : 8|8@1+ (1,0) [0|255] "" *
699 SG_ SigZ : 16|8@1+ (1,0) [0|255] "" *
700"#,
701 )
702 .unwrap();
703
704 let fast = FastDbc::new(dbc);
705
706 assert_eq!(fast.message_count(), 3);
707 assert_eq!(fast.max_signals(), 3); assert_eq!(fast.total_signals(), 6);
709
710 assert!(fast.contains(256));
711 assert!(fast.contains(512));
712 assert!(fast.contains(768));
713 }
714
715 #[test]
716 fn test_fast_dbc_large_id() {
717 let dbc = Dbc::parse(
719 r#"VERSION "1.0"
720
721BU_: ECM
722
723BO_ 3000 LargeId : 8 ECM
724 SG_ Value : 0|16@1+ (1,0) [0|65535] "" *
725"#,
726 )
727 .unwrap();
728
729 let fast = FastDbc::new(dbc);
730
731 assert!(fast.contains(3000));
732 assert!(!fast.contains(256));
733
734 let payload = [0x39, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00];
735 let mut values = [0.0f64; 1];
736
737 let count = fast.decode_into(3000, &payload, &mut values).unwrap();
738 assert_eq!(count, 1);
739 assert_eq!(values[0], 12345.0);
740 }
741
742 #[test]
743 fn test_fast_dbc_from_trait() {
744 let dbc = Dbc::parse(
745 r#"VERSION "1.0"
746
747BU_: ECM
748
749BO_ 256 Engine : 8 ECM
750"#,
751 )
752 .unwrap();
753
754 let fast: FastDbc = dbc.into();
755 assert_eq!(fast.message_count(), 1);
756 }
757
758 #[test]
759 fn test_fast_dbc_into_dbc() {
760 let dbc = Dbc::parse(
761 r#"VERSION "1.0"
762
763BU_: ECM
764
765BO_ 256 Engine : 8 ECM
766"#,
767 )
768 .unwrap();
769
770 let fast = FastDbc::new(dbc);
771 let dbc_back = fast.into_dbc();
772
773 assert_eq!(dbc_back.messages().len(), 1);
774 }
775
776 #[test]
777 fn test_fast_dbc_ids_iterator() {
778 let dbc = Dbc::parse(
779 r#"VERSION "1.0"
780
781BU_: ECM
782
783BO_ 100 Msg1 : 8 ECM
784BO_ 200 Msg2 : 8 ECM
785BO_ 3000 LargeId : 8 ECM
786"#,
787 )
788 .unwrap();
789
790 let fast = FastDbc::new(dbc);
791 let ids: Vec<u32> = fast.ids().collect();
792
793 assert_eq!(ids.len(), 3);
794 assert!(ids.contains(&100));
795 assert!(ids.contains(&200));
796 assert!(ids.contains(&3000));
797 }
798
799 #[test]
800 fn test_fast_dbc_decode_raw_into() {
801 let dbc = Dbc::parse(
802 r#"VERSION "1.0"
803
804BU_: ECM
805
806BO_ 256 Engine : 8 ECM
807 SG_ RPM : 0|16@1+ (0.25,0) [0|8000] "rpm" *
808"#,
809 )
810 .unwrap();
811
812 let fast = FastDbc::new(dbc);
813
814 let payload = [0x40, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00];
815 let mut raw_values = [0i64; 8];
816
817 let count = fast.decode_raw_into(256, &payload, &mut raw_values).unwrap();
818
819 assert_eq!(count, 1);
820 assert_eq!(raw_values[0], 8000); }
822}