// SPDX-License-Identifier: MIT // Copyright (c) 2026 Dim Himro #include "decode_database.h" #include "signal_layout.h" #include #include #include #include #include namespace { constexpr std::size_t MaximumPayloadBits = std::numeric_limits::max() * 8U; using SignalBits = std::bitset; void ValidateSignal(const DecodeSignal &signal, std::uint8_t dlc) { if (signal.byteOrder != ByteOrder::Intel && signal.byteOrder != ByteOrder::Motorola) throw std::invalid_argument("Invalid signal byte order: " + signal.name); if (signal.valueType != ValueType::Unsigned && signal.valueType != ValueType::Signed) throw std::invalid_argument("Invalid signal value type: " + signal.name); if (signal.multiplexing.role != MultiplexRole::Unconditional && signal.multiplexing.role != MultiplexRole::Selector && signal.multiplexing.role != MultiplexRole::Branch) throw std::invalid_argument("Invalid multiplexing role: " + signal.name); if (!IsValidSignalLayout(signal.startBit, signal.length, signal.byteOrder == ByteOrder::Intel, dlc)) throw std::invalid_argument("Invalid signal length or payload position: " + signal.name); if (!std::isfinite(signal.factor) || !std::isfinite(signal.offset) || !std::isfinite(signal.minimum) || !std::isfinite(signal.maximum) || signal.minimum > signal.maximum) throw std::invalid_argument("Invalid signal numeric parameters: " + signal.name); } SignalBits GetSignalBits(const DecodeSignal &signal) { SignalBits bits; const bool littleEndian = signal.byteOrder == ByteOrder::Intel; const std::uint64_t start = SignalStartCoordinate(signal.startBit, littleEndian); for (std::uint32_t index = 0U; index < signal.length; ++index) { const std::uint64_t coordinate = start + index; const std::uint64_t bit = littleEndian ? coordinate : coordinate / 8U * 8U + 7U - coordinate % 8U; bits.set(static_cast(bit)); } return bits; } bool AreMutuallyExclusive(const DecodeSignal &first, const DecodeSignal &second) { return first.multiplexing.role == MultiplexRole::Branch && second.multiplexing.role == MultiplexRole::Branch && first.multiplexing.value != second.multiplexing.value; } void ValidateFrame(const DecodeFrame &frame) { const std::uint32_t maximumId = frame.isExtended ? 0x1FFFFFFFU : 0x7FFU; if (frame.canId > maximumId) throw std::invalid_argument("Invalid normalized CAN ID: " + std::to_string(frame.canId)); std::unordered_set names; const DecodeSignal *selector = nullptr; for (const DecodeSignal &signal: frame.signals) { if (!names.insert(signal.name).second) throw std::invalid_argument("Duplicate runtime signal key: " + signal.name); ValidateSignal(signal, frame.dlc); if (signal.multiplexing.role == MultiplexRole::Selector) { if (selector != nullptr || signal.valueType != ValueType::Unsigned) throw std::invalid_argument("Multiplexing requires one unsigned selector per frame"); selector = &signal; } } std::vector layouts; layouts.reserve(frame.signals.size()); for (std::size_t index = 0U; index < frame.signals.size(); ++index) { const DecodeSignal &signal = frame.signals[index]; if (signal.multiplexing.role == MultiplexRole::Branch && (selector == nullptr || (selector->length < 64U && signal.multiplexing.value >= (std::uint64_t(1U) << selector->length)))) throw std::invalid_argument("Missing selector or branch value outside selector range: " + signal.name); const SignalBits bits = GetSignalBits(signal); for (std::size_t previous = 0U; previous < index; ++previous) { if (!AreMutuallyExclusive(signal, frame.signals[previous]) && (bits & layouts[previous]).any()) throw std::invalid_argument("Overlapping signals: " + signal.name); } layouts.push_back(bits); } } }// namespace DecodeDatabase::Storage::Storage(const std::vector &definitions) : frames(definitions) { // Validate the complete snapshot before creating its lookup index. for (const DecodeFrame &frame: frames) ValidateFrame(frame); frameIndexByKey.reserve(frames.size()); for (std::size_t index = 0U; index < frames.size(); ++index) { if (!frameIndexByKey.emplace(frames[index].GetKey(), index).second) throw std::invalid_argument("Duplicate runtime frame key"); } } DecodeDatabase::DecodeDatabase(const std::vector &frames) : storage(std::make_shared(frames)) { } const std::vector &DecodeDatabase::GetFrames() const { static const std::vector empty; return storage ? storage->frames : empty; } std::shared_ptr DecodeDatabase::FindFrame(const FrameKey &key) const { if (!storage) return nullptr; const std::unordered_map::const_iterator it = storage->frameIndexByKey.find(key); if (it == storage->frameIndexByKey.end()) return nullptr; return std::shared_ptr(storage, &storage->frames[it->second]); } std::shared_ptr DecodeDatabase::FindSignal(const SignalKey &key) const { const std::shared_ptr frame = FindFrame(key.frame); if (!frame) return nullptr; for (const DecodeSignal &signal: frame->signals) { if (signal.name == key.name) return std::shared_ptr(frame, &signal); } return nullptr; }