dbc_rs/dbc/
message_list.rs

1use crate::Message;
2
3/// Iterator over messages in a MessageList collection
4struct MessageListIter<'a, 'b> {
5    messages: &'b [Option<Message<'a>>],
6    count: usize,
7    pos: usize,
8}
9
10impl<'a, 'b> Iterator for MessageListIter<'a, 'b> {
11    type Item = &'b Message<'a>;
12
13    #[inline]
14    fn next(&mut self) -> Option<Self::Item> {
15        while self.pos < self.count {
16            let result = self.messages[self.pos].as_ref();
17            self.pos += 1;
18            if let Some(msg) = result {
19                return Some(msg);
20            }
21        }
22        None
23    }
24}
25
26// Maximum messages per DBC
27//
28// Defaults to 10000, but can be overridden at build time by setting the `DBC_MAX_MESSAGES`
29// environment variable:
30// ```bash
31// DBC_MAX_MESSAGES=5000 cargo build
32// ```
33//
34// The value must be a valid positive integer. If not set or invalid, defaults to 10000.
35// This constant is generated by build.rs at compile time.
36include!(concat!(env!("OUT_DIR"), "/limits.rs"));
37
38/// Encapsulates the messages array and count for a DBC
39///
40/// Storage strategy:
41/// - `no_std`: Uses fixed-size array `[Option<Message>; MAX_MESSAGES]`
42/// - `alloc`: Uses heap-allocated `Box<[Option<Message>]>` for dynamic sizing
43#[derive(Debug, Clone, PartialEq, Eq, Hash)]
44pub struct MessageList<'a> {
45    #[cfg(not(any(feature = "alloc", feature = "kernel")))]
46    messages: [Option<Message<'a>>; MAX_MESSAGES],
47    #[cfg(any(feature = "alloc", feature = "kernel"))]
48    messages: alloc::boxed::Box<[Option<Message<'a>>]>,
49    message_count: usize,
50}
51
52impl<'a> MessageList<'a> {
53    /// Create Messages from a slice of messages by cloning them
54    #[cfg(any(feature = "alloc", feature = "kernel"))]
55    pub(crate) fn from_messages_slice(messages: &[Message<'a>]) -> Self {
56        let count = messages.len().min(MAX_MESSAGES);
57
58        #[cfg(not(any(feature = "alloc", feature = "kernel")))]
59        {
60            let mut messages_array: [Option<Message<'a>>; MAX_MESSAGES] =
61                [const { None }; MAX_MESSAGES];
62            for (i, message) in messages.iter().take(MAX_MESSAGES).enumerate() {
63                messages_array[i] = Some(message.clone());
64            }
65            Self {
66                messages: messages_array,
67                message_count: count,
68            }
69        }
70
71        #[cfg(any(feature = "alloc", feature = "kernel"))]
72        {
73            use alloc::vec::Vec;
74            let messages_vec: Vec<Option<Message<'a>>> =
75                messages.iter().take(count).map(|message| Some(message.clone())).collect();
76            Self {
77                messages: messages_vec.into_boxed_slice(),
78                message_count: count,
79            }
80        }
81    }
82
83    /// Create MessageList from a slice of `Option<Message>` and count
84    pub(crate) fn from_options_slice(
85        messages: &[Option<Message<'a>>],
86        message_count: usize,
87    ) -> Self {
88        let count = message_count.min(MAX_MESSAGES).min(messages.len());
89
90        #[cfg(not(any(feature = "alloc", feature = "kernel")))]
91        {
92            let mut messages_array: [Option<Message<'a>>; MAX_MESSAGES] =
93                [const { None }; MAX_MESSAGES];
94            for (i, message_opt) in messages.iter().take(count).enumerate() {
95                messages_array[i] = message_opt.clone();
96            }
97            Self {
98                messages: messages_array,
99                message_count: count,
100            }
101        }
102
103        #[cfg(any(feature = "alloc", feature = "kernel"))]
104        {
105            use alloc::vec::Vec;
106            let messages_vec: Vec<Option<Message<'a>>> =
107                messages.iter().take(count).cloned().collect();
108            Self {
109                messages: messages_vec.into_boxed_slice(),
110                message_count: count,
111            }
112        }
113    }
114
115    /// Get an iterator over the messages
116    ///
117    /// # Examples
118    ///
119    /// ```rust,no_run
120    /// use dbc_rs::Dbc;
121    ///
122    /// let dbc = Dbc::parse("VERSION \"1.0\"\n\nBU_: ECM\n\nBO_ 256 Engine : 8 ECM")?;
123    /// let mut iter = dbc.messages().iter();
124    /// let message = iter.next().unwrap();
125    /// assert_eq!(message.name(), "Engine");
126    /// assert_eq!(message.id(), 256);
127    /// assert!(iter.next().is_none());
128    /// # Ok::<(), dbc_rs::Error>(())
129    /// ```
130    #[inline]
131    #[must_use = "iterator is lazy and does nothing unless consumed"]
132    pub fn iter(&self) -> impl Iterator<Item = &Message<'a>> + '_ {
133        let messages_slice: &[Option<Message<'a>>] = &self.messages;
134        MessageListIter {
135            messages: messages_slice,
136            count: self.message_count,
137            pos: 0,
138        }
139    }
140
141    /// Get the number of messages
142    ///
143    /// # Examples
144    ///
145    /// ```rust,no_run
146    /// use dbc_rs::Dbc;
147    ///
148    /// let dbc = Dbc::parse("VERSION \"1.0\"\n\nBU_: ECM\n\nBO_ 256 Engine : 8 ECM")?;
149    /// assert_eq!(dbc.messages().len(), 1);
150    /// # Ok::<(), dbc_rs::Error>(())
151    /// ```
152    #[inline]
153    #[must_use]
154    pub fn len(&self) -> usize {
155        self.message_count
156    }
157
158    /// Returns `true` if there are no messages
159    ///
160    /// # Examples
161    ///
162    /// ```rust,no_run
163    /// use dbc_rs::Dbc;
164    ///
165    /// let dbc = Dbc::parse("VERSION \"1.0\"\n\nBU_: ECM")?;
166    /// assert!(dbc.messages().is_empty());
167    /// # Ok::<(), dbc_rs::Error>(())
168    /// ```
169    #[inline]
170    #[must_use]
171    pub fn is_empty(&self) -> bool {
172        self.len() == 0
173    }
174
175    /// Get a message by index, or None if index is out of bounds
176    ///
177    /// # Examples
178    ///
179    /// ```rust,no_run
180    /// use dbc_rs::Dbc;
181    ///
182    /// let dbc = Dbc::parse("VERSION \"1.0\"\n\nBU_: ECM\n\nBO_ 256 Engine : 8 ECM")?;
183    /// if let Some(message) = dbc.messages().at(0) {
184    ///     assert_eq!(message.name(), "Engine");
185    /// }
186    /// # Ok::<(), dbc_rs::Error>(())
187    /// ```
188    #[inline]
189    #[must_use]
190    pub fn at(&self, index: usize) -> Option<&Message<'a>> {
191        if index >= self.message_count {
192            return None;
193        }
194        self.messages[index].as_ref()
195    }
196
197    /// Find a message by name, or None if not found
198    ///
199    /// # Examples
200    ///
201    /// ```rust,no_run
202    /// use dbc_rs::Dbc;
203    ///
204    /// let dbc = Dbc::parse("VERSION \"1.0\"\n\nBU_: ECM\n\nBO_ 256 Engine : 8 ECM")?;
205    /// if let Some(message) = dbc.messages().find("Engine") {
206    ///     assert_eq!(message.name(), "Engine");
207    ///     assert_eq!(message.id(), 256);
208    /// }
209    /// # Ok::<(), dbc_rs::Error>(())
210    /// ```
211    #[must_use]
212    pub fn find(&self, name: &str) -> Option<&Message<'a>> {
213        self.iter().find(|m| m.name() == name)
214    }
215
216    /// Get the maximum capacity (for limit checking during parsing)
217    pub(crate) const fn max_capacity() -> usize {
218        MAX_MESSAGES
219    }
220
221    /// Create a temporary buffer for parsing (no alloc in no_std)
222    /// Returns a buffer that can hold up to MAX_MESSAGES messages
223    #[cfg(not(any(feature = "alloc", feature = "kernel")))]
224    pub(crate) fn new_parse_buffer<'b>() -> [Option<Message<'b>>; MAX_MESSAGES] {
225        [const { None }; MAX_MESSAGES]
226    }
227
228    /// Count messages and signals per message without storing (first pass of two-pass parsing)
229    /// Returns message_count (signal counts are not needed after counting pass)
230    pub(crate) fn count_messages_and_signals<'b>(
231        parser: &mut crate::Parser<'b>,
232    ) -> crate::error::ParseResult<usize> {
233        use crate::{
234            BA_, BA_DEF_, BA_DEF_DEF_, BO_, BO_TX_BU_, BS_, BU_, CM_, EV_, NS_, SG_, SIG_GROUP_,
235            SIG_VALTYPE_, VAL_, VAL_TABLE_, VERSION,
236        };
237
238        // Use fixed-size array for counting (no alloc)
239        let mut message_count = 0;
240
241        loop {
242            parser.skip_newlines_and_spaces();
243            if parser.starts_with(b"//") {
244                parser.skip_to_end_of_line();
245                continue;
246            }
247
248            let keyword_result = parser.peek_next_keyword();
249            let keyword = match keyword_result {
250                Ok(kw) => kw,
251                Err(crate::error::ParseError::UnexpectedEof) => break,
252                Err(crate::error::ParseError::Expected(_)) => {
253                    if parser.starts_with(b"//") {
254                        parser.skip_to_end_of_line();
255                        continue;
256                    }
257                    return Err(keyword_result.unwrap_err());
258                }
259                Err(e) => return Err(e),
260            };
261
262            // Consume the keyword (peek_next_keyword only peeks at it, doesn't consume it)
263            parser
264                .expect(keyword.as_bytes())
265                .map_err(|_| crate::error::ParseError::Expected("Failed to consume keyword"))?;
266
267            match keyword {
268                NS_ => {
269                    parser.skip_newlines_and_spaces();
270                    let _ = parser.expect(b":").ok();
271                    loop {
272                        parser.skip_newlines_and_spaces();
273                        if parser.is_empty() {
274                            break;
275                        }
276                        if parser.starts_with(b" ") || parser.starts_with(b"\t") {
277                            parser.skip_to_end_of_line();
278                            continue;
279                        }
280                        if parser.starts_with(b"//") {
281                            parser.skip_to_end_of_line();
282                            continue;
283                        }
284                        if parser.starts_with(BS_.as_bytes())
285                            || parser.starts_with(BU_.as_bytes())
286                            || parser.starts_with(BO_.as_bytes())
287                            || parser.starts_with(SG_.as_bytes())
288                            || parser.starts_with(VERSION.as_bytes())
289                        {
290                            break;
291                        }
292                        parser.skip_to_end_of_line();
293                    }
294                    continue;
295                }
296                CM_ | BS_ | VAL_TABLE_ | BA_DEF_ | BA_DEF_DEF_ | BA_ | VAL_ | SIG_GROUP_
297                | SIG_VALTYPE_ | EV_ | BO_TX_BU_ => {
298                    parser.skip_to_end_of_line();
299                    continue;
300                }
301                VERSION | BU_ => {
302                    // Skip VERSION and BU_ lines (we'll parse them in second pass)
303                    parser.skip_to_end_of_line();
304                    continue;
305                }
306                BO_ => {
307                    // Count this message
308                    if message_count >= MAX_MESSAGES {
309                        return Err(crate::error::ParseError::Message(
310                            crate::error::lang::NODES_TOO_MANY,
311                        ));
312                    }
313
314                    // Skip message header (ID, name, DLC, sender)
315                    parser.skip_newlines_and_spaces();
316                    let _ = parser.parse_u32().ok();
317                    parser.skip_newlines_and_spaces();
318                    let _ = parser.parse_identifier().ok();
319                    parser.skip_newlines_and_spaces();
320                    let _ = parser.expect(b":").ok();
321                    parser.skip_newlines_and_spaces();
322                    let _ = parser.parse_u8().ok();
323                    parser.skip_newlines_and_spaces();
324                    let _ = parser.parse_identifier().ok();
325                    parser.skip_to_end_of_line();
326
327                    // Count signals for this message
328                    let mut signal_count = 0;
329                    loop {
330                        parser.skip_newlines_and_spaces();
331                        if parser.starts_with(crate::SG_.as_bytes()) {
332                            if let Some(next_byte) = parser.peek_byte_at(3) {
333                                if matches!(next_byte, b' ' | b'\n' | b'\r' | b'\t') {
334                                    if signal_count >= crate::Signals::max_capacity() {
335                                        return Err(crate::error::ParseError::Message(
336                                            crate::error::lang::MESSAGE_TOO_MANY_SIGNALS,
337                                        ));
338                                    }
339                                    signal_count += 1;
340                                    // Peek and consume keyword, then skip the signal line
341                                    if let Ok(kw) = parser.peek_next_keyword() {
342                                        let _ = parser.expect(kw.as_bytes()).ok();
343                                    }
344                                    parser.skip_to_end_of_line();
345                                    continue;
346                                }
347                            }
348                        }
349                        break;
350                    }
351
352                    message_count += 1;
353                    continue;
354                }
355                SG_ => {
356                    // Standalone signal, skip it
357                    let _ = crate::Signal::parse(parser).ok();
358                    continue;
359                }
360                _ => {
361                    parser.skip_to_end_of_line();
362                    continue;
363                }
364            }
365        }
366
367        Ok(message_count)
368    }
369}