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]
223 pub fn decode_raw_into(&self, id: u32, data: &[u8], out: &mut [i64]) -> Option<usize> {
224 let plan_idx = self.get_plan_index(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_raw_with_plan(plan, data, out))
232 }
233
234 #[inline(always)]
240 fn decode_with_plan(&self, plan: &DecodePlan, data: &[u8], out: &mut [f64]) -> usize {
241 let mut count = 0;
242 for (out_val, sig) in out.iter_mut().zip(plan.signals.iter()) {
243 let raw = self.extract_raw(*sig, data);
244 *out_val = self.apply_scaling(*sig, raw);
245 count += 1;
246 }
247 count
248 }
249
250 #[inline(always)]
252 fn decode_raw_with_plan(&self, plan: &DecodePlan, data: &[u8], out: &mut [i64]) -> usize {
253 let mut count = 0;
254 for (out_val, sig) in out.iter_mut().zip(plan.signals.iter()) {
255 *out_val = self.extract_raw(*sig, data);
256 count += 1;
257 }
258 count
259 }
260
261 #[inline(always)]
263 fn extract_raw(&self, sig: SignalDecode, data: &[u8]) -> i64 {
264 let byte_order = if sig.is_little_endian() {
265 ByteOrder::LittleEndian
266 } else {
267 ByteOrder::BigEndian
268 };
269
270 let start_bit = sig.byte_start as usize * 8 + sig.bit_offset as usize;
271 let raw_bits = byte_order.extract_bits(data, start_bit, sig.length as usize);
272
273 if sig.is_unsigned() {
274 raw_bits as i64
275 } else {
276 Self::sign_extend(raw_bits, sig.length as usize)
277 }
278 }
279
280 #[inline(always)]
282 fn apply_scaling(&self, sig: SignalDecode, raw: i64) -> f64 {
283 if sig.is_identity() {
284 raw as f64
285 } else {
286 (raw as f64) * sig.factor + sig.offset
287 }
288 }
289
290 #[inline(always)]
292 fn sign_extend(value: u64, bits: usize) -> i64 {
293 if bits >= 64 {
296 return value as i64;
297 }
298 let sign_bit = 1u64 << (bits - 1);
299 if (value & sign_bit) != 0 {
300 let mask = !((1u64 << bits) - 1);
301 (value | mask) as i64
302 } else {
303 value as i64
304 }
305 }
306
307 #[inline]
315 pub fn max_signals(&self) -> usize {
316 self.inner.max_signals
317 }
318
319 #[inline]
321 pub fn total_signals(&self) -> usize {
322 self.inner.total_signals
323 }
324
325 #[inline]
327 pub fn message_count(&self) -> usize {
328 self.inner.decode_plans.len()
329 }
330
331 #[inline]
333 pub fn contains(&self, id: u32) -> bool {
334 self.get_plan_index(id).is_some()
335 }
336
337 #[inline]
339 pub fn contains_extended(&self, id: u32) -> bool {
340 self.get_plan_index_extended(id).is_some()
341 }
342
343 #[inline]
345 pub fn dbc(&self) -> &Dbc {
346 &self.inner.dbc
347 }
348
349 #[inline]
353 pub fn into_dbc(self) -> Dbc {
354 match Arc::try_unwrap(self.inner) {
355 Ok(inner) => inner.dbc,
356 Err(arc) => arc.dbc.clone(),
357 }
358 }
359
360 pub fn ids(&self) -> impl Iterator<Item = u32> + '_ {
362 let standard = self
364 .inner
365 .standard_ids
366 .iter()
367 .enumerate()
368 .filter(|(_, idx)| **idx != usize::MAX)
369 .map(|(id, _)| id as u32);
370
371 let extended = self.inner.extended_ids.keys().copied();
373
374 standard.chain(extended)
375 }
376}
377
378impl From<Dbc> for FastDbc {
379 fn from(dbc: Dbc) -> Self {
380 Self::new(dbc)
381 }
382}
383
384#[cfg(test)]
389mod tests {
390 use super::*;
391
392 #[test]
393 fn test_fast_dbc_basic() {
394 let dbc = Dbc::parse(
395 r#"VERSION "1.0"
396
397BU_: ECM
398
399BO_ 256 Engine : 8 ECM
400 SG_ RPM : 0|16@1+ (0.25,0) [0|8000] "rpm" *
401 SG_ Temp : 16|8@1- (1,-40) [-40|215] "C" *
402"#,
403 )
404 .unwrap();
405
406 let fast = FastDbc::new(dbc);
407
408 assert_eq!(fast.message_count(), 1);
409 assert_eq!(fast.max_signals(), 2);
410 assert_eq!(fast.total_signals(), 2);
411 assert!(fast.contains(256));
412 assert!(!fast.contains(512));
413
414 let msg = fast.get(256).unwrap();
415 assert_eq!(msg.name(), "Engine");
416 }
417
418 #[test]
419 fn test_fast_dbc_decode_into() {
420 let dbc = Dbc::parse(
421 r#"VERSION "1.0"
422
423BU_: ECM
424
425BO_ 256 Engine : 8 ECM
426 SG_ RPM : 0|16@1+ (0.25,0) [0|8000] "rpm" *
427 SG_ Temp : 16|8@1- (1,-40) [-40|215] "C" *
428"#,
429 )
430 .unwrap();
431
432 let fast = FastDbc::new(dbc);
433
434 let payload = [0x40, 0x1F, 0x5A, 0x00, 0x00, 0x00, 0x00, 0x00];
436 let mut values = vec![0.0f64; fast.max_signals()];
437
438 let count = fast.decode_into(256, &payload, &mut values).unwrap();
439
440 assert_eq!(count, 2);
441 assert_eq!(values[0], 2000.0);
442 assert_eq!(values[1], 50.0);
443 }
444
445 #[test]
446 fn test_fast_dbc_identity_transform() {
447 let dbc = Dbc::parse(
448 r#"VERSION "1.0"
449
450BU_: ECM
451
452BO_ 256 Engine : 8 ECM
453 SG_ RawValue : 0|16@1+ (1,0) [0|65535] "" *
454"#,
455 )
456 .unwrap();
457
458 let fast = FastDbc::new(dbc);
459
460 let payload = [0x39, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00];
462 let mut values = [0.0f64; 1];
463
464 fast.decode_into(256, &payload, &mut values);
465
466 assert_eq!(values[0], 12345.0);
467 }
468
469 #[test]
470 fn test_fast_dbc_message_not_found() {
471 let dbc = Dbc::parse(
472 r#"VERSION "1.0"
473
474BU_: ECM
475
476BO_ 256 Engine : 8 ECM
477 SG_ RPM : 0|16@1+ (1,0) [0|8000] "rpm" *
478"#,
479 )
480 .unwrap();
481
482 let fast = FastDbc::new(dbc);
483 let payload = [0x00; 8];
484 let mut values = [0.0f64; 8];
485
486 assert!(fast.decode_into(512, &payload, &mut values).is_none());
487 }
488
489 #[test]
490 fn test_fast_dbc_extended_id() {
491 let dbc = Dbc::parse(
492 r#"VERSION "1.0"
493
494BU_: ECM
495
496BO_ 2147484672 ExtendedMsg : 8 ECM
497 SG_ Speed : 0|16@1+ (0.1,0) [0|6553.5] "km/h" *
498"#,
499 )
500 .unwrap();
501 let fast = FastDbc::new(dbc);
504
505 assert!(!fast.contains(0x400));
507 assert!(fast.get(0x400).is_none());
508
509 assert!(fast.contains_extended(0x400));
511 let msg = fast.get_extended(0x400).unwrap();
512 assert_eq!(msg.name(), "ExtendedMsg");
513
514 let payload = [0xE8, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00];
516 let mut values = [0.0f64; 8];
517
518 let count = fast.decode_extended_into(0x400, &payload, &mut values).unwrap();
519 assert_eq!(count, 1);
520 assert_eq!(values[0], 100.0); }
522
523 #[test]
524 fn test_fast_dbc_multiple_messages() {
525 let dbc = Dbc::parse(
526 r#"VERSION "1.0"
527
528BU_: ECM
529
530BO_ 256 Msg1 : 8 ECM
531 SG_ Sig1 : 0|8@1+ (1,0) [0|255] "" *
532 SG_ Sig2 : 8|8@1+ (1,0) [0|255] "" *
533
534BO_ 512 Msg2 : 8 ECM
535 SG_ SigA : 0|16@1+ (1,0) [0|65535] "" *
536
537BO_ 768 Msg3 : 8 ECM
538 SG_ SigX : 0|8@1+ (1,0) [0|255] "" *
539 SG_ SigY : 8|8@1+ (1,0) [0|255] "" *
540 SG_ SigZ : 16|8@1+ (1,0) [0|255] "" *
541"#,
542 )
543 .unwrap();
544
545 let fast = FastDbc::new(dbc);
546
547 assert_eq!(fast.message_count(), 3);
548 assert_eq!(fast.max_signals(), 3); assert_eq!(fast.total_signals(), 6);
550
551 assert!(fast.contains(256));
552 assert!(fast.contains(512));
553 assert!(fast.contains(768));
554 }
555
556 #[test]
557 fn test_fast_dbc_large_id() {
558 let dbc = Dbc::parse(
560 r#"VERSION "1.0"
561
562BU_: ECM
563
564BO_ 3000 LargeId : 8 ECM
565 SG_ Value : 0|16@1+ (1,0) [0|65535] "" *
566"#,
567 )
568 .unwrap();
569
570 let fast = FastDbc::new(dbc);
571
572 assert!(fast.contains(3000));
573 assert!(!fast.contains(256));
574
575 let payload = [0x39, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00];
576 let mut values = [0.0f64; 1];
577
578 let count = fast.decode_into(3000, &payload, &mut values).unwrap();
579 assert_eq!(count, 1);
580 assert_eq!(values[0], 12345.0);
581 }
582
583 #[test]
584 fn test_fast_dbc_from_trait() {
585 let dbc = Dbc::parse(
586 r#"VERSION "1.0"
587
588BU_: ECM
589
590BO_ 256 Engine : 8 ECM
591"#,
592 )
593 .unwrap();
594
595 let fast: FastDbc = dbc.into();
596 assert_eq!(fast.message_count(), 1);
597 }
598
599 #[test]
600 fn test_fast_dbc_into_dbc() {
601 let dbc = Dbc::parse(
602 r#"VERSION "1.0"
603
604BU_: ECM
605
606BO_ 256 Engine : 8 ECM
607"#,
608 )
609 .unwrap();
610
611 let fast = FastDbc::new(dbc);
612 let dbc_back = fast.into_dbc();
613
614 assert_eq!(dbc_back.messages().len(), 1);
615 }
616
617 #[test]
618 fn test_fast_dbc_ids_iterator() {
619 let dbc = Dbc::parse(
620 r#"VERSION "1.0"
621
622BU_: ECM
623
624BO_ 100 Msg1 : 8 ECM
625BO_ 200 Msg2 : 8 ECM
626BO_ 3000 LargeId : 8 ECM
627"#,
628 )
629 .unwrap();
630
631 let fast = FastDbc::new(dbc);
632 let ids: Vec<u32> = fast.ids().collect();
633
634 assert_eq!(ids.len(), 3);
635 assert!(ids.contains(&100));
636 assert!(ids.contains(&200));
637 assert!(ids.contains(&3000));
638 }
639
640 #[test]
641 fn test_fast_dbc_decode_raw_into() {
642 let dbc = Dbc::parse(
643 r#"VERSION "1.0"
644
645BU_: ECM
646
647BO_ 256 Engine : 8 ECM
648 SG_ RPM : 0|16@1+ (0.25,0) [0|8000] "rpm" *
649"#,
650 )
651 .unwrap();
652
653 let fast = FastDbc::new(dbc);
654
655 let payload = [0x40, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00];
656 let mut raw_values = [0i64; 8];
657
658 let count = fast.decode_raw_into(256, &payload, &mut raw_values).unwrap();
659
660 assert_eq!(count, 1);
661 assert_eq!(raw_values[0], 8000); }
663}