dbc_rs/message/
decode.rs

1//! High-performance decoding for Message.
2
3use super::Message;
4
5impl Message {
6    /// Decode all signals into the output buffer (physical values).
7    ///
8    /// This is a zero-allocation decode path for high-speed CAN processing.
9    /// Signals are decoded in order and written to the output buffer.
10    ///
11    /// # Arguments
12    /// * `data` - Raw CAN payload bytes
13    /// * `out` - Output buffer for physical values (must be at least `signals().len()` long)
14    ///
15    /// # Returns
16    /// Number of signals decoded, or 0 if payload is too short.
17    ///
18    /// # Example
19    /// ```rust,ignore
20    /// let msg = dbc.messages().find_by_id(256).unwrap();
21    /// let mut values = [0.0f64; 64];
22    /// let count = msg.decode_into(&payload, &mut values);
23    /// for i in 0..count {
24    ///     let signal = msg.signals().at(i).unwrap();
25    ///     println!("{}: {}", signal.name(), values[i]);
26    /// }
27    /// ```
28    #[inline]
29    pub fn decode_into(&self, data: &[u8], out: &mut [f64]) -> usize {
30        // Check minimum payload length
31        let min_bytes = self.min_bytes_required() as usize;
32        if data.len() < min_bytes {
33            return 0;
34        }
35
36        let signals = self.signals();
37        let count = signals.len().min(out.len());
38
39        for (i, out_val) in out.iter_mut().enumerate().take(count) {
40            if let Some(signal) = signals.at(i) {
41                // Use decode_raw which returns (raw_value, physical_value)
42                if let Ok((_, physical)) = signal.decode_raw(data) {
43                    *out_val = physical;
44                } else {
45                    *out_val = 0.0;
46                }
47            }
48        }
49
50        count
51    }
52
53    /// Decode all signals into the output buffer (raw integer values).
54    ///
55    /// Returns raw values before factor/offset conversion.
56    /// Useful for encoding or debugging.
57    ///
58    /// # Arguments
59    /// * `data` - Raw CAN payload bytes
60    /// * `out` - Output buffer for raw values (must be at least `signals().len()` long)
61    ///
62    /// # Returns
63    /// Number of signals decoded, or 0 if payload is too short.
64    #[inline]
65    pub fn decode_raw_into(&self, data: &[u8], out: &mut [i64]) -> usize {
66        let min_bytes = self.min_bytes_required() as usize;
67        if data.len() < min_bytes {
68            return 0;
69        }
70
71        let signals = self.signals();
72        let count = signals.len().min(out.len());
73
74        for (i, out_val) in out.iter_mut().enumerate().take(count) {
75            if let Some(signal) = signals.at(i) {
76                if let Ok((raw, _)) = signal.decode_raw(data) {
77                    *out_val = raw;
78                } else {
79                    *out_val = 0;
80                }
81            }
82        }
83
84        count
85    }
86
87    /// Decode a single signal by index.
88    ///
89    /// Returns the physical value or `None` if index is out of bounds or decode fails.
90    #[inline]
91    pub fn decode_signal(&self, index: usize, data: &[u8]) -> Option<f64> {
92        let signal = self.signals().at(index)?;
93        signal.decode_raw(data).ok().map(|(_, physical)| physical)
94    }
95
96    /// Decode a single signal by index (raw value).
97    ///
98    /// Returns the raw integer value or `None` if index is out of bounds or decode fails.
99    #[inline]
100    pub fn decode_signal_raw(&self, index: usize, data: &[u8]) -> Option<i64> {
101        let signal = self.signals().at(index)?;
102        signal.decode_raw(data).ok().map(|(raw, _)| raw)
103    }
104}
105
106#[cfg(test)]
107mod tests {
108    use crate::Dbc;
109
110    #[test]
111    fn test_decode_into_basic() {
112        let dbc = Dbc::parse(
113            r#"VERSION "1.0"
114
115BU_: ECM
116
117BO_ 256 Engine : 8 ECM
118 SG_ RPM : 0|16@1+ (0.25,0) [0|8000] "rpm" *
119 SG_ Temp : 16|8@1- (1,-40) [-40|215] "C" *
120"#,
121        )
122        .unwrap();
123
124        let msg = dbc.messages().find_by_id(256).unwrap();
125
126        // RPM = 2000 (raw 8000 = 0x1F40), Temp = 50°C (raw 90 = 0x5A)
127        let payload = [0x40, 0x1F, 0x5A, 0x00, 0x00, 0x00, 0x00, 0x00];
128        let mut values = [0.0f64; 8];
129
130        let count = msg.decode_into(&payload, &mut values);
131
132        assert_eq!(count, 2);
133        assert_eq!(values[0], 2000.0); // RPM
134        assert_eq!(values[1], 50.0); // Temp
135    }
136
137    #[test]
138    fn test_decode_raw_into() {
139        let dbc = Dbc::parse(
140            r#"VERSION "1.0"
141
142BU_: ECM
143
144BO_ 256 Engine : 8 ECM
145 SG_ RPM : 0|16@1+ (0.25,0) [0|8000] "rpm" *
146"#,
147        )
148        .unwrap();
149
150        let msg = dbc.messages().find_by_id(256).unwrap();
151
152        let payload = [0x40, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00];
153        let mut raw_values = [0i64; 8];
154
155        let count = msg.decode_raw_into(&payload, &mut raw_values);
156
157        assert_eq!(count, 1);
158        assert_eq!(raw_values[0], 8000); // Raw before factor
159    }
160
161    #[test]
162    fn test_decode_into_payload_too_short() {
163        let dbc = Dbc::parse(
164            r#"VERSION "1.0"
165
166BU_: ECM
167
168BO_ 256 Engine : 8 ECM
169 SG_ RPM : 0|16@1+ (1,0) [0|65535] "rpm" *
170"#,
171        )
172        .unwrap();
173
174        let msg = dbc.messages().find_by_id(256).unwrap();
175
176        // Payload too short (need 2 bytes for 16-bit signal)
177        let payload = [0x40];
178        let mut values = [0.0f64; 8];
179
180        let count = msg.decode_into(&payload, &mut values);
181
182        assert_eq!(count, 0);
183    }
184
185    #[test]
186    fn test_decode_signal_by_index() {
187        let dbc = Dbc::parse(
188            r#"VERSION "1.0"
189
190BU_: ECM
191
192BO_ 256 Engine : 8 ECM
193 SG_ RPM : 0|16@1+ (0.25,0) [0|8000] "rpm" *
194 SG_ Temp : 16|8@1- (1,-40) [-40|215] "C" *
195"#,
196        )
197        .unwrap();
198
199        let msg = dbc.messages().find_by_id(256).unwrap();
200        let payload = [0x40, 0x1F, 0x5A, 0x00, 0x00, 0x00, 0x00, 0x00];
201
202        assert_eq!(msg.decode_signal(0, &payload), Some(2000.0));
203        assert_eq!(msg.decode_signal(1, &payload), Some(50.0));
204        assert_eq!(msg.decode_signal(2, &payload), None); // Out of bounds
205    }
206
207    #[test]
208    fn test_decode_into_buffer_smaller_than_signals() {
209        let dbc = Dbc::parse(
210            r#"VERSION "1.0"
211
212BU_: ECM
213
214BO_ 256 Engine : 8 ECM
215 SG_ Sig1 : 0|8@1+ (1,0) [0|255] "" *
216 SG_ Sig2 : 8|8@1+ (1,0) [0|255] "" *
217 SG_ Sig3 : 16|8@1+ (1,0) [0|255] "" *
218"#,
219        )
220        .unwrap();
221
222        let msg = dbc.messages().find_by_id(256).unwrap();
223        let payload = [0x01, 0x02, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00];
224
225        // Buffer only has space for 2 values, but message has 3 signals
226        let mut values = [0.0f64; 2];
227        let count = msg.decode_into(&payload, &mut values);
228
229        assert_eq!(count, 2);
230        assert_eq!(values[0], 1.0);
231        assert_eq!(values[1], 2.0);
232    }
233}