dbc_rs/message/
message.rs

1use crate::{
2    ByteOrder, Parser, Signal,
3    error::{ParseError, ParseResult},
4};
5
6use super::Signals;
7
8/// Represents a CAN message in a DBC file.
9///
10/// A `Message` contains:
11/// - A unique ID (CAN identifier)
12/// - A name
13/// - A DLC (Data Length Code) specifying the message size in bytes
14/// - A sender node (ECU that transmits this message)
15/// - A collection of signals
16///
17/// # Examples
18///
19/// ```rust,no_run
20/// use dbc_rs::Dbc;
21///
22/// let dbc_content = r#"VERSION "1.0"
23///
24/// BU_: ECM
25///
26/// BO_ 256 EngineData : 8 ECM
27///  SG_ RPM : 0|16@0+ (0.25,0) [0|8000] "rpm" *
28/// "#;
29///
30/// let dbc = Dbc::parse(dbc_content)?;
31/// let message = dbc.messages().at(0).unwrap();
32/// println!("Message: {} (ID: {})", message.name(), message.id());
33/// # Ok::<(), dbc_rs::Error>(())
34/// ```
35#[derive(Debug, Clone, PartialEq, Eq, Hash)]
36pub struct Message<'a> {
37    id: u32,
38    name: &'a str,
39    dlc: u8,
40    sender: &'a str,
41    signals: Signals<'a>,
42}
43
44impl<'a> Message<'a> {
45    #[allow(clippy::similar_names)] // physical_lsb and physical_msb are intentionally similar
46    fn calculate_bit_range(start_bit: u16, length: u16, byte_order: ByteOrder) -> (u16, u16) {
47        let start = start_bit;
48        let len = length;
49
50        match byte_order {
51            ByteOrder::LittleEndian => {
52                // Little-endian: start_bit is LSB, signal extends forward
53                // Range: [start_bit, start_bit + length - 1]
54                (start, start + len - 1)
55            }
56            ByteOrder::BigEndian => {
57                // Big-endian: start_bit is MSB in big-endian numbering, signal extends backward
58                // The big-endian bit numbering follows Vector convention:
59                // be_bits = [7, 6, 5, 4, 3, 2, 1, 0, 15, 14, 13, 12, 11, 10, 9, 8, 23, 22, ...]
60                // This means: BE bit 0 -> physical bit 7, BE bit 7 -> physical bit 0
61                //            BE bit 8 -> physical bit 15, BE bit 15 -> physical bit 8
62                // To find the physical bit range:
63                // 1. Find the index of start_bit in the be_bits sequence
64                // 2. MSB (physical) = be_bits[idx]
65                // 3. LSB (physical) = be_bits[idx + length - 1]
66                // We can calculate this directly:
67                // For BE bit N: byte_num = N / 8, bit_in_byte = N % 8
68                // Physical bit = byte_num * 8 + (7 - bit_in_byte)
69                let byte_num = start / 8;
70                let bit_in_byte = start % 8;
71                let physical_msb = byte_num * 8 + (7 - bit_in_byte);
72
73                // Calculate LSB: move forward (length - 1) positions in the BE sequence
74                // BE bit (start + length - 1) maps to physical bit
75                let lsb_be_bit = start + len - 1;
76                let lsb_byte_num = lsb_be_bit / 8;
77                let lsb_bit_in_byte = lsb_be_bit % 8;
78                let physical_lsb = lsb_byte_num * 8 + (7 - lsb_bit_in_byte);
79
80                // Ensure lsb <= msb (they should be in that order for big-endian)
81                if physical_lsb <= physical_msb {
82                    (physical_lsb, physical_msb)
83                } else {
84                    (physical_msb, physical_lsb)
85                }
86            }
87        }
88    }
89
90    #[allow(clippy::similar_names)] // Overlap detection uses intentionally similar variable names (sig1_lsb/sig1_msb, sig2_lsb/sig2_msb)
91    pub(crate) fn validate_internal(
92        id: u32,
93        name: &str,
94        dlc: u8,
95        sender: &str,
96        signals: &[Option<Signal<'a>>],
97        signal_count: usize,
98        options: crate::ParseOptions,
99    ) -> ParseResult<()> {
100        // Validate CAN ID range
101        // CAN specification allows:
102        // - Standard 11-bit IDs: 0x000 to 0x7FF (0-2047)
103        // - Extended 29-bit IDs: 0x00000000 to 0x1FFFFFFF (0-536870911)
104        // Note: Extended IDs can technically be 0-536870911, but DBC files typically
105        // use the convention where IDs 0-2047 are treated as standard and 2048+ as extended.
106        // We only validate the maximum range here; the distinction between standard/extended
107        // is determined by the ID value in practice.
108        const MAX_EXTENDED_ID: u32 = 0x1FFF_FFFF; // 536870911
109
110        // Check signal count limit per message (DoS protection)
111        const MAX_SIGNALS_PER_MESSAGE: usize = crate::Signals::max_capacity();
112        if signal_count > MAX_SIGNALS_PER_MESSAGE {
113            return Err(ParseError::Message(
114                crate::error::lang::MESSAGE_TOO_MANY_SIGNALS,
115            ));
116        }
117
118        if name.trim().is_empty() {
119            return Err(ParseError::Message(crate::error::lang::MESSAGE_NAME_EMPTY));
120        }
121
122        if sender.trim().is_empty() {
123            return Err(ParseError::Message(
124                crate::error::lang::MESSAGE_SENDER_EMPTY,
125            ));
126        }
127
128        // Validate DLC (Data Length Code): must be between 1 and 64 bytes
129        // - Classic CAN Standard (CAN 2.0A): DLC <= 8 bytes (64 bits) maximum payload
130        // - Classic CAN Extended (CAN 2.0B): DLC <= 8 bytes (64 bits) maximum payload
131        // - CAN FD (Flexible Data Rate, ISO/Bosch): DLC <= 64 bytes (512 bits) maximum payload
132        if dlc == 0 {
133            return Err(ParseError::Message(
134                crate::error::lang::MESSAGE_DLC_TOO_SMALL,
135            ));
136        }
137        if dlc > 64 {
138            return Err(ParseError::Message(
139                crate::error::lang::MESSAGE_DLC_TOO_LARGE,
140            ));
141        }
142
143        // Validate that ID is within valid CAN ID range
144        // Extended CAN IDs can be 0x00000000 to 0x1FFFFFFF (0 to 536870911)
145        // IDs exceeding 0x1FFFFFFF are invalid
146        if id > MAX_EXTENDED_ID {
147            return Err(ParseError::Message(
148                crate::error::lang::MESSAGE_ID_OUT_OF_RANGE,
149            ));
150        }
151
152        // Validate that all signals fit within the message boundary
153        // Each signal must fit within: DLC * 8 bits
154        // - Classic CAN (2.0A/2.0B): DLC * 8 <= 64 bits (8 bytes)
155        // - CAN FD: DLC * 8 <= 512 bits (64 bytes)
156        // This ensures no signal extends beyond the message payload capacity
157        let max_bits = u16::from(dlc) * 8;
158        for signal in signals.iter().take(signal_count).filter_map(|opt| opt.as_ref()) {
159            // Calculate the actual bit range for this signal (accounting for byte order)
160            let (lsb, msb) =
161                Self::calculate_bit_range(signal.start_bit(), signal.length(), signal.byte_order());
162            // Check if the signal extends beyond the message boundary
163            // The signal's highest bit position must be less than max_bits
164            let signal_max_bit = lsb.max(msb);
165            if signal_max_bit >= max_bits {
166                // Only fail if strict boundary checking is enabled
167                if options.strict_boundary_check {
168                    return Err(ParseError::Message(
169                        crate::error::lang::SIGNAL_LENGTH_TOO_LARGE,
170                    ));
171                }
172                // In lenient mode, we allow signals that extend beyond boundaries
173            }
174        }
175
176        // Validate signal overlap detection
177        // Check if any two signals overlap in the same message
178        // Must account for byte order: little-endian signals extend forward,
179        // big-endian signals extend backward from start_bit
180        // We iterate over pairs without collecting to avoid alloc
181        let signals_slice = &signals[..signal_count];
182        for (i, sig1_opt) in signals_slice.iter().enumerate() {
183            let sig1 = match sig1_opt {
184                Some(s) => s,
185                None => continue, // Should not happen, but be safe
186            };
187            let (sig1_lsb, sig1_msb) =
188                Self::calculate_bit_range(sig1.start_bit(), sig1.length(), sig1.byte_order());
189
190            for sig2_opt in signals_slice.iter().skip(i + 1) {
191                let sig2 = match sig2_opt {
192                    Some(s) => s,
193                    None => continue, // Should not happen, but be safe
194                };
195                let (sig2_lsb, sig2_msb) =
196                    Self::calculate_bit_range(sig2.start_bit(), sig2.length(), sig2.byte_order());
197
198                // Check if ranges overlap
199                // Two ranges [lsb1, msb1] and [lsb2, msb2] overlap if:
200                // lsb1 <= msb2 && lsb2 <= msb1
201                if sig1_lsb <= sig2_msb && sig2_lsb <= sig1_msb {
202                    return Err(ParseError::Message(crate::error::lang::SIGNAL_OVERLAP));
203                }
204            }
205        }
206
207        Ok(())
208    }
209
210    #[cfg(any(feature = "alloc", feature = "kernel"))]
211    pub(crate) fn new(
212        id: u32,
213        name: &'a str,
214        dlc: u8,
215        sender: &'a str,
216        signals: &'a [Signal<'a>],
217    ) -> Self {
218        // Validation should have been done prior (by builder or parse)
219        Self {
220            id,
221            name,
222            dlc,
223            sender,
224            signals: Signals::from_signals_slice(signals),
225        }
226    }
227
228    pub(crate) fn new_from_options(
229        id: u32,
230        name: &'a str,
231        dlc: u8,
232        sender: &'a str,
233        signals: &[Option<Signal<'a>>],
234        signal_count: usize,
235    ) -> Self {
236        // Validation should have been done prior (by builder or parse)
237        Self {
238            id,
239            name,
240            dlc,
241            sender,
242            signals: Signals::from_options_slice(signals, signal_count),
243        }
244    }
245
246    pub(crate) fn parse<'b: 'a>(
247        parser: &mut Parser<'b>,
248        signals: &[Option<Signal<'a>>],
249        signal_count: usize,
250        options: crate::ParseOptions,
251    ) -> ParseResult<Self> {
252        // Message parsing must always start with "BO_" keyword
253        parser
254            .expect(crate::BO_.as_bytes())
255            .map_err(|_| ParseError::Expected("Expected BO_ keyword"))?;
256
257        // Skip whitespace
258        let _ = parser.skip_whitespace();
259
260        // Parse message ID
261        let id = parser
262            .parse_u32()
263            .map_err(|_| ParseError::Message(crate::error::lang::MESSAGE_INVALID_ID))?;
264
265        // Skip whitespace
266        parser
267            .skip_whitespace()
268            .map_err(|_| ParseError::Expected("Expected whitespace"))?;
269
270        // Parse message name (identifier)
271        let name = parser
272            .parse_identifier()
273            .map_err(|_| ParseError::Message(crate::error::lang::MESSAGE_NAME_EMPTY))?;
274
275        // Skip whitespace (optional before colon)
276        let _ = parser.skip_whitespace();
277
278        // Expect colon
279        parser.expect(b":").map_err(|_| ParseError::Expected("Expected colon"))?;
280
281        // Skip whitespace after colon
282        let _ = parser.skip_whitespace();
283
284        // Parse DLC
285        let dlc = parser
286            .parse_u8()
287            .map_err(|_| ParseError::Message(crate::error::lang::MESSAGE_INVALID_DLC))?;
288
289        // Skip whitespace
290        parser
291            .skip_whitespace()
292            .map_err(|_| ParseError::Expected("Expected whitespace"))?;
293
294        // Parse sender (identifier, until end of line or whitespace)
295        let sender = parser
296            .parse_identifier()
297            .map_err(|_| ParseError::Message(crate::error::lang::MESSAGE_SENDER_EMPTY))?;
298
299        // Check for extra content after sender (invalid format)
300        parser.skip_newlines_and_spaces();
301        if !parser.is_empty() {
302            return Err(ParseError::Expected(
303                "Unexpected content after message sender",
304            ));
305        }
306
307        // Validate before construction
308        Self::validate_internal(
309            id,
310            name,
311            dlc,
312            sender,
313            &signals[..signal_count],
314            signal_count,
315            options,
316        )?;
317        // Construct directly (validation already done)
318        Ok(Self::new_from_options(
319            id,
320            name,
321            dlc,
322            sender,
323            signals,
324            signal_count,
325        ))
326    }
327
328    /// Returns the CAN message ID.
329    ///
330    /// # Examples
331    ///
332    /// ```rust,no_run
333    /// use dbc_rs::Dbc;
334    ///
335    /// let dbc = Dbc::parse(r#"VERSION "1.0"\n\nBU_: ECM\n\nBO_ 256 EngineData : 8 ECM"#)?;
336    /// let message = dbc.messages().at(0).unwrap();
337    /// assert_eq!(message.id(), 256);
338    /// # Ok::<(), dbc_rs::Error>(())
339    /// ```
340    #[inline]
341    #[must_use]
342    pub fn id(&self) -> u32 {
343        self.id
344    }
345
346    /// Returns the message name.
347    ///
348    /// # Examples
349    ///
350    /// ```rust,no_run
351    /// use dbc_rs::Dbc;
352    ///
353    /// let dbc = Dbc::parse(r#"VERSION "1.0"\n\nBU_: ECM\n\nBO_ 256 EngineData : 8 ECM"#)?;
354    /// let message = dbc.messages().at(0).unwrap();
355    /// assert_eq!(message.name(), "EngineData");
356    /// # Ok::<(), dbc_rs::Error>(())
357    /// ```
358    #[inline]
359    #[must_use]
360    pub fn name(&self) -> &'a str {
361        self.name
362    }
363
364    /// Returns the Data Length Code (DLC) in bytes.
365    ///
366    /// DLC specifies the size of the message payload. For classic CAN, this is 1-8 bytes.
367    /// For CAN FD, this can be up to 64 bytes.
368    ///
369    /// # Examples
370    ///
371    /// ```rust,no_run
372    /// use dbc_rs::Dbc;
373    ///
374    /// let dbc = Dbc::parse(r#"VERSION "1.0"\n\nBU_: ECM\n\nBO_ 256 EngineData : 8 ECM"#)?;
375    /// let message = dbc.messages().at(0).unwrap();
376    /// assert_eq!(message.dlc(), 8);
377    /// # Ok::<(), dbc_rs::Error>(())
378    /// ```
379    #[inline]
380    #[must_use]
381    pub fn dlc(&self) -> u8 {
382        self.dlc
383    }
384
385    #[inline]
386    #[must_use]
387    pub fn sender(&self) -> &'a str {
388        self.sender
389    }
390
391    /// Get a reference to the signals collection
392    #[inline]
393    #[must_use]
394    pub fn signals(&self) -> &Signals<'a> {
395        &self.signals
396    }
397
398    #[cfg(feature = "alloc")]
399    #[must_use]
400    pub fn to_dbc_string(&self) -> alloc::string::String {
401        use alloc::format;
402        format!(
403            "BO_ {} {} : {} {}",
404            self.id(),
405            self.name(),
406            self.dlc(),
407            self.sender()
408        )
409    }
410
411    #[cfg(feature = "alloc")]
412    #[must_use]
413    pub fn to_dbc_string_with_signals(&self) -> alloc::string::String {
414        use alloc::string::String;
415        let mut result = String::with_capacity(200 + (self.signals.len() * 100));
416        result.push_str(&self.to_dbc_string());
417        result.push('\n');
418
419        for signal in self.signals().iter() {
420            result.push_str(&signal.to_dbc_string());
421            result.push('\n');
422        }
423
424        result
425    }
426}
427
428#[cfg(feature = "alloc")]
429impl<'a> core::fmt::Display for Message<'a> {
430    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
431        write!(f, "{}", self.to_dbc_string_with_signals())
432    }
433}
434
435#[cfg(test)]
436mod tests {
437    #![allow(clippy::float_cmp)]
438    use super::*;
439    use crate::{Parser, error::ParseError};
440    #[cfg(any(feature = "alloc", feature = "kernel"))]
441    use alloc::{format, vec};
442    // Note: Builder tests have been moved to message_builder.rs
443    // This module only tests Message parsing and direct API usage
444
445    // Note: All test_message_new_* tests have been removed - they belong in message_builder.rs
446    // This module only tests Message parsing and direct API usage
447
448    #[test]
449    fn test_message_parse_valid() {
450        let data = b"BO_ 256 EngineData : 8 ECM";
451        let mut parser = Parser::new(data).unwrap();
452        const MAX_CAP: usize = crate::Signals::max_capacity();
453        let signals: [Option<Signal>; MAX_CAP] = [const { None }; MAX_CAP];
454        let message = Message::parse(&mut parser, &signals, 0, crate::ParseOptions::new()).unwrap();
455        assert_eq!(message.id(), 256);
456        assert_eq!(message.name(), "EngineData");
457        assert_eq!(message.dlc(), 8);
458        assert_eq!(message.sender(), "ECM");
459        assert_eq!(message.signals().len(), 0);
460    }
461
462    #[test]
463    fn test_message_parse_invalid_id() {
464        let data = b"BO_ invalid EngineData : 8 ECM";
465        let mut parser = Parser::new(data).unwrap();
466        const MAX_CAP: usize = crate::Signals::max_capacity();
467        let signals: [Option<Signal>; MAX_CAP] = [const { None }; MAX_CAP];
468        let result = Message::parse(&mut parser, &signals, 0, crate::ParseOptions::new());
469        assert!(result.is_err());
470        match result.unwrap_err() {
471            ParseError::Message(_) => {
472                // Expected
473            }
474            _ => panic!("Expected ParseError::Message"),
475        }
476    }
477
478    #[test]
479    fn test_message_parse_empty_name() {
480        let data = b"BO_ 256  : 8 ECM";
481        let mut parser = Parser::new(data).unwrap();
482        const MAX_CAP: usize = crate::Signals::max_capacity();
483        let signals: [Option<Signal>; MAX_CAP] = [const { None }; MAX_CAP];
484        let result = Message::parse(&mut parser, &signals, 0, crate::ParseOptions::new());
485        assert!(result.is_err());
486        match result.unwrap_err() {
487            ParseError::Message(_) => {
488                // Expected
489            }
490            _ => panic!("Expected ParseError::Message"),
491        }
492    }
493
494    #[test]
495    fn test_message_parse_invalid_dlc() {
496        let data = b"BO_ 256 EngineData : invalid ECM";
497        let mut parser = Parser::new(data).unwrap();
498        const MAX_CAP: usize = crate::Signals::max_capacity();
499        let signals: [Option<Signal>; MAX_CAP] = [const { None }; MAX_CAP];
500        let result = Message::parse(&mut parser, &signals, 0, crate::ParseOptions::new());
501        assert!(result.is_err());
502        match result.unwrap_err() {
503            ParseError::Message(_) => {
504                // Expected
505            }
506            _ => panic!("Expected ParseError::Message"),
507        }
508    }
509
510    #[test]
511    fn test_message_parse_empty_sender() {
512        let data = b"BO_ 256 EngineData : 8 ";
513        let mut parser = Parser::new(data).unwrap();
514        const MAX_CAP: usize = crate::Signals::max_capacity();
515        let signals: [Option<Signal>; MAX_CAP] = [const { None }; MAX_CAP];
516        let result = Message::parse(&mut parser, &signals, 0, crate::ParseOptions::new());
517        assert!(result.is_err());
518        match result.unwrap_err() {
519            ParseError::Message(_) => {
520                // Expected
521            }
522            _ => panic!("Expected ParseError::Message"),
523        }
524    }
525
526    #[test]
527    fn test_message_parse_with_signals() {
528        let data = b"BO_ 256 EngineData : 8 ECM";
529        let mut parser = Parser::new(data).unwrap();
530
531        // Create test signals
532        let signal1 = Signal::parse(
533            &mut Parser::new(b"SG_ RPM : 0|16@0+ (0.25,0) [0|8000] \"rpm\"").unwrap(),
534        )
535        .unwrap();
536        let signal2 = Signal::parse(
537            &mut Parser::new(b"SG_ Temp : 16|8@0- (1,-40) [-40|215] \"\xC2\xB0C\"").unwrap(),
538        )
539        .unwrap();
540
541        const MAX_CAP: usize = crate::Signals::max_capacity();
542        let mut signals: [Option<Signal>; MAX_CAP] = [const { None }; MAX_CAP];
543        signals[0] = Some(signal1);
544        signals[1] = Some(signal2);
545
546        let message = Message::parse(&mut parser, &signals, 2, crate::ParseOptions::new()).unwrap();
547        assert_eq!(message.id(), 256);
548        assert_eq!(message.name(), "EngineData");
549        assert_eq!(message.dlc(), 8);
550        assert_eq!(message.sender(), "ECM");
551        assert_eq!(message.signals().len(), 2);
552    }
553
554    #[test]
555    fn test_message_signals_iterator() {
556        let data = b"BO_ 256 EngineData : 8 ECM";
557        let mut parser = Parser::new(data).unwrap();
558
559        // Create test signals
560        let signal1 = Signal::parse(
561            &mut Parser::new(b"SG_ RPM : 0|16@0+ (0.25,0) [0|8000] \"rpm\"").unwrap(),
562        )
563        .unwrap();
564        let signal2 = Signal::parse(
565            &mut Parser::new(b"SG_ Temp : 16|8@0- (1,-40) [-40|215] \"\xC2\xB0C\"").unwrap(),
566        )
567        .unwrap();
568
569        const MAX_CAP: usize = crate::Signals::max_capacity();
570        let mut signals: [Option<Signal>; MAX_CAP] = [const { None }; MAX_CAP];
571        signals[0] = Some(signal1);
572        signals[1] = Some(signal2);
573
574        let message = Message::parse(&mut parser, &signals, 2, crate::ParseOptions::new()).unwrap();
575        let mut signals_iter = message.signals().iter();
576        assert_eq!(signals_iter.next().unwrap().name(), "RPM");
577        assert_eq!(signals_iter.next().unwrap().name(), "Temp");
578        assert!(signals_iter.next().is_none());
579    }
580
581    #[test]
582    fn test_message_signal_count() {
583        let data = b"BO_ 256 EngineData : 8 ECM";
584        let mut parser = Parser::new(data).unwrap();
585
586        let signals: [Option<Signal>; crate::Signals::max_capacity()] =
587            [const { None }; crate::Signals::max_capacity()];
588        let message = Message::parse(&mut parser, &signals, 0, crate::ParseOptions::new()).unwrap();
589        assert_eq!(message.signals().len(), 0);
590
591        // Create a new parser for the second parse since the first one consumed the input
592        let data2 = b"BO_ 256 EngineData : 8 ECM";
593        let mut parser2 = Parser::new(data2).unwrap();
594        let signal1 = Signal::parse(
595            &mut Parser::new(b"SG_ RPM : 0|16@0+ (0.25,0) [0|8000] \"rpm\"").unwrap(),
596        )
597        .unwrap();
598        const MAX_CAP: usize = crate::Signals::max_capacity();
599        let mut signals: [Option<Signal>; MAX_CAP] = [const { None }; MAX_CAP];
600        signals[0] = Some(signal1);
601        let message =
602            Message::parse(&mut parser2, &signals, 1, crate::ParseOptions::new()).unwrap();
603        assert_eq!(message.signals().len(), 1);
604    }
605
606    #[test]
607    fn test_message_signal_at() {
608        let data = b"BO_ 256 EngineData : 8 ECM";
609        let mut parser = Parser::new(data).unwrap();
610
611        let signal1 = Signal::parse(
612            &mut Parser::new(b"SG_ RPM : 0|16@0+ (0.25,0) [0|8000] \"rpm\"").unwrap(),
613        )
614        .unwrap();
615        let signal2 = Signal::parse(
616            &mut Parser::new(b"SG_ Temp : 16|8@0- (1,-40) [-40|215] \"\xC2\xB0C\"").unwrap(),
617        )
618        .unwrap();
619
620        const MAX_CAP: usize = crate::Signals::max_capacity();
621        let mut signals: [Option<Signal>; MAX_CAP] = [const { None }; MAX_CAP];
622        signals[0] = Some(signal1);
623        signals[1] = Some(signal2);
624
625        let message = Message::parse(&mut parser, &signals, 2, crate::ParseOptions::new()).unwrap();
626        assert_eq!(message.signals().at(0).unwrap().name(), "RPM");
627        assert_eq!(message.signals().at(1).unwrap().name(), "Temp");
628        assert!(message.signals().at(2).is_none());
629    }
630
631    #[test]
632    fn test_message_find_signal() {
633        let data = b"BO_ 256 EngineData : 8 ECM";
634        let mut parser = Parser::new(data).unwrap();
635
636        let signal1 = Signal::parse(
637            &mut Parser::new(b"SG_ RPM : 0|16@0+ (0.25,0) [0|8000] \"rpm\"").unwrap(),
638        )
639        .unwrap();
640        let signal2 = Signal::parse(
641            &mut Parser::new(b"SG_ Temp : 16|8@0- (1,-40) [-40|215] \"\xC2\xB0C\"").unwrap(),
642        )
643        .unwrap();
644
645        const MAX_CAP: usize = crate::Signals::max_capacity();
646        let mut signals: [Option<Signal>; MAX_CAP] = [const { None }; MAX_CAP];
647        signals[0] = Some(signal1);
648        signals[1] = Some(signal2);
649
650        let message = Message::parse(&mut parser, &signals, 2, crate::ParseOptions::new()).unwrap();
651        assert_eq!(message.signals().find("RPM").unwrap().name(), "RPM");
652        assert_eq!(message.signals().find("Temp").unwrap().name(), "Temp");
653        assert!(message.signals().find("Nonexistent").is_none());
654    }
655
656    #[test]
657    #[cfg(feature = "alloc")]
658    fn test_message_to_dbc_string() {
659        let data = b"BO_ 256 EngineData : 8 ECM";
660        let mut parser = Parser::new(data).unwrap();
661        const MAX_CAP: usize = crate::Signals::max_capacity();
662        let signals: [Option<Signal>; MAX_CAP] = [const { None }; MAX_CAP];
663        let message = Message::parse(&mut parser, &signals, 0, crate::ParseOptions::new()).unwrap();
664        let dbc_string = message.to_dbc_string();
665        assert_eq!(dbc_string, "BO_ 256 EngineData : 8 ECM");
666    }
667
668    #[test]
669    #[cfg(feature = "alloc")]
670    fn test_message_to_dbc_string_with_signals() {
671        let data = b"BO_ 256 EngineData : 8 ECM";
672        let mut parser = Parser::new(data).unwrap();
673
674        let signal1 = Signal::parse(
675            &mut Parser::new(b"SG_ RPM : 0|16@0+ (0.25,0) [0|8000] \"rpm\"").unwrap(),
676        )
677        .unwrap();
678        let signal2 = Signal::parse(
679            &mut Parser::new(b"SG_ Temp : 16|8@0- (1,-40) [-40|215] \"\xC2\xB0C\"").unwrap(),
680        )
681        .unwrap();
682
683        const MAX_CAP: usize = crate::Signals::max_capacity();
684        let mut signals: [Option<Signal>; MAX_CAP] = [const { None }; MAX_CAP];
685        signals[0] = Some(signal1);
686        signals[1] = Some(signal2);
687
688        let message = Message::parse(&mut parser, &signals, 2, crate::ParseOptions::new()).unwrap();
689        let dbc_string = message.to_dbc_string_with_signals();
690        assert!(dbc_string.contains("BO_ 256 EngineData : 8 ECM"));
691        assert!(dbc_string.contains("SG_ RPM"));
692        assert!(dbc_string.contains("SG_ Temp"));
693    }
694
695    #[test]
696    #[cfg(any(feature = "alloc", feature = "kernel"))]
697    fn test_message_can_2_0a_dlc_limits() {
698        use alloc::format;
699        // CAN 2.0A: DLC can be 1-8 bytes (8-64 bits)
700        // Test valid DLC values
701        for dlc in 1..=8 {
702            let data = format!("BO_ 256 EngineData : {} ECM", dlc);
703            let mut parser = Parser::new(data.as_bytes()).unwrap();
704            const MAX_CAP: usize = crate::Signals::max_capacity();
705            let signals: [Option<Signal>; MAX_CAP] = [const { None }; MAX_CAP];
706            let message =
707                Message::parse(&mut parser, &signals, 0, crate::ParseOptions::new()).unwrap();
708            assert_eq!(message.dlc(), dlc);
709        }
710    }
711
712    #[test]
713    #[cfg(any(feature = "alloc", feature = "kernel"))]
714    fn test_message_can_2_0b_dlc_limits() {
715        use alloc::format;
716        // CAN 2.0B: DLC can be 1-8 bytes (8-64 bits)
717        // Test valid DLC values
718        for dlc in 1..=8 {
719            let data = format!("BO_ 256 EngineData : {} ECM", dlc);
720            let mut parser = Parser::new(data.as_bytes()).unwrap();
721            const MAX_CAP: usize = crate::Signals::max_capacity();
722            let signals: [Option<Signal>; MAX_CAP] = [const { None }; MAX_CAP];
723            let message =
724                Message::parse(&mut parser, &signals, 0, crate::ParseOptions::new()).unwrap();
725            assert_eq!(message.dlc(), dlc);
726        }
727    }
728
729    #[test]
730    #[cfg(any(feature = "alloc", feature = "kernel"))]
731    fn test_message_can_fd_dlc_limits() {
732        use alloc::format;
733        // CAN FD: DLC can be 1-64 bytes (8-512 bits)
734        // Test valid DLC values up to 64
735        for dlc in [1, 8, 12, 16, 20, 24, 32, 48, 64] {
736            let data = format!("BO_ 256 EngineData : {} ECM", dlc);
737            let mut parser = Parser::new(data.as_bytes()).unwrap();
738            const MAX_CAP: usize = crate::Signals::max_capacity();
739            let signals: [Option<Signal>; MAX_CAP] = [const { None }; MAX_CAP];
740            let message =
741                Message::parse(&mut parser, &signals, 0, crate::ParseOptions::new()).unwrap();
742            assert_eq!(message.dlc(), dlc);
743        }
744    }
745
746    #[test]
747    fn test_message_multiple_signals_boundary_validation() {
748        // Test that signals at message boundaries are validated correctly
749        let data = b"BO_ 256 EngineData : 8 ECM";
750        let mut parser = Parser::new(data).unwrap();
751
752        // Create signals that exactly fill the message (8 bytes = 64 bits)
753        // Signal 1: bits 0-15 (16 bits)
754        let signal1 =
755            Signal::parse(&mut Parser::new(b"SG_ Signal1 : 0|16@0+ (1,0) [0|65535] \"\"").unwrap())
756                .unwrap();
757        // Signal 2: bits 16-31 (16 bits)
758        let signal2 = Signal::parse(
759            &mut Parser::new(b"SG_ Signal2 : 16|16@0+ (1,0) [0|65535] \"\"").unwrap(),
760        )
761        .unwrap();
762        // Signal 3: bits 32-47 (16 bits)
763        let signal3 = Signal::parse(
764            &mut Parser::new(b"SG_ Signal3 : 32|16@0+ (1,0) [0|65535] \"\"").unwrap(),
765        )
766        .unwrap();
767        // Signal 4: bits 48-63 (16 bits) - exactly at boundary
768        let signal4 = Signal::parse(
769            &mut Parser::new(b"SG_ Signal4 : 48|16@0+ (1,0) [0|65535] \"\"").unwrap(),
770        )
771        .unwrap();
772
773        const MAX_CAP: usize = crate::Signals::max_capacity();
774        let mut signals: [Option<Signal>; MAX_CAP] = [const { None }; MAX_CAP];
775        signals[0] = Some(signal1);
776        signals[1] = Some(signal2);
777        signals[2] = Some(signal3);
778        signals[3] = Some(signal4);
779
780        let message = Message::parse(&mut parser, &signals, 4, crate::ParseOptions::new()).unwrap();
781        assert_eq!(message.signals().len(), 4);
782    }
783
784    #[test]
785    fn test_message_big_endian_bit_range_calculation() {
786        // Test big-endian bit range calculation
787        // BE bit 0 -> physical bit 7
788        // BE bit 7 -> physical bit 0
789        // BE bit 8 -> physical bit 15
790        // BE bit 15 -> physical bit 8
791        let data = b"BO_ 256 EngineData : 8 ECM";
792        let mut parser = Parser::new(data).unwrap();
793
794        // Signal starting at BE bit 0, length 8 -> should map to physical bits 0-7
795        let signal =
796            Signal::parse(&mut Parser::new(b"SG_ Signal1 : 0|8@1+ (1,0) [0|255] \"\"").unwrap())
797                .unwrap();
798
799        const MAX_CAP: usize = crate::Signals::max_capacity();
800        let mut signals: [Option<Signal>; MAX_CAP] = [const { None }; MAX_CAP];
801        signals[0] = Some(signal);
802
803        let message = Message::parse(&mut parser, &signals, 1, crate::ParseOptions::new()).unwrap();
804        // The signal should be valid and fit within the message
805        assert_eq!(message.signals().len(), 1);
806    }
807
808    #[test]
809    fn test_message_little_endian_bit_range_calculation() {
810        // Test little-endian bit range calculation
811        // LE bit N -> physical bit N (straightforward mapping)
812        let data = b"BO_ 256 EngineData : 8 ECM";
813        let mut parser = Parser::new(data).unwrap();
814
815        // Signal starting at LE bit 0, length 8 -> should map to physical bits 0-7
816        let signal =
817            Signal::parse(&mut Parser::new(b"SG_ Signal1 : 0|8@0+ (1,0) [0|255] \"\"").unwrap())
818                .unwrap();
819
820        const MAX_CAP: usize = crate::Signals::max_capacity();
821        let mut signals: [Option<Signal>; MAX_CAP] = [const { None }; MAX_CAP];
822        signals[0] = Some(signal);
823
824        let message = Message::parse(&mut parser, &signals, 1, crate::ParseOptions::new()).unwrap();
825        // The signal should be valid and fit within the message
826        assert_eq!(message.signals().len(), 1);
827    }
828
829    // Note: Big-endian signal overlap and boundary tests have been moved to message_builder.rs
830
831    #[test]
832    fn test_parse_with_lenient_boundary_check() {
833        // Test that lenient mode allows signals that extend beyond message boundaries
834        let data = b"BO_ 256 Test : 8 ECM";
835        let mut parser = Parser::new(data).unwrap();
836
837        // Signal that extends beyond 8-byte boundary (start_bit 63, length 8 = bits 63-70, exceeds 64 bits)
838        let signal =
839            Signal::parse(&mut Parser::new(b"SG_ CHECKSUM : 63|8@1+ (1,0) [0|255] \"\"").unwrap())
840                .unwrap();
841
842        const MAX_CAP: usize = crate::Signals::max_capacity();
843        let mut signals: [Option<Signal>; MAX_CAP] = [const { None }; MAX_CAP];
844        signals[0] = Some(signal);
845
846        // Strict mode should fail
847        let result = Message::parse(&mut parser, &signals, 1, crate::ParseOptions::new());
848        assert!(result.is_err());
849
850        // Lenient mode should succeed
851        let mut parser2 = Parser::new(data).unwrap();
852        let message =
853            Message::parse(&mut parser2, &signals, 1, crate::ParseOptions::lenient()).unwrap();
854        assert_eq!(message.signals().len(), 1);
855        assert_eq!(message.signals().at(0).unwrap().name(), "CHECKSUM");
856    }
857
858    #[test]
859    #[cfg(feature = "alloc")]
860    fn test_message_to_dbc_string_empty_signals() {
861        let data = b"BO_ 256 EngineData : 8 ECM";
862        let mut parser = Parser::new(data).unwrap();
863        let signals: [Option<Signal>; crate::Signals::max_capacity()] =
864            [const { None }; crate::Signals::max_capacity()];
865        let message = Message::parse(&mut parser, &signals, 0, crate::ParseOptions::new()).unwrap();
866
867        let dbc_string = message.to_dbc_string();
868        assert_eq!(dbc_string, "BO_ 256 EngineData : 8 ECM");
869
870        let dbc_string_with_signals = message.to_dbc_string_with_signals();
871        assert_eq!(dbc_string_with_signals, "BO_ 256 EngineData : 8 ECM\n");
872    }
873
874    #[test]
875    #[cfg(feature = "alloc")]
876    fn test_message_to_dbc_string_special_characters() {
877        let data = b"BO_ 1234 Test_Message_With_Underscores : 4 Sender_Node";
878        let mut parser = Parser::new(data).unwrap();
879        let signals: [Option<Signal>; crate::Signals::max_capacity()] =
880            [const { None }; crate::Signals::max_capacity()];
881        let message = Message::parse(&mut parser, &signals, 0, crate::ParseOptions::new()).unwrap();
882
883        let dbc_string = message.to_dbc_string();
884        assert_eq!(
885            dbc_string,
886            "BO_ 1234 Test_Message_With_Underscores : 4 Sender_Node"
887        );
888    }
889
890    #[test]
891    #[cfg(feature = "alloc")]
892    fn test_message_to_dbc_string_extended_id() {
893        // Use a valid extended ID (max is 0x1FFF_FFFF = 536870911)
894        let data = b"BO_ 536870911 ExtendedID : 8 ECM";
895        let mut parser = Parser::new(data).unwrap();
896        let signals: [Option<Signal>; crate::Signals::max_capacity()] =
897            [const { None }; crate::Signals::max_capacity()];
898        let message = Message::parse(&mut parser, &signals, 0, crate::ParseOptions::new()).unwrap();
899
900        let dbc_string = message.to_dbc_string();
901        assert_eq!(dbc_string, "BO_ 536870911 ExtendedID : 8 ECM");
902    }
903
904    #[test]
905    #[cfg(feature = "alloc")]
906    fn test_message_to_dbc_string_dlc_edge_cases() {
907        // Test DLC = 1
908        let data = b"BO_ 256 MinDLC : 1 ECM";
909        let mut parser = Parser::new(data).unwrap();
910        let signals: [Option<Signal>; crate::Signals::max_capacity()] =
911            [const { None }; crate::Signals::max_capacity()];
912        let message = Message::parse(&mut parser, &signals, 0, crate::ParseOptions::new()).unwrap();
913        assert_eq!(message.to_dbc_string(), "BO_ 256 MinDLC : 1 ECM");
914
915        // Test DLC = 64 (CAN FD max)
916        let data2 = b"BO_ 257 MaxDLC : 64 ECM";
917        let mut parser2 = Parser::new(data2).unwrap();
918        let message2 =
919            Message::parse(&mut parser2, &signals, 0, crate::ParseOptions::new()).unwrap();
920        assert_eq!(message2.to_dbc_string(), "BO_ 257 MaxDLC : 64 ECM");
921    }
922
923    #[test]
924    fn test_message_signals_is_empty() {
925        let data = b"BO_ 256 EngineData : 8 ECM";
926        let mut parser = Parser::new(data).unwrap();
927        let signals: [Option<Signal>; crate::Signals::max_capacity()] =
928            [const { None }; crate::Signals::max_capacity()];
929        let message = Message::parse(&mut parser, &signals, 0, crate::ParseOptions::new()).unwrap();
930        assert!(message.signals().is_empty());
931        assert_eq!(message.signals().len(), 0);
932    }
933
934    #[test]
935    fn test_message_signals_at_out_of_bounds() {
936        let data = b"BO_ 256 EngineData : 8 ECM";
937        let mut parser = Parser::new(data).unwrap();
938
939        let signal = Signal::parse(
940            &mut Parser::new(b"SG_ RPM : 0|16@0+ (0.25,0) [0|8000] \"rpm\"").unwrap(),
941        )
942        .unwrap();
943
944        const MAX_CAP: usize = crate::Signals::max_capacity();
945        let mut signals: [Option<Signal>; MAX_CAP] = [const { None }; MAX_CAP];
946        signals[0] = Some(signal);
947
948        let message = Message::parse(&mut parser, &signals, 1, crate::ParseOptions::new()).unwrap();
949
950        // Valid index
951        assert!(message.signals().at(0).is_some());
952        assert_eq!(message.signals().at(0).unwrap().name(), "RPM");
953
954        // Out of bounds
955        assert!(message.signals().at(1).is_none());
956        assert!(message.signals().at(100).is_none());
957        assert!(message.signals().at(usize::MAX).is_none());
958    }
959
960    #[test]
961    fn test_message_signals_find_case_sensitive() {
962        let data = b"BO_ 256 EngineData : 8 ECM";
963        let mut parser = Parser::new(data).unwrap();
964
965        let signal1 = Signal::parse(
966            &mut Parser::new(b"SG_ RPM : 0|16@0+ (0.25,0) [0|8000] \"rpm\"").unwrap(),
967        )
968        .unwrap();
969        let signal2 = Signal::parse(
970            &mut Parser::new(b"SG_ Temp : 16|8@0- (1,-40) [-40|215] \"\xC2\xB0C\"").unwrap(),
971        )
972        .unwrap();
973
974        const MAX_CAP: usize = crate::Signals::max_capacity();
975        let mut signals: [Option<Signal>; MAX_CAP] = [const { None }; MAX_CAP];
976        signals[0] = Some(signal1);
977        signals[1] = Some(signal2);
978
979        let message = Message::parse(&mut parser, &signals, 2, crate::ParseOptions::new()).unwrap();
980
981        // Exact match
982        assert!(message.signals().find("RPM").is_some());
983        assert_eq!(message.signals().find("RPM").unwrap().name(), "RPM");
984
985        // Case sensitive - should not find
986        assert!(message.signals().find("rpm").is_none());
987        assert!(message.signals().find("Rpm").is_none());
988
989        // Find second signal
990        assert!(message.signals().find("Temp").is_some());
991        assert_eq!(message.signals().find("Temp").unwrap().name(), "Temp");
992
993        // Not found
994        assert!(message.signals().find("Nonexistent").is_none());
995        assert!(message.signals().find("").is_none());
996    }
997
998    #[test]
999    fn test_message_signals_find_empty_collection() {
1000        let data = b"BO_ 256 EngineData : 8 ECM";
1001        let mut parser = Parser::new(data).unwrap();
1002        let signals: [Option<Signal>; crate::Signals::max_capacity()] =
1003            [const { None }; crate::Signals::max_capacity()];
1004        let message = Message::parse(&mut parser, &signals, 0, crate::ParseOptions::new()).unwrap();
1005
1006        assert!(message.signals().find("RPM").is_none());
1007        assert!(message.signals().find("").is_none());
1008    }
1009
1010    #[test]
1011    #[cfg(feature = "alloc")]
1012    fn test_message_display_trait() {
1013        use alloc::format;
1014        let data = b"BO_ 256 EngineData : 8 ECM";
1015        let mut parser = Parser::new(data).unwrap();
1016
1017        // Parse signal from DBC string instead of using builder
1018        let signal = Signal::parse(
1019            &mut Parser::new(b"SG_ RPM : 0|16@0+ (0.25,0) [0|8000] \"rpm\" *").unwrap(),
1020        )
1021        .unwrap();
1022
1023        const MAX_CAP: usize = crate::Signals::max_capacity();
1024        let mut signals: [Option<Signal>; MAX_CAP] = [const { None }; MAX_CAP];
1025        signals[0] = Some(signal);
1026
1027        let message = Message::parse(&mut parser, &signals, 1, crate::ParseOptions::new()).unwrap();
1028
1029        let display_str = format!("{}", message);
1030        assert!(display_str.contains("BO_ 256 EngineData : 8 ECM"));
1031        assert!(display_str.contains("SG_ RPM"));
1032    }
1033
1034    #[test]
1035    #[cfg(feature = "alloc")]
1036    fn test_message_to_dbc_string_with_signals_multiple() {
1037        let data = b"BO_ 256 EngineData : 8 ECM";
1038        let mut parser = Parser::new(data).unwrap();
1039
1040        // Parse signals from DBC strings instead of using builders
1041        let signal1 = Signal::parse(
1042            &mut Parser::new(b"SG_ RPM : 0|16@0+ (0.25,0) [0|8000] \"rpm\" *").unwrap(),
1043        )
1044        .unwrap();
1045
1046        let signal2 = Signal::parse(
1047            &mut Parser::new(b"SG_ Temp : 16|8@0- (1,-40) [-40|215] \"\xC2\xB0C\" *").unwrap(),
1048        )
1049        .unwrap();
1050
1051        const MAX_CAP: usize = crate::Signals::max_capacity();
1052        let mut signals: [Option<Signal>; MAX_CAP] = [const { None }; MAX_CAP];
1053        signals[0] = Some(signal1);
1054        signals[1] = Some(signal2);
1055
1056        let message = Message::parse(&mut parser, &signals, 2, crate::ParseOptions::new()).unwrap();
1057
1058        let dbc_string = message.to_dbc_string_with_signals();
1059        assert!(dbc_string.contains("BO_ 256 EngineData : 8 ECM"));
1060        assert!(dbc_string.contains("SG_ RPM"));
1061        assert!(dbc_string.contains("SG_ Temp"));
1062        // Should have newlines between signals
1063        #[cfg(any(feature = "alloc", feature = "kernel"))]
1064        use crate::compat::Vec;
1065        let lines: Vec<&str> = dbc_string.lines().collect();
1066        assert!(lines.len() >= 3); // Message line + at least 2 signal lines
1067    }
1068
1069    #[test]
1070    fn test_message_getters_edge_cases() {
1071        // Test with minimum values
1072        let data = b"BO_ 0 A : 1 B";
1073        let mut parser = Parser::new(data).unwrap();
1074        let signals: [Option<Signal>; crate::Signals::max_capacity()] =
1075            [const { None }; crate::Signals::max_capacity()];
1076        let message = Message::parse(&mut parser, &signals, 0, crate::ParseOptions::new()).unwrap();
1077
1078        assert_eq!(message.id(), 0);
1079        assert_eq!(message.name(), "A");
1080        assert_eq!(message.dlc(), 1);
1081        assert_eq!(message.sender(), "B");
1082    }
1083
1084    #[test]
1085    fn test_message_signals_iterator_empty() {
1086        let data = b"BO_ 256 EngineData : 8 ECM";
1087        let mut parser = Parser::new(data).unwrap();
1088        let signals: [Option<Signal>; crate::Signals::max_capacity()] =
1089            [const { None }; crate::Signals::max_capacity()];
1090        let message = Message::parse(&mut parser, &signals, 0, crate::ParseOptions::new()).unwrap();
1091
1092        let mut iter = message.signals().iter();
1093        assert!(iter.next().is_none());
1094    }
1095
1096    #[test]
1097    fn test_message_signals_iterator_multiple() {
1098        let data = b"BO_ 256 EngineData : 8 ECM";
1099        let mut parser = Parser::new(data).unwrap();
1100
1101        let signal1 =
1102            Signal::parse(&mut Parser::new(b"SG_ Signal1 : 0|8@0+ (1,0) [0|255] \"\"").unwrap())
1103                .unwrap();
1104        let signal2 =
1105            Signal::parse(&mut Parser::new(b"SG_ Signal2 : 8|8@0+ (1,0) [0|255] \"\"").unwrap())
1106                .unwrap();
1107        let signal3 =
1108            Signal::parse(&mut Parser::new(b"SG_ Signal3 : 16|8@0+ (1,0) [0|255] \"\"").unwrap())
1109                .unwrap();
1110
1111        const MAX_CAP: usize = crate::Signals::max_capacity();
1112        let mut signals: [Option<Signal>; MAX_CAP] = [const { None }; MAX_CAP];
1113        signals[0] = Some(signal1);
1114        signals[1] = Some(signal2);
1115        signals[2] = Some(signal3);
1116
1117        let message = Message::parse(&mut parser, &signals, 3, crate::ParseOptions::new()).unwrap();
1118
1119        let mut iter = message.signals().iter();
1120        assert_eq!(iter.next().unwrap().name(), "Signal1");
1121        assert_eq!(iter.next().unwrap().name(), "Signal2");
1122        assert_eq!(iter.next().unwrap().name(), "Signal3");
1123        assert!(iter.next().is_none());
1124
1125        // Test that iterator can be used multiple times
1126        #[cfg(any(feature = "alloc", feature = "kernel"))]
1127        {
1128            use crate::compat::Vec;
1129            use alloc::vec;
1130            let names: Vec<&str> = message.signals().iter().map(|s| s.name()).collect();
1131            // Convert kernel Vec to alloc Vec for comparison
1132            #[cfg(feature = "kernel")]
1133            let names: alloc::vec::Vec<&str> = names.into();
1134            assert_eq!(names, vec!["Signal1", "Signal2", "Signal3"]);
1135        }
1136    }
1137}