Branch data Line data Source code
1 : : // SPDX-License-Identifier: MIT
2 : : #include "dbc_statement_handler.h"
3 : : #include "j1939.h"
4 : : #include "text_utils.h"
5 : : #include "signal_layout.h"
6 : : #include <charconv>
7 : : #include <cmath>
8 : : #include <limits>
9 : : #include <regex>
10 : : #include <sstream>
11 : : #include <stdexcept>
12 : :
13 : : namespace {
14 : 2466 : std::uint32_t ParseDecimal(const std::string &text,
15 : : std::uint32_t maximum,
16 : : const char *field) {
17 : 2466 : std::uint32_t value = 0U;
18 [ + - ]: 2466 : const std::from_chars_result parsed = std::from_chars(text.data(), text.data() + text.size(), value);
19 [ + + ]: 4932 : if (text.empty() || parsed.ec != std::errc() ||
20 [ + - + + : 4932 : parsed.ptr != text.data() + text.size() || value > maximum)
+ + + + ]
21 [ + - + - : 174 : throw std::runtime_error(std::string("Invalid ") + field + ": " + text);
+ - + - +
- ]
22 : 2408 : return value;
23 : : }
24 : :
25 : 784 : std::uint32_t ParseCanId(const std::string &text) {
26 : 784 : const std::uint32_t rawId = ParseDecimal(
27 : : text, std::numeric_limits<std::uint32_t>::max(), "CAN ID");
28 : : // Only bit 31 may accompany the actual 29-bit identifier.
29 [ + + ]: 750 : if ((rawId & 0x60000000U) != 0U)
30 [ + - ]: 20 : throw std::runtime_error("CAN ID exceeds the 29-bit identifier range.");
31 : 730 : return rawId;
32 : : }
33 : :
34 : 1666 : double ParsePhysicalValue(const std::string &text, const char *field) {
35 : : static const std::regex number(
36 [ + + + - : 1666 : R"(^[+-]?(?:[0-9]+(?:\.[0-9]*)?|\.[0-9]+)(?:[eE][+-]?[0-9]+)?$)");
+ - - - ]
37 [ + - + + ]: 1666 : if (!std::regex_match(text, number))
38 [ + - + - : 336 : throw std::runtime_error(std::string("Invalid ") + field + ": " + text);
+ - + - +
- ]
39 : : // Floating-point from_chars is locale-independent and does not accept
40 : : // a leading '+', which the DBC numeric grammar permits.
41 : 1554 : const char *begin = text.data();
42 : 1554 : const char *end = begin + text.size();
43 [ + + ]: 1554 : if (*begin == '+')
44 : 18 : ++begin;
45 : 1554 : double value = 0.0;
46 : : const std::from_chars_result parsed =
47 : 1554 : std::from_chars(begin, end, value, std::chars_format::general);
48 : : // Keep the existing rejection of conversion underflow into subnormal values.
49 [ + + + - : 3084 : if (parsed.ec != std::errc() || parsed.ptr != end || !std::isfinite(value) ||
+ - + + +
+ ]
50 : 1530 : std::fpclassify(value) == FP_SUBNORMAL)
51 [ + - + - : 120 : throw std::runtime_error(std::string("Invalid ") + field + ": " + text);
+ - + - +
- ]
52 : 1514 : return value;
53 : : }
54 : :
55 : 496 : Multiplexing ParseMultiplexingMarker(const std::string &marker) {
56 : 496 : Multiplexing result;
57 [ + - + + ]: 496 : if (marker == "M")
58 : 34 : result.role = MultiplexRole::Selector;
59 [ + + ]: 462 : else if (!marker.empty()) {
60 [ + + ]: 38 : if (marker[0] != 'm')
61 [ + - ]: 1 : throw std::runtime_error("Unsupported multiplexing marker.");
62 [ + - ]: 37 : const std::string value = marker.substr(1U);
63 : : const std::from_chars_result parsed =
64 [ + - ]: 37 : std::from_chars(value.data(), value.data() + value.size(), result.value);
65 [ + + + + : 37 : if (value.empty() || parsed.ec != std::errc() || parsed.ptr != value.data() + value.size())
+ + + + ]
66 [ + - ]: 7 : throw std::runtime_error("Invalid or unsupported multiplexing marker.");
67 : 30 : result.role = MultiplexRole::Branch;
68 : 37 : }
69 : :
70 : 488 : return result;
71 : : }
72 : :
73 : 104 : RawSignalValue ParseValueKey(const std::string &number) {
74 : 104 : RawSignalValue key;
75 [ + - + + : 104 : if (!number.empty() && number[0] == '-') {
+ + ]
76 : 16 : std::int64_t value = 0;
77 : : const std::from_chars_result parsed =
78 [ + - ]: 16 : std::from_chars(number.data(), number.data() + number.size(), value);
79 [ + + - + : 16 : if (parsed.ec != std::errc() || parsed.ptr != number.data() + number.size())
+ + ]
80 [ + - + - ]: 2 : throw std::runtime_error("Invalid VAL_ integer: " + number);
81 : 14 : key = NormalizeValueKey(RawSignalValue(value));
82 : : } else {
83 : 88 : std::uint64_t value = 0;
84 : : const std::from_chars_result parsed =
85 [ + - ]: 88 : std::from_chars(number.data(), number.data() + number.size(), value);
86 [ + - + + : 88 : if (number.empty() || parsed.ec != std::errc() || parsed.ptr != number.data() + number.size())
+ + + + ]
87 [ + - + - ]: 6 : throw std::runtime_error("Invalid VAL_ integer: " + number);
88 : 82 : key = value;
89 : : }
90 : 96 : return key;
91 : : }
92 : :
93 : 96 : std::string UnescapeDescription(const std::string "ed) {
94 : 96 : std::string description;
95 [ + + ]: 724 : for (std::size_t i = 0; i < quoted.size(); ++i) {
96 [ + + ]: 628 : if (quoted[i] == '\\')
97 : 7 : ++i;
98 [ + - ]: 628 : description += quoted[i];
99 : : }
100 : 96 : return description;
101 : 0 : }
102 : :
103 : 67 : FrameInfo *FindUniqueFrame(DbcDatabase &database, std::uint32_t canId, bool isExtended) {
104 : 67 : FrameInfo *frame = nullptr;
105 [ + + ]: 178 : for (FrameInfo &candidate : database.frames) {
106 [ + + + + ]: 113 : if (candidate.canId == canId && candidate.isExtended == isExtended) {
107 [ + + ]: 66 : if (frame)
108 [ + - ]: 2 : throw std::runtime_error("Ambiguous VAL_ frame reference.");
109 : 64 : frame = &candidate;
110 : : }
111 : : }
112 [ + + ]: 65 : if (!frame)
113 [ + - ]: 3 : throw std::runtime_error("Unresolved VAL_ frame reference.");
114 : 62 : return frame;
115 : : }
116 : :
117 : 62 : SignalInfo *FindUniqueSignal(FrameInfo &frame, const std::string &signalName) {
118 : 62 : SignalInfo *signal = nullptr;
119 [ + + ]: 149 : for (SignalInfo &candidate : frame.signals) {
120 [ + + ]: 89 : if (candidate.name == signalName) {
121 [ + + ]: 59 : if (signal)
122 [ + - + - ]: 2 : throw std::runtime_error("Ambiguous VAL_ signal reference: " + signalName);
123 : 57 : signal = &candidate;
124 : : }
125 : : }
126 [ + + ]: 60 : if (!signal)
127 [ + - + - ]: 5 : throw std::runtime_error("Unresolved VAL_ signal reference: " + signalName);
128 : 55 : return signal;
129 : : }
130 : :
131 : 884 : std::uint64_t NextSignalBit(std::uint64_t bit, bool isLittleEndian) {
132 [ + + ]: 884 : if (isLittleEndian)
133 : 724 : return bit + 1U;
134 : : // From bit 0, Motorola continues at bit 7 of the next byte (0 -> 15).
135 [ + + ]: 160 : return bit % 8U == 0U ? bit + 15U : bit - 1U;
136 : : }
137 : :
138 : : FrameInfo *FindFrameById(DbcDatabase &, std::uint32_t, bool);
139 : : SignalInfo *FindSignalByName(FrameInfo &, const std::string &);
140 : : } // namespace
141 : :
142 : : namespace {
143 : 317 : std::vector<std::string> SplitReceivers(const std::string &text) {
144 : 317 : std::vector<std::string> receivers;
145 : 317 : std::string token;
146 [ + - ]: 317 : std::istringstream stream(text);
147 : :
148 [ + - + - : 956 : while (std::getline(stream, token, ',')) {
+ + ]
149 [ + - ]: 322 : token = dbc_detail::TrimText(token);
150 [ - + ]: 322 : if (!token.empty())
151 [ + - ]: 322 : receivers.push_back(token);
152 : : }
153 : :
154 : 317 : return receivers;
155 : 317 : }
156 : :
157 : 803 : void NormalizeCanId(std::uint32_t rawCanId, std::uint32_t &normalizedCanId, bool &isExtended) {
158 : : /*
159 : : * DBC commonly stores extended identifiers with bit 31 set.
160 : : * Example:
161 : : * raw id = 0x80000000 | actual_29_bit_id
162 : : */
163 [ + + + + ]: 803 : isExtended = (rawCanId & 0x80000000U) != 0U || rawCanId > 0x7FFU;
164 : 803 : normalizedCanId = rawCanId & 0x1FFFFFFFU;
165 : 803 : }
166 : : } // namespace
167 : :
168 : 649 : FrameInfo DbcFrameHandler::ParseFrameLine(const std::string &line) {
169 : : /*
170 : : * Example:
171 : : * BO_ 256 EngineData: 8 EEC1
172 : : */
173 : :
174 [ + - ]: 649 : std::istringstream stream(line);
175 : 649 : std::string keyword;
176 : 649 : std::string idText;
177 [ + - + - : 649 : if (!(stream >> keyword >> idText) || keyword != "BO_")
+ - + + +
- - + +
+ ]
178 [ + - ]: 3 : throw std::runtime_error("Missing CAN ID in frame definition.");
179 : :
180 [ + + ]: 646 : const std::uint32_t rawCanId = ParseCanId(idText);
181 : :
182 : 628 : std::string nameText;
183 [ + - + - : 628 : if (!std::getline(stream, nameText, ':') || stream.eof())
+ + + - +
+ + + ]
184 [ + - ]: 2 : throw std::runtime_error("Missing ':' in frame definition.");
185 [ + - ]: 626 : std::istringstream nameStream(nameText);
186 : 626 : FrameInfo frame;
187 : 626 : std::string extra;
188 [ + - + - : 626 : if (!(nameStream >> frame.name) || (nameStream >> extra))
+ + + - +
- + + +
+ ]
189 [ + - ]: 2 : throw std::runtime_error("Missing or invalid frame name.");
190 : :
191 : 624 : std::string lengthText;
192 [ + - + - : 624 : if (!(stream >> lengthText))
+ + ]
193 [ + - ]: 1 : throw std::runtime_error("Missing payload length in frame definition.");
194 [ + + ]: 623 : frame.dlc = static_cast<std::uint8_t>(ParseDecimal(
195 : 623 : lengthText, std::numeric_limits<std::uint8_t>::max(), "payload length"));
196 [ + - + - : 617 : if (!(stream >> frame.transmitter))
+ + ]
197 [ + - ]: 1 : throw std::runtime_error("Missing transmitter in frame definition.");
198 [ + - + - : 616 : if (stream >> extra)
+ + ]
199 [ + - + - ]: 1 : throw std::runtime_error("Unexpected trailing field in frame definition: " + extra);
200 : :
201 : 615 : NormalizeCanId(rawCanId, frame.canId, frame.isExtended);
202 : :
203 : 615 : return frame;
204 : 772 : }
205 : :
206 : 521 : SignalInfo DbcSignalHandler::ParseSignalLine(const std::string &line) {
207 : : // Match the complete statement before converting any numeric fields.
208 : : enum Field {
209 : : NameAndMarker = 1, StartBit, BitLength, ByteOrder, Signedness,
210 : : Factor, Offset, Minimum, Maximum, Unit, Receivers
211 : : };
212 : : static const std::regex syntax(
213 : : // Name and optional multiplexing marker.
214 : : R"dbc(^SG_[ \t]+([^:]+):[ \t]*)dbc"
215 : : // Bit position, length, byte order and signedness.
216 : : R"dbc(([^| \t]+)[ \t]*\|[ \t]*([^@ \t]+)[ \t]*@[ \t]*([01])([+-])[ \t]*)dbc"
217 : : // Scaling factor and offset.
218 : : R"dbc(\([ \t]*([^, \t]+)[ \t]*,[ \t]*([^ )\t]+)[ \t]*\)[ \t]*)dbc"
219 : : // Physical range.
220 : : R"dbc(\[[ \t]*([^| \t]+)[ \t]*\|[ \t]*([^\] \t]+)[ \t]*\][ \t]*)dbc"
221 : : // Unit and receivers.
222 [ + + + - : 521 : R"dbc("([^"\r\n]*)"[ \t]+([^ \t,]+(?:[ \t]*,[ \t]*[^ \t,]+)*)[ \t]*$)dbc");
+ - - - ]
223 : 521 : std::smatch fields;
224 [ + - + + ]: 521 : if (!std::regex_match(line, fields, syntax))
225 [ + - ]: 23 : throw std::runtime_error("Malformed or unsupported SG_ signal definition.");
226 : :
227 : 498 : SignalInfo signal;
228 [ + - + - : 498 : std::istringstream nameFields(fields[NameAndMarker].str());
+ - ]
229 : 498 : std::string marker;
230 : 498 : std::string extra;
231 [ + - + - ]: 498 : nameFields >> signal.name >> marker;
232 [ + + + - : 498 : if (signal.name.empty() || (nameFields >> extra))
+ - + + +
+ ]
233 [ + - ]: 2 : throw std::runtime_error("Invalid signal name or multiplexing marker.");
234 [ + + ]: 496 : signal.multiplexing = ParseMultiplexingMarker(marker);
235 : :
236 [ + - + - : 495 : signal.startBit = ParseDecimal(fields[StartBit].str(),
+ + ]
237 : : std::numeric_limits<std::uint32_t>::max(), "signal start bit");
238 [ + - + - : 489 : signal.length = ParseDecimal(fields[BitLength].str(), 64U, "signal length");
+ + ]
239 [ + + ]: 473 : if (signal.length == 0U)
240 [ + - ]: 1 : throw std::runtime_error("Signal length must be within 1..64 bits.");
241 [ + - + - : 472 : signal.isLittleEndian = fields[ByteOrder].str() == "1";
+ - ]
242 [ + - + - : 472 : signal.isSigned = fields[Signedness].str() == "-";
+ - ]
243 : :
244 [ + - + - : 504 : signal.factor = ParsePhysicalValue(fields[Factor].str(), "signal factor");
+ + ]
245 [ + - + - : 484 : signal.offset = ParsePhysicalValue(fields[Offset].str(), "signal offset");
+ + ]
246 [ + - + - : 434 : signal.minimum = ParsePhysicalValue(fields[Minimum].str(), "signal minimum");
+ + ]
247 [ + - + - : 396 : signal.maximum = ParsePhysicalValue(fields[Maximum].str(), "signal maximum");
+ + ]
248 [ + + ]: 320 : if (signal.minimum > signal.maximum)
249 [ + - ]: 3 : throw std::runtime_error("Signal minimum exceeds maximum.");
250 [ + - + - ]: 317 : signal.unit = fields[Unit].str();
251 [ + - + - : 317 : signal.receivers = SplitReceivers(fields[Receivers].str());
+ - ]
252 : 317 : return signal;
253 : 1245 : }
254 : :
255 : :
256 : : namespace {
257 : 698 : FrameInfo *FindFrameById(DbcDatabase &database,
258 : : std::uint32_t canId,
259 : : bool isExtended) {
260 [ + + ]: 757 : for (FrameInfo &frame : database.frames) {
261 [ + + + + ]: 144 : if (frame.canId == canId && frame.isExtended == isExtended)
262 : 85 : return &frame;
263 : : }
264 : :
265 : 613 : return nullptr;
266 : : }
267 : :
268 : 364 : SignalInfo *FindSignalByName(FrameInfo &frame, const std::string &signalName) {
269 [ + + ]: 441 : for (SignalInfo &signal : frame.signals) {
270 [ + + ]: 126 : if (signal.name == signalName)
271 : 49 : return &signal;
272 : : }
273 : :
274 : 315 : return nullptr;
275 : : }
276 : : }// namespace
277 : :
278 : 649 : void DbcFrameHandler::Handle(const std::string &line, DbcParseContext &context) const {
279 [ + + ]: 649 : FrameInfo frame = ParseFrameLine(line);
280 [ + + + + ]: 615 : if (context.enableJ1939 && frame.isExtended) {
281 : 27 : frame.hasPgn = true;
282 : 27 : frame.pgn = ExtractJ1939Pgn(frame.canId);
283 : : }
284 [ + + ]: 615 : if (FindFrameById(context.database, frame.canId, frame.isExtended) != nullptr)
285 [ + + + - : 16 : throw std::runtime_error("Duplicate " + std::string(frame.isExtended ? "extended" : "standard") +
+ - ]
286 [ + - + - : 24 : " frame CAN ID: " + std::to_string(frame.canId));
+ - ]
287 [ + - ]: 607 : context.database.frames.push_back(std::move(frame));
288 : 607 : context.currentFrame = &context.database.frames.back();
289 [ + - ]: 607 : context.signalLines.emplace_back();
290 : 615 : }
291 : :
292 : 526 : void DbcSignalHandler::Handle(const std::string &line, DbcParseContext &context) const {
293 [ + + ]: 526 : if (context.currentFrame == nullptr)
294 [ + - ]: 5 : throw std::runtime_error("Signal found before any frame definition.");
295 [ + + ]: 521 : SignalInfo signal = ParseSignalLine(line);
296 [ + + ]: 317 : if (FindSignalByName(*context.currentFrame, signal.name) != nullptr)
297 : 6 : throw std::runtime_error("Duplicate signal name '" + signal.name +
298 [ + - + - : 12 : "' in frame '" + context.currentFrame->name + "'.");
+ - + - +
- ]
299 [ + + ]: 311 : if (!IsValidSignalLayout(signal.startBit, signal.length, signal.isLittleEndian,
300 : 311 : context.currentFrame->dlc))
301 [ + - ]: 7 : throw std::runtime_error("Signal exceeds the frame payload length.");
302 [ + - ]: 304 : context.currentFrame->signals.push_back(std::move(signal));
303 [ + - ]: 304 : context.signalLines.back().push_back(context.lineNumber);
304 : 317 : }
305 : :
306 : 149 : void DbcCommentHandler::Handle(const std::string &line, DbcParseContext &context) const {
307 [ + - ]: 149 : std::istringstream stream(line);
308 : 149 : std::string keyword;
309 : 149 : std::string target;
310 [ + - + - ]: 149 : stream >> keyword >> target;
311 [ + - + + : 149 : if (keyword != "CM_" || (target != "BO_" && target != "SG_"))
+ - + + +
- + + +
+ ]
312 : 9 : return;
313 : :
314 : 140 : PendingComment pending;
315 [ + - ]: 140 : pending.isSignal = target == "SG_";
316 : 140 : pending.lineNumber = context.lineNumber;
317 : 140 : std::string idText;
318 [ + - + - : 140 : if (!(stream >> idText))
+ + ]
319 [ + - ]: 2 : throw std::runtime_error("Missing CAN ID in CM_ statement.");
320 [ + + ]: 138 : NormalizeCanId(ParseCanId(idText), pending.canId, pending.isExtended);
321 [ + + + - : 157 : if (pending.isSignal && (!(stream >> pending.signalName) ||
+ - + - +
+ + + ]
322 : 55 : pending.signalName.find_first_of("\";") != std::string::npos))
323 [ + - ]: 2 : throw std::runtime_error("Missing or invalid signal name in CM_ statement.");
324 : :
325 [ + - ]: 100 : stream >> std::ws;
326 [ + - + + ]: 100 : if (stream.get() != '"')
327 [ + - ]: 4 : throw std::runtime_error("Missing opening quote in CM_ statement.");
328 : 96 : bool closed = false;
329 : : char character;
330 [ + - + - : 1096 : while (stream.get(character)) {
+ + ]
331 [ + + ]: 1094 : if (character == '"') {
332 : 90 : closed = true;
333 : 90 : break;
334 : : }
335 [ + + ]: 1004 : if (character == '\\') {
336 [ + - + - : 7 : if (!stream.get(character) || (character != '"' && character != '\\'))
+ + + + +
+ + + ]
337 [ + - ]: 4 : throw std::runtime_error("Invalid escape in CM_ statement.");
338 : : }
339 [ + - ]: 1000 : pending.comment += character;
340 : : }
341 [ + + ]: 92 : if (!closed)
342 [ + - ]: 2 : throw std::runtime_error("Unterminated CM_ comment.");
343 [ + - ]: 90 : stream >> std::ws;
344 [ + - + + ]: 90 : if (stream.get() != ';')
345 [ + - ]: 4 : throw std::runtime_error("Missing semicolon in CM_ statement.");
346 [ + - ]: 86 : stream >> std::ws;
347 [ + - + + ]: 86 : if (!stream.eof())
348 [ + - ]: 2 : throw std::runtime_error("Unexpected trailing text in CM_ statement.");
349 [ + - ]: 84 : context.pendingComments.push_back(std::move(pending));
350 [ + + + + : 391 : }
+ + ]
351 : :
352 : 597 : void DbcParseContext::ResolveComments(const std::string &filePath) {
353 [ + + ]: 670 : for (const PendingComment &pending: pendingComments) {
354 : 83 : FrameInfo *frame = FindFrameById(database, pending.canId, pending.isExtended);
355 [ + + ]: 83 : if (!frame)
356 : : throw DbcDiagnosticError(DbcDiagnosticCode::InvalidStatement, filePath,
357 [ + - + - ]: 18 : pending.lineNumber, "Unresolved CM_ frame reference.");
358 [ + + ]: 77 : if (pending.isSignal) {
359 : 47 : SignalInfo *signal = FindSignalByName(*frame, pending.signalName);
360 [ + + ]: 47 : if (!signal)
361 : : throw DbcDiagnosticError(DbcDiagnosticCode::InvalidStatement, filePath,
362 [ + - + - ]: 4 : pending.lineNumber, "Unresolved CM_ signal reference: " + pending.signalName);
363 [ + - ]: 43 : signal->comment = pending.comment;
364 : : } else {
365 [ + - ]: 30 : frame->comment = pending.comment;
366 : : }
367 : : }
368 : 587 : }
369 : :
370 : 949 : DbcStatementDispatcher::DbcStatementDispatcher() {
371 [ + - + - ]: 1898 : RegisterType<DbcHeaderHandler>("VERSION");
372 [ + - + - ]: 1898 : RegisterType<DbcHeaderHandler>("NS_");
373 [ + - + - ]: 1898 : RegisterType<DbcHeaderHandler>("NS_:");
374 [ + - + - ]: 1898 : RegisterType<DbcHeaderHandler>("BS_");
375 [ + - + - ]: 1898 : RegisterType<DbcHeaderHandler>("BS_:");
376 [ + - + - ]: 1898 : RegisterType<DbcHeaderHandler>("BU_");
377 [ + - + - ]: 1898 : RegisterType<DbcHeaderHandler>("BU_:");
378 [ + - + - ]: 1898 : RegisterType<DbcUnsupportedMultiplexingHandler>("SG_MUL_VAL_");
379 [ + - + - ]: 1898 : RegisterType<DbcFrameHandler>("BO_");
380 [ + - + - ]: 1898 : RegisterType<DbcSignalHandler>("SG_");
381 [ + - + - ]: 1898 : RegisterType<DbcCommentHandler>("CM_");
382 [ + - + - ]: 1898 : RegisterType<DbcValueDescriptionHandler>("VAL_");
383 [ + - + - ]: 1898 : RegisterType<DbcAttributeHandler>("BA_DEF_");
384 [ + - + - ]: 1898 : RegisterType<DbcAttributeHandler>("BA_DEF_DEF_");
385 [ + - + - ]: 949 : RegisterType<DbcAttributeHandler>("BA_");
386 : 949 : }
387 : :
388 : 62 : void DbcHeaderHandler::Handle(const std::string &, DbcParseContext &) const {}
389 : :
390 : 2132 : bool DbcStatementDispatcher::Dispatch(const std::string &line,
391 : : DbcParseContext &context) const {
392 [ + - ]: 2132 : std::istringstream stream(line);
393 : 2132 : std::string keyword;
394 [ + - ]: 2132 : stream >> keyword;
395 [ + - ]: 2132 : const HandlerMap::const_iterator handler = m_handlers.find(keyword);
396 [ + + ]: 2132 : if (handler == m_handlers.end())
397 : 40 : return false;
398 [ + + ]: 2092 : handler->second->Handle(line, context);
399 : 1743 : return true;
400 : 2481 : }
401 : :
402 : 94 : void DbcValueDescriptionHandler::Handle(const std::string &line, DbcParseContext &context) const {
403 [ + + + - : 94 : static const std::regex header(R"(^VAL_[ \t]+([^ \t]+)[ \t]+([^ \t;]+)[ \t]+(.*)$)");
+ - - - ]
404 [ + + + - : 94 : static const std::regex pair(R"dbc(^([^ \t]+)[ \t]+"((?:[^"\\]|\\["\\])*)")dbc");
+ - - - ]
405 : 94 : std::smatch fields;
406 [ + - + + ]: 94 : if (!std::regex_match(line, fields, header))
407 [ + - ]: 4 : throw std::runtime_error("Malformed or unsupported VAL_ statement.");
408 [ + - + - : 93 : const std::uint32_t rawId = ParseDecimal(fields[1].str(), std::numeric_limits<std::uint32_t>::max(), "VAL_ CAN ID");
+ + ]
409 [ + + ]: 87 : if ((rawId & 0x60000000U) != 0U)
410 [ + - ]: 1 : throw std::runtime_error("VAL_ CAN ID exceeds the 29-bit identifier range.");
411 : 86 : PendingValueDescriptions pending;
412 : 86 : NormalizeCanId(rawId, pending.canId, pending.isExtended);
413 [ + - + - ]: 86 : pending.signalName = fields[2].str();
414 : 86 : pending.lineNumber = context.lineNumber;
415 [ + - + - ]: 86 : std::string rest = fields[3].str();
416 [ + - + + ]: 179 : while (rest != ";") {
417 [ + - + + ]: 109 : if (!std::regex_search(rest, fields, pair))
418 [ + - ]: 5 : throw std::runtime_error("Malformed VAL_ value/description pair or missing semicolon.");
419 [ + - + - ]: 104 : const std::string number = fields[1].str();
420 [ + + ]: 104 : const RawSignalValue key = ParseValueKey(number);
421 [ + - + - : 96 : const std::string description = UnescapeDescription(fields[2].str());
+ - ]
422 [ + - + + ]: 96 : if (!pending.descriptions.emplace(key, description).second)
423 [ + - + - ]: 2 : throw std::runtime_error("Duplicate VAL_ value key: " + number);
424 [ + - ]: 94 : const std::size_t consumed = static_cast<std::size_t>(fields.length());
425 [ + - ]: 94 : rest.erase(0, consumed);
426 [ + + + - : 94 : if (!rest.empty() && rest[0] != ';' && rest[0] != ' ' && rest[0] != '\t')
+ + + - +
+ + - + +
+ + ]
427 [ + - ]: 1 : throw std::runtime_error("Missing whitespace between VAL_ pairs.");
428 [ + - ]: 93 : rest = dbc_detail::TrimText(rest);
429 : 107 : }
430 [ + + ]: 70 : if (pending.descriptions.empty())
431 [ + - ]: 1 : throw std::runtime_error("VAL_ requires at least one value/description pair.");
432 [ + - ]: 69 : context.pendingValues.push_back(std::move(pending));
433 : 128 : }
434 : :
435 : 592 : void DbcParseContext::ResolveValueDescriptions(const std::string &filePath) {
436 [ + + ]: 644 : for (const PendingValueDescriptions &pending: pendingValues) {
437 : : try {
438 [ + + ]: 67 : FrameInfo *frame = FindUniqueFrame(database, pending.canId, pending.isExtended);
439 [ + + ]: 62 : SignalInfo *signal = FindUniqueSignal(*frame, pending.signalName);
440 [ + + ]: 127 : for (const ValueDescriptions::value_type &entry: pending.descriptions) {
441 [ + - + + ]: 75 : if (!signal->valueDescriptions.insert(entry).second)
442 [ + - + - ]: 3 : throw std::runtime_error("Duplicate VAL_ value key for signal: " + pending.signalName);
443 : : }
444 [ - + ]: 15 : } catch (const std::exception &error) {
445 : : throw DbcDiagnosticError(DbcDiagnosticCode::InvalidValueDescription,
446 [ + - + - ]: 45 : filePath, pending.lineNumber, error.what());
447 : 15 : }
448 : : }
449 : 577 : }
450 : :
451 : 4 : void DbcUnsupportedMultiplexingHandler::Handle(const std::string &, DbcParseContext &) const {
452 [ + - ]: 4 : throw std::runtime_error("Extended and nested multiplexing are unsupported.");
453 : : }
454 : :
455 : : namespace {
456 : 114 : std::vector<std::uint64_t> SignalBits(const SignalInfo &signal) {
457 : 114 : std::vector<std::uint64_t> bits;
458 : 114 : std::uint64_t bit = signal.startBit;
459 [ + + ]: 998 : for (std::uint32_t index = 0U; index < signal.length; ++index) {
460 [ + - ]: 884 : bits.push_back(bit);
461 : 884 : bit = NextSignalBit(bit, signal.isLittleEndian);
462 : : }
463 : 114 : return bits;
464 : 0 : }
465 : :
466 : 58 : bool AreMutuallyExclusive(const SignalInfo &first, const SignalInfo &second) {
467 : 79 : return first.multiplexing.role == MultiplexRole::Branch &&
468 [ + + + + ]: 62 : second.multiplexing.role == MultiplexRole::Branch &&
469 [ + + ]: 62 : first.multiplexing.value != second.multiplexing.value;
470 : : }
471 : :
472 : 193 : std::string ValidateBranch(const SignalInfo &signal, const SignalInfo *selector) {
473 [ + + ]: 193 : if (signal.multiplexing.role != MultiplexRole::Branch)
474 : 164 : return {};
475 [ + + ]: 29 : if (selector == nullptr)
476 [ + - ]: 8 : return "Multiplexed branch has no selector.";
477 [ + + + + ]: 25 : if (selector->length < 64U && signal.multiplexing.value >= (std::uint64_t(1U) << selector->length))
478 [ + - ]: 10 : return "Branch value exceeds selector raw range.";
479 : 20 : return {};
480 : : }
481 : :
482 : 280 : const SignalInfo *FindSelector(const FrameInfo &frame,
483 : : const std::vector<std::size_t> &lines,
484 : : const std::string &filePath) {
485 : 280 : const SignalInfo *selector = nullptr;
486 [ + + ]: 479 : for (std::size_t index = 0U; index < frame.signals.size(); ++index) {
487 : 207 : const SignalInfo &signal = frame.signals[index];
488 [ + + ]: 207 : if (signal.multiplexing.role != MultiplexRole::Selector)
489 : 173 : continue;
490 [ + + + + ]: 34 : if (selector != nullptr || signal.isSigned)
491 : : throw DbcDiagnosticError(DbcDiagnosticCode::InvalidMultiplexing,
492 : 8 : filePath, lines[index],
493 [ + - + - ]: 32 : "Multiplexing requires one unsigned selector per frame.");
494 : 26 : selector = &signal;
495 : : }
496 : 272 : return selector;
497 : : }
498 : :
499 : 57 : bool SignalsOverlap(const SignalInfo &first, const SignalInfo &second) {
500 [ + - ]: 57 : const std::vector<std::uint64_t> firstBits = SignalBits(first);
501 [ + - ]: 57 : const std::vector<std::uint64_t> secondBits = SignalBits(second);
502 [ + + ]: 489 : for (const std::uint64_t firstBit : firstBits) {
503 [ + + ]: 3380 : for (const std::uint64_t secondBit : secondBits) {
504 [ + + ]: 2948 : if (firstBit == secondBit)
505 : 10 : return true;
506 : : }
507 : : }
508 : 47 : return false;
509 : 57 : }
510 : : }
511 : :
512 : 576 : void DbcParseContext::ValidateMultiplexing(const std::string &filePath) const {
513 [ + + ]: 829 : for (std::size_t frameIndex = 0U; frameIndex < database.frames.size(); ++frameIndex) {
514 : 280 : const FrameInfo &frame = database.frames[frameIndex];
515 : 280 : const SignalInfo *selector = FindSelector(frame, signalLines[frameIndex], filePath);
516 [ + + ]: 446 : for (std::size_t index = 0U; index < frame.signals.size(); ++index) {
517 : 193 : const SignalInfo &signal = frame.signals[index];
518 [ + - ]: 193 : std::string error = ValidateBranch(signal, selector);
519 [ + + + + : 251 : for (std::size_t previous = 0U; previous < index && error.empty(); ++previous) {
+ + ]
520 : 58 : const SignalInfo &other = frame.signals[previous];
521 [ + + + - : 58 : if (!AreMutuallyExclusive(signal, other) && SignalsOverlap(signal, other))
+ + + + ]
522 [ + - ]: 10 : error = "Signals overlap without mutually exclusive multiplexing conditions.";
523 : : }
524 [ + + ]: 193 : if (!error.empty())
525 : : throw DbcDiagnosticError(DbcDiagnosticCode::InvalidMultiplexing,
526 [ + - ]: 19 : filePath, signalLines[frameIndex][index], error);
527 : 193 : }
528 : : }
529 : 549 : }
|