1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
use super::Message;
use crate::{ByteOrder, Error, MAX_SIGNALS_PER_MESSAGE, Result, Signal, error::check_max_limit};
impl Message {
pub(crate) fn validate(
id: u32,
name: &str,
dlc: u8,
sender: &str,
signals: &[Signal],
) -> Result<()> {
// Check signal count limit per message (DoS protection)
if let Some(err) = check_max_limit(
signals.len(),
MAX_SIGNALS_PER_MESSAGE,
Error::Validation(Error::MESSAGE_TOO_MANY_SIGNALS),
) {
return Err(err);
}
if name.trim().is_empty() {
return Err(Error::Validation(Error::MESSAGE_NAME_EMPTY));
}
if sender.trim().is_empty() {
return Err(Error::Validation(Error::MESSAGE_SENDER_EMPTY));
}
// Per DBC spec Section 8.3: DLC can be 0-8 for CAN 2.0, 0-64 for CAN FD
// DLC = 0 is valid (e.g., for control messages without data payload)
if dlc > 64 {
return Err(Error::Validation(Error::MESSAGE_DLC_TOO_LARGE));
}
// Validate ID is in a valid range
let id_valid = id <= Self::MAX_EXTENDED_ID
|| (Self::EXTENDED_ID_FLAG..=Self::MAX_EXTENDED_ID_WITH_FLAG).contains(&id)
|| id == Self::PSEUDO_MESSAGE_ID;
if !id_valid {
return Err(Error::Validation(Error::MESSAGE_ID_OUT_OF_RANGE));
}
// Validate that all signals fit within the message boundary
// Each signal must fit within: DLC * 8 bits
// - Classic CAN (2.0A/2.0B): DLC * 8 <= 64 bits (8 bytes)
// - CAN FD: DLC * 8 <= 512 bits (64 bytes)
// This ensures no signal extends beyond the message payload capacity
//
// EXCEPTION: Per spec Section 8.6, VECTOR__INDEPENDENT_SIG_MSG (ID 0xC0000000)
// is a special pseudo-message for "orphan" signals. Skip boundary validation
// for this pseudo-message since its signals aren't meant to be transmitted.
if id != Self::PSEUDO_MESSAGE_ID {
let max_bits = u16::from(dlc) * 8;
for signal in signals.iter() {
// Calculate the actual bit range for this signal (accounting for byte order)
let (lsb, msb) =
Self::bit_range(signal.start_bit(), signal.length(), signal.byte_order());
// Check if the signal extends beyond the message boundary
// The signal's highest bit position must be less than max_bits
let signal_max_bit = lsb.max(msb);
if signal_max_bit >= max_bits {
return Err(Error::Validation(Error::SIGNAL_EXTENDS_BEYOND_MESSAGE));
}
}
}
// Validate signal overlap detection
// Check if any two signals overlap in the same message
// Must account for byte order: little-endian signals extend forward,
// big-endian signals extend backward from start_bit
// NOTE: Multiplexed signals (signals with multiplexer_switch_value) are allowed
// to overlap because they're only active when the multiplexer has a specific value.
// We skip overlap checking for multiplexed signals.
// We iterate over pairs without collecting to avoid alloc
//
// EXCEPTION: Per spec Section 8.6, VECTOR__INDEPENDENT_SIG_MSG (ID 0xC0000000)
// is a special pseudo-message for "orphan" signals. Skip overlap validation
// for this pseudo-message since its signals aren't meant to be transmitted.
if id != Self::PSEUDO_MESSAGE_ID {
for (i, sig1) in signals.iter().enumerate() {
// Skip overlap check if sig1 is multiplexed
if sig1.multiplexer_switch_value().is_some() {
continue;
}
let (sig1_lsb, sig1_msb) =
Self::bit_range(sig1.start_bit(), sig1.length(), sig1.byte_order());
for sig2 in signals.iter().skip(i + 1) {
// Skip overlap check if sig2 is multiplexed
if sig2.multiplexer_switch_value().is_some() {
continue;
}
let (sig2_lsb, sig2_msb) =
Self::bit_range(sig2.start_bit(), sig2.length(), sig2.byte_order());
// Check if ranges overlap
// Two ranges [lsb1, msb1] and [lsb2, msb2] overlap if:
// lsb1 <= msb2 && lsb2 <= msb1
if sig1_lsb <= sig2_msb && sig2_lsb <= sig1_msb {
return Err(Error::Validation(Error::SIGNAL_OVERLAP));
}
}
}
}
Ok(())
}
pub(crate) fn bit_range(start_bit: u16, length: u16, byte_order: ByteOrder) -> (u16, u16) {
match byte_order {
ByteOrder::LittleEndian => {
// Little-endian: start_bit is LSB, signal extends forward (to higher bit positions)
// Range: [start_bit, start_bit + length - 1]
(start_bit, start_bit + length - 1)
}
ByteOrder::BigEndian => {
// Big-endian (Motorola): start_bit is MSB position in Vector convention
// Vector bit numbering: byte N contains bits [N*8+7, N*8+6, ..., N*8+0]
// where bit N*8+7 is the MSB of byte N
//
// For a BE signal:
// - First byte: uses bits from (start_bit % 8) down to 0
// - Subsequent bytes: uses bits from 7 down to 0 (or partial for last byte)
//
// Example: start_bit=7, length=16
// Byte 0: bits 7,6,5,4,3,2,1,0 (8 bits) -> linear bits 7-0
// Byte 1: bits 7,6,5,4,3,2,1,0 (8 bits) -> linear bits 15-8
// Linear range: [0, 15]
//
// Example: start_bit=23, length=16
// Byte 2: bits 7,6,5,4,3,2,1,0 (8 bits) -> linear bits 23-16
// Byte 3: bits 7,6,5,4,3,2,1,0 (8 bits) -> linear bits 31-24
// Linear range: [16, 31]
let start_byte = start_bit / 8;
let msb_bit_in_byte = start_bit % 8; // 0-7, where 7 is MSB of byte
let bits_in_first_byte = msb_bit_in_byte + 1;
if length <= bits_in_first_byte {
// Signal fits within one byte
// Uses bits from msb_bit_in_byte down to (msb_bit_in_byte - length + 1)
let lsb = start_bit - (length - 1);
(lsb, start_bit)
} else {
// Signal spans multiple bytes
let remaining = length - bits_in_first_byte;
let additional_full_bytes = remaining / 8;
let bits_in_last_byte = remaining % 8;
// Calculate last byte index
let last_byte = if bits_in_last_byte > 0 {
start_byte + 1 + additional_full_bytes
} else {
// All remaining bits fit in full bytes
start_byte + additional_full_bytes
};
// For multi-byte BE signals:
// - Min bit is bit 0 of the first byte (we go all the way down)
// - Max bit is bit 7 of the last byte (subsequent bytes start at bit 7)
let min_bit = start_byte * 8;
let max_bit = last_byte * 8 + 7;
(min_bit, max_bit)
}
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::Parser;
#[test]
fn test_message_big_endian_bit_range_calculation() {
// Test big-endian bit range calculation
// For BE, start_bit is the MSB position
// A typical BE signal starts at bit 7 (MSB of byte 0)
let data = b"BO_ 256 EngineData : 8 ECM";
let mut parser = Parser::new(data).unwrap();
// Signal starting at BE bit 7 (MSB of byte 0), length 8 -> bytes 0
let signal =
Signal::parse(&mut Parser::new(b"SG_ Signal1 : 7|8@0+ (1,0) [0|255] \"\"").unwrap())
.unwrap();
let message = Message::parse(&mut parser, &[signal]).unwrap();
// The signal should be valid and fit within the message
assert_eq!(message.signals().len(), 1);
}
#[test]
fn test_message_little_endian_bit_range_calculation() {
// Test little-endian bit range calculation
// LE bit N -> physical bit N (straightforward mapping)
let data = b"BO_ 256 EngineData : 8 ECM";
let mut parser = Parser::new(data).unwrap();
// Signal starting at LE bit 0, length 8 -> should map to physical bits 0-7
let signal =
Signal::parse(&mut Parser::new(b"SG_ Signal1 : 0|8@1+ (1,0) [0|255] \"\"").unwrap())
.unwrap();
let message = Message::parse(&mut parser, &[signal]).unwrap();
// The signal should be valid and fit within the message
assert_eq!(message.signals().len(), 1);
}
#[test]
fn test_message_multiple_signals_boundary_validation() {
// Test that signals at message boundaries are validated correctly
let data = b"BO_ 256 EngineData : 8 ECM";
let mut parser = Parser::new(data).unwrap();
// Create signals that exactly fill the message (8 bytes = 64 bits)
// Using little-endian (@1+) where start_bit is LSB position
// Signal 1: bits 0-15 (16 bits)
let signal1 =
Signal::parse(&mut Parser::new(b"SG_ Signal1 : 0|16@1+ (1,0) [0|65535] \"\"").unwrap())
.unwrap();
// Signal 2: bits 16-31 (16 bits)
let signal2 = Signal::parse(
&mut Parser::new(b"SG_ Signal2 : 16|16@1+ (1,0) [0|65535] \"\"").unwrap(),
)
.unwrap();
// Signal 3: bits 32-47 (16 bits)
let signal3 = Signal::parse(
&mut Parser::new(b"SG_ Signal3 : 32|16@1+ (1,0) [0|65535] \"\"").unwrap(),
)
.unwrap();
// Signal 4: bits 48-63 (16 bits) - exactly at boundary
let signal4 = Signal::parse(
&mut Parser::new(b"SG_ Signal4 : 48|16@1+ (1,0) [0|65535] \"\"").unwrap(),
)
.unwrap();
let message = Message::parse(&mut parser, &[signal1, signal2, signal3, signal4]).unwrap();
assert_eq!(message.signals().len(), 4);
}
#[test]
fn test_big_endian_non_overlapping_signals() {
// Test that non-overlapping big-endian signals are correctly detected as non-overlapping
// This was a regression where BE signals in different bytes were incorrectly flagged as overlapping
let data = b"BO_ 768 WheelSpeeds : 8 ECU";
let mut parser = Parser::new(data).unwrap();
// Signal 1: BE, start_bit=7 (byte 0 MSB), length=16 -> bytes 0-1
let signal1 = Signal::parse(
&mut Parser::new(b"SG_ WheelSpeed_FL : 7|16@0+ (0.01,0) [0|300] \"km/h\"").unwrap(),
)
.unwrap();
// Signal 2: BE, start_bit=23 (byte 2 MSB), length=16 -> bytes 2-3
let signal2 = Signal::parse(
&mut Parser::new(b"SG_ WheelSpeed_FR : 23|16@0+ (0.01,0) [0|300] \"km/h\"").unwrap(),
)
.unwrap();
// These signals should NOT overlap (bytes 0-1 vs bytes 2-3)
let message = Message::parse(&mut parser, &[signal1, signal2]).unwrap();
assert_eq!(message.signals().len(), 2);
}
#[test]
fn test_big_endian_overlapping_signals_detected() {
// Test that overlapping big-endian signals ARE correctly detected
let data = b"BO_ 256 Test : 8 ECU";
let mut parser = Parser::new(data).unwrap();
// Signal 1: BE, start_bit=7, length=16 -> bytes 0-1
let signal1 =
Signal::parse(&mut Parser::new(b"SG_ Signal1 : 7|16@0+ (1,0) [0|65535] \"\"").unwrap())
.unwrap();
// Signal 2: BE, start_bit=15, length=16 -> bytes 1-2 (overlaps with signal1 in byte 1)
let signal2 = Signal::parse(
&mut Parser::new(b"SG_ Signal2 : 15|16@0+ (1,0) [0|65535] \"\"").unwrap(),
)
.unwrap();
// These signals SHOULD overlap (both use byte 1)
let result = Message::parse(&mut parser, &[signal1, signal2]);
assert!(result.is_err());
}
#[test]
fn test_bit_range_big_endian_single_byte() {
// Single byte BE signal: start_bit=5, length=4 -> bits 5,4,3,2
let (min, max) = Message::bit_range(5, 4, ByteOrder::BigEndian);
assert_eq!(min, 2);
assert_eq!(max, 5);
}
#[test]
fn test_bit_range_big_endian_multi_byte() {
// Multi-byte BE signal: start_bit=7, length=16 -> bytes 0-1, bits 0-15
let (min, max) = Message::bit_range(7, 16, ByteOrder::BigEndian);
assert_eq!(min, 0);
assert_eq!(max, 15);
// Multi-byte BE signal: start_bit=23, length=16 -> bytes 2-3, bits 16-31
let (min, max) = Message::bit_range(23, 16, ByteOrder::BigEndian);
assert_eq!(min, 16);
assert_eq!(max, 31);
}
#[test]
fn test_bit_range_little_endian() {
// LE signal: start_bit=0, length=16 -> bits 0-15
let (min, max) = Message::bit_range(0, 16, ByteOrder::LittleEndian);
assert_eq!(min, 0);
assert_eq!(max, 15);
// LE signal: start_bit=16, length=16 -> bits 16-31
let (min, max) = Message::bit_range(16, 16, ByteOrder::LittleEndian);
assert_eq!(min, 16);
assert_eq!(max, 31);
}
}