Branch data Line data Source code
1 : : // SPDX-License-Identifier: MIT
2 : : // Copyright (c) 2026 Dim Himro
3 : :
4 : : #include "decode_database.h"
5 : : #include "j1939.h"
6 : : #include "signal_layout.h"
7 : : #include <bitset>
8 : : #include <cmath>
9 : : #include <limits>
10 : : #include <stdexcept>
11 : : #include <unordered_set>
12 : :
13 : : namespace {
14 : : constexpr std::size_t MaximumPayloadBits = std::numeric_limits<std::uint8_t>::max() * 8U;
15 : : using SignalBits = std::bitset<MaximumPayloadBits>;
16 : :
17 : 2476 : void ValidateSignal(const DecodeSignal &signal, std::uint8_t dlc) {
18 [ + + + + ]: 2476 : if (signal.byteOrder != ByteOrder::Intel && signal.byteOrder != ByteOrder::Motorola)
19 [ + - + - ]: 3 : throw std::invalid_argument("Invalid signal byte order: " + signal.name);
20 [ + + + + ]: 2473 : if (signal.valueType != ValueType::Unsigned && signal.valueType != ValueType::Signed)
21 [ + - + - ]: 3 : throw std::invalid_argument("Invalid signal value type: " + signal.name);
22 [ + + ]: 2470 : if (signal.multiplexing.role != MultiplexRole::Unconditional &&
23 [ + + ]: 75 : signal.multiplexing.role != MultiplexRole::Selector &&
24 [ + + ]: 38 : signal.multiplexing.role != MultiplexRole::Branch)
25 [ + - + - ]: 4 : throw std::invalid_argument("Invalid multiplexing role: " + signal.name);
26 [ + + ]: 2466 : if (!IsValidSignalLayout(signal.startBit, signal.length,
27 : 2466 : signal.byteOrder == ByteOrder::Intel, dlc))
28 [ + - + - ]: 36 : throw std::invalid_argument("Invalid signal length or payload position: " + signal.name);
29 [ + + ]: 4847 : if (!std::isfinite(signal.factor) || !std::isfinite(signal.offset) ||
30 [ + + + + : 7240 : !std::isfinite(signal.minimum) || !std::isfinite(signal.maximum) ||
+ + + + ]
31 [ + + ]: 2393 : signal.minimum > signal.maximum)
32 [ + - + - ]: 39 : throw std::invalid_argument("Invalid signal numeric parameters: " + signal.name);
33 : 2391 : }
34 : :
35 : 2364 : SignalBits GetSignalBits(const DecodeSignal &signal) {
36 : 2364 : SignalBits bits;
37 : 2364 : const bool littleEndian = signal.byteOrder == ByteOrder::Intel;
38 : 2364 : const std::uint64_t start = SignalStartCoordinate(signal.startBit, littleEndian);
39 [ + + ]: 75593 : for (std::uint32_t index = 0U; index < signal.length; ++index) {
40 : 73229 : const std::uint64_t coordinate = start + index;
41 [ + + ]: 73229 : const std::uint64_t bit = littleEndian ? coordinate
42 : 36098 : : coordinate / 8U * 8U + 7U - coordinate % 8U;
43 : 73229 : bits.set(static_cast<std::size_t>(bit));
44 : : }
45 : 2364 : return bits;
46 : : }
47 : :
48 : 71 : bool AreMutuallyExclusive(const DecodeSignal &first, const DecodeSignal &second) {
49 : 96 : return first.multiplexing.role == MultiplexRole::Branch &&
50 [ + + + + ]: 77 : second.multiplexing.role == MultiplexRole::Branch &&
51 [ + + ]: 77 : first.multiplexing.value != second.multiplexing.value;
52 : : }
53 : :
54 : 2447 : void ValidateFrame(const DecodeFrame &frame) {
55 [ + + ]: 2447 : const std::uint32_t maximumId = frame.isExtended ? 0x1FFFFFFFU : 0x7FFU;
56 [ + + ]: 2447 : if (frame.canId > maximumId)
57 [ + - + - ]: 9 : throw std::invalid_argument("Invalid normalized CAN ID: " + std::to_string(frame.canId));
58 : :
59 [ + + + + : 2438 : if (frame.hasPgn && (!frame.isExtended || frame.pgn != ExtractJ1939Pgn(frame.canId)))
+ + + + ]
60 [ + - + - ]: 3 : throw std::invalid_argument("Invalid J1939 PGN metadata: " + frame.name);
61 : :
62 : 2435 : std::unordered_set<std::string> names;
63 : 2435 : const DecodeSignal *selector = nullptr;
64 [ + + ]: 4820 : for (const DecodeSignal &signal: frame.signals) {
65 [ + - + + ]: 2480 : if (!names.insert(signal.name).second)
66 [ + - + - ]: 4 : throw std::invalid_argument("Duplicate runtime signal key: " + signal.name);
67 [ + + ]: 2476 : ValidateSignal(signal, frame.dlc);
68 [ + + ]: 2391 : if (signal.multiplexing.role == MultiplexRole::Selector) {
69 [ + + + + ]: 35 : if (selector != nullptr || signal.valueType != ValueType::Unsigned)
70 [ + - ]: 6 : throw std::invalid_argument("Multiplexing requires one unsigned selector per frame");
71 : 29 : selector = &signal;
72 : : }
73 : : }
74 : :
75 : 2340 : std::vector<SignalBits> layouts;
76 [ + - ]: 2340 : layouts.reserve(frame.signals.size());
77 [ + + ]: 4696 : for (std::size_t index = 0U; index < frame.signals.size(); ++index) {
78 : 2372 : const DecodeSignal &signal = frame.signals[index];
79 [ + + + + ]: 2372 : if (signal.multiplexing.role == MultiplexRole::Branch &&
80 : 27 : (selector == nullptr ||
81 [ + + + + ]: 27 : (selector->length < 64U && signal.multiplexing.value >= (std::uint64_t(1U) << selector->length))))
82 [ + - + - ]: 8 : throw std::invalid_argument("Missing selector or branch value outside selector range: " + signal.name);
83 [ + - ]: 2364 : const SignalBits bits = GetSignalBits(signal);
84 [ + + ]: 2427 : for (std::size_t previous = 0U; previous < index; ++previous) {
85 [ + + + + : 71 : if (!AreMutuallyExclusive(signal, frame.signals[previous]) && (bits & layouts[previous]).any())
+ + ]
86 [ + - + - ]: 8 : throw std::invalid_argument("Overlapping signals: " + signal.name);
87 : : }
88 [ + - ]: 2356 : layouts.push_back(bits);
89 : : }
90 : 2451 : }
91 : : }// namespace
92 : :
93 : 2394 : DecodeDatabase::Storage::Storage(const std::vector<DecodeFrame> &definitions)
94 : 2394 : : frames(definitions) {
95 : : // Validate the complete snapshot before creating its lookup index.
96 : 2394 : std::unordered_set<FrameKey, FrameKeyHasher> keys;
97 [ + - ]: 2394 : keys.reserve(frames.size());
98 [ + + ]: 4713 : for (const DecodeFrame &frame: frames) {
99 [ + + ]: 2447 : ValidateFrame(frame);
100 [ + - + + ]: 2324 : if (!keys.insert(frame.GetKey()).second)
101 [ + - ]: 5 : throw std::invalid_argument("Duplicate runtime frame key");
102 : : }
103 [ + - ]: 2266 : frameIndexByKey.reserve(frames.size());
104 [ + + ]: 4580 : for (std::size_t index = 0U; index < frames.size(); ++index) {
105 [ + - ]: 2314 : frameIndexByKey.emplace(frames[index].GetKey(), index);
106 [ + + ]: 2314 : if (frames[index].hasPgn)
107 [ + - + - ]: 21 : frameIndicesByPgn[frames[index].pgn].push_back(index);
108 : : }
109 : 2778 : }
110 : :
111 : 2394 : DecodeDatabase::DecodeDatabase(const std::vector<DecodeFrame> &frames)
112 : 2394 : : storage(std::make_shared<const Storage>(frames)) {
113 : 2266 : }
114 : :
115 : 76 : const std::vector<DecodeFrame> &DecodeDatabase::GetFrames() const {
116 [ + + + - ]: 76 : static const std::vector<DecodeFrame> empty;
117 [ + + ]: 76 : return storage ? storage->frames : empty;
118 : : }
119 : :
120 : 6555 : std::shared_ptr<const DecodeFrame> DecodeDatabase::FindFrame(const FrameKey &key) const {
121 [ + + ]: 6555 : if (!storage)
122 : 16 : return nullptr;
123 : : const std::unordered_map<FrameKey, std::size_t, FrameKeyHasher>::const_iterator it =
124 [ + - ]: 6539 : storage->frameIndexByKey.find(key);
125 [ + + ]: 6539 : if (it == storage->frameIndexByKey.end())
126 : 115 : return nullptr;
127 : 6424 : return std::shared_ptr<const DecodeFrame>(storage, &storage->frames[it->second]);
128 : : }
129 : :
130 : 116 : PgnLookupResult DecodeDatabase::FindFrameByPgn(std::uint32_t pgn) const {
131 : 116 : PgnLookupResult result;
132 [ + + ]: 116 : if (!storage)
133 : 24 : return result;
134 : : const std::unordered_map<std::uint32_t, std::vector<std::size_t>>::const_iterator it =
135 [ + - ]: 92 : storage->frameIndicesByPgn.find(pgn);
136 [ + + ]: 92 : if (it == storage->frameIndicesByPgn.end())
137 : 47 : return result;
138 [ + + ]: 45 : if (it->second.size() != 1U) {
139 : 16 : result.ambiguous = true;
140 : 16 : return result;
141 : : }
142 : 29 : result.definition = std::shared_ptr<const DecodeFrame>(storage, &storage->frames[it->second[0]]);
143 : 29 : return result;
144 : 0 : }
145 : :
146 : 53 : std::shared_ptr<const DecodeSignal> DecodeDatabase::FindSignal(const SignalKey &key) const {
147 [ + - ]: 53 : const std::shared_ptr<const DecodeFrame> frame = FindFrame(key.frame);
148 [ + + ]: 53 : if (!frame)
149 : 10 : return nullptr;
150 [ + + ]: 53 : for (const DecodeSignal &signal: frame->signals) {
151 [ + + ]: 48 : if (signal.name == key.name)
152 : 38 : return std::shared_ptr<const DecodeSignal>(frame, &signal);
153 : : }
154 : 5 : return nullptr;
155 : 53 : }
|