dbc_rs/dbc/messages.rs
1use crate::Message;
2
3/// Iterator over messages in a Messages collection
4struct MessagesIter<'a, 'b> {
5 messages: &'b [Option<Message<'a>>],
6 count: usize,
7 pos: usize,
8}
9
10impl<'a, 'b> Iterator for MessagesIter<'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 Messages<'a> {
45 #[cfg(not(feature = "alloc"))]
46 messages: [Option<Message<'a>>; MAX_MESSAGES],
47 #[cfg(feature = "alloc")]
48 messages: alloc::boxed::Box<[Option<Message<'a>>]>,
49 message_count: usize,
50}
51
52impl<'a> Messages<'a> {
53 /// Create Messages from a slice of messages by cloning them
54 #[allow(dead_code)] // Only used by builders (std-only)
55 pub(crate) fn from_messages_slice(messages: &[Message<'a>]) -> Self {
56 let count = messages.len().min(MAX_MESSAGES);
57
58 #[cfg(not(feature = "alloc"))]
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(feature = "alloc")]
72 {
73 use alloc::vec::Vec;
74 let mut messages_vec = Vec::with_capacity(count);
75 for message in messages.iter().take(count) {
76 messages_vec.push(Some(message.clone()));
77 }
78 Self {
79 messages: messages_vec.into_boxed_slice(),
80 message_count: count,
81 }
82 }
83 }
84
85 /// Create Messages from a slice of `Option<Message>` and count
86 pub(crate) fn from_options_slice(
87 messages: &[Option<Message<'a>>],
88 message_count: usize,
89 ) -> Self {
90 let count = message_count.min(MAX_MESSAGES).min(messages.len());
91
92 #[cfg(not(feature = "alloc"))]
93 {
94 let mut messages_array: [Option<Message<'a>>; MAX_MESSAGES] =
95 [const { None }; MAX_MESSAGES];
96 for (i, message_opt) in messages.iter().take(count).enumerate() {
97 messages_array[i] = message_opt.clone();
98 }
99 Self {
100 messages: messages_array,
101 message_count: count,
102 }
103 }
104
105 #[cfg(feature = "alloc")]
106 {
107 use alloc::vec::Vec;
108 let mut messages_vec = Vec::with_capacity(count);
109 for message_opt in messages.iter().take(count) {
110 messages_vec.push(message_opt.clone());
111 }
112 Self {
113 messages: messages_vec.into_boxed_slice(),
114 message_count: count,
115 }
116 }
117 }
118
119 /// Get an iterator over the messages
120 ///
121 /// # Examples
122 ///
123 /// ```
124 /// use dbc_rs::Dbc;
125 ///
126 /// let dbc = Dbc::parse("VERSION \"1.0\"\n\nBU_: ECM\n\nBO_ 256 Engine : 8 ECM")?;
127 /// for message in dbc.messages().iter() {
128 /// println!("Message: {} (ID: {})", message.name(), message.id());
129 /// }
130 /// # Ok::<(), dbc_rs::Error>(())
131 /// ```
132 #[inline]
133 #[must_use = "iterator is lazy and does nothing unless consumed"]
134 pub fn iter(&self) -> impl Iterator<Item = &Message<'a>> + '_ {
135 let messages_slice: &[Option<Message<'a>>] = &self.messages;
136 MessagesIter {
137 messages: messages_slice,
138 count: self.message_count,
139 pos: 0,
140 }
141 }
142
143 /// Get the number of messages
144 ///
145 /// # Examples
146 ///
147 /// ```
148 /// use dbc_rs::Dbc;
149 ///
150 /// let dbc = Dbc::parse("VERSION \"1.0\"\n\nBU_: ECM\n\nBO_ 256 Engine : 8 ECM")?;
151 /// assert_eq!(dbc.messages().len(), 1);
152 /// # Ok::<(), dbc_rs::Error>(())
153 /// ```
154 #[inline]
155 #[must_use]
156 pub fn len(&self) -> usize {
157 self.message_count
158 }
159
160 /// Returns `true` if there are no messages
161 ///
162 /// # Examples
163 ///
164 /// ```
165 /// use dbc_rs::Dbc;
166 ///
167 /// let dbc = Dbc::parse("VERSION \"1.0\"\n\nBU_: ECM")?;
168 /// assert!(dbc.messages().is_empty());
169 /// # Ok::<(), dbc_rs::Error>(())
170 /// ```
171 #[inline]
172 #[must_use]
173 pub fn is_empty(&self) -> bool {
174 self.len() == 0
175 }
176
177 /// Get a message by index, or None if index is out of bounds
178 ///
179 /// # Examples
180 ///
181 /// ```
182 /// use dbc_rs::Dbc;
183 ///
184 /// let dbc = Dbc::parse("VERSION \"1.0\"\n\nBU_: ECM\n\nBO_ 256 Engine : 8 ECM")?;
185 /// if let Some(message) = dbc.messages().at(0) {
186 /// println!("First message: {}", message.name());
187 /// }
188 /// # Ok::<(), dbc_rs::Error>(())
189 /// ```
190 #[inline]
191 #[must_use]
192 pub fn at(&self, index: usize) -> Option<&Message<'a>> {
193 if index >= self.message_count {
194 return None;
195 }
196 self.messages[index].as_ref()
197 }
198
199 /// Find a message by name, or None if not found
200 ///
201 /// # Examples
202 ///
203 /// ```
204 /// use dbc_rs::Dbc;
205 ///
206 /// let dbc = Dbc::parse("VERSION \"1.0\"\n\nBU_: ECM\n\nBO_ 256 Engine : 8 ECM")?;
207 /// if let Some(message) = dbc.messages().find("Engine") {
208 /// println!("Found message: {} (ID: {})", message.name(), message.id());
209 /// }
210 /// # Ok::<(), dbc_rs::Error>(())
211 /// ```
212 #[must_use]
213 pub fn find(&self, name: &str) -> Option<&Message<'a>> {
214 self.iter().find(|m| m.name() == name)
215 }
216
217 /// Get the maximum capacity (for limit checking during parsing)
218 pub(crate) const fn max_capacity() -> usize {
219 MAX_MESSAGES
220 }
221
222 /// Create a temporary buffer for parsing (no alloc in no_std)
223 /// Returns a buffer that can hold up to MAX_MESSAGES messages
224 #[cfg(not(feature = "alloc"))]
225 pub(crate) fn new_parse_buffer<'b>() -> [Option<Message<'b>>; MAX_MESSAGES] {
226 [const { None }; MAX_MESSAGES]
227 }
228
229 /// Count messages and signals per message without storing (first pass of two-pass parsing)
230 /// Returns message_count (signal counts are not needed after counting pass)
231 pub(crate) fn count_messages_and_signals<'b>(
232 parser: &mut crate::Parser<'b>,
233 ) -> crate::error::ParseResult<usize> {
234 use crate::{
235 BA_, BA_DEF_, BA_DEF_DEF_, BO_, BO_TX_BU_, BS_, BU_, CM_, EV_, NS_, SG_, SIG_GROUP_,
236 SIG_VALTYPE_, VAL_, VAL_TABLE_, VERSION,
237 };
238
239 // Use fixed-size array for counting (no alloc)
240 let mut message_count = 0;
241
242 loop {
243 parser.skip_newlines_and_spaces();
244 if parser.starts_with(b"//") {
245 parser.skip_to_end_of_line();
246 continue;
247 }
248
249 let keyword_result = parser.find_next_keyword();
250 let keyword = match keyword_result {
251 Ok(kw) => kw,
252 Err(crate::error::ParseError::UnexpectedEof) => break,
253 Err(crate::error::ParseError::Expected(_)) => {
254 if parser.starts_with(b"//") {
255 parser.skip_to_end_of_line();
256 continue;
257 }
258 return Err(keyword_result.unwrap_err());
259 }
260 Err(e) => return Err(e),
261 };
262
263 match keyword {
264 NS_ => {
265 parser.skip_newlines_and_spaces();
266 let _ = parser.expect(b":").ok();
267 loop {
268 parser.skip_newlines_and_spaces();
269 if parser.is_empty() {
270 break;
271 }
272 if parser.starts_with(b" ") || parser.starts_with(b"\t") {
273 parser.skip_to_end_of_line();
274 continue;
275 }
276 if parser.starts_with(b"//") {
277 parser.skip_to_end_of_line();
278 continue;
279 }
280 if parser.starts_with(BS_.as_bytes())
281 || parser.starts_with(BU_.as_bytes())
282 || parser.starts_with(BO_.as_bytes())
283 || parser.starts_with(SG_.as_bytes())
284 || parser.starts_with(VERSION.as_bytes())
285 {
286 break;
287 }
288 parser.skip_to_end_of_line();
289 }
290 continue;
291 }
292 CM_ | BS_ | VAL_TABLE_ | BA_DEF_ | BA_DEF_DEF_ | BA_ | VAL_ | SIG_GROUP_
293 | SIG_VALTYPE_ | EV_ | BO_TX_BU_ => {
294 parser.skip_to_end_of_line();
295 continue;
296 }
297 VERSION | BU_ => {
298 // Skip VERSION and BU_ lines (we'll parse them in second pass)
299 parser.skip_to_end_of_line();
300 continue;
301 }
302 BO_ => {
303 // Count this message
304 if message_count >= MAX_MESSAGES {
305 return Err(crate::error::ParseError::Version(
306 crate::error::messages::NODES_TOO_MANY,
307 ));
308 }
309
310 // Skip message header (ID, name, DLC, sender)
311 parser.skip_newlines_and_spaces();
312 let _ = parser.parse_u32().ok();
313 parser.skip_newlines_and_spaces();
314 let _ = parser.parse_identifier().ok();
315 parser.skip_newlines_and_spaces();
316 let _ = parser.expect(b":").ok();
317 parser.skip_newlines_and_spaces();
318 let _ = parser.parse_u8().ok();
319 parser.skip_newlines_and_spaces();
320 let _ = parser.parse_identifier().ok();
321 parser.skip_to_end_of_line();
322
323 // Count signals for this message
324 let mut signal_count = 0;
325 loop {
326 parser.skip_newlines_and_spaces();
327 if parser.starts_with(crate::SG_.as_bytes()) {
328 if let Some(next_byte) = parser.peek_byte_at(3) {
329 if matches!(next_byte, b' ' | b'\n' | b'\r' | b'\t') {
330 if signal_count >= crate::Signals::max_capacity() {
331 return Err(crate::error::ParseError::Version(
332 crate::error::messages::SIGNAL_RECEIVERS_TOO_MANY,
333 ));
334 }
335 signal_count += 1;
336 let _ = parser.find_next_keyword().ok();
337 // Skip the signal line
338 parser.skip_to_end_of_line();
339 continue;
340 }
341 }
342 }
343 break;
344 }
345
346 message_count += 1;
347 continue;
348 }
349 SG_ => {
350 // Standalone signal, skip it
351 let _ = crate::Signal::parse(parser).ok();
352 continue;
353 }
354 _ => {
355 parser.skip_to_end_of_line();
356 continue;
357 }
358 }
359 }
360
361 Ok(message_count)
362 }
363}