LCOV - code coverage report
Current view: top level - core - decode_database.cpp (source / functions) Coverage Total Hit
Test: coverage.info Lines: 99.1 % 110 109
Test Date: 2026-10-20 13:34:58 Functions: 100.0 % 10 10
Branches: 80.2 % 182 146

             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 : }
        

Generated by: LCOV version 2.0-1