Files
dbc/core/multiplex_graph.h
T
2026-10-23 20:28:55 -04:00

102 lines
4.9 KiB
C++

// SPDX-License-Identifier: MIT
#ifndef MULTIPLEX_GRAPH_H
#define MULTIPLEX_GRAPH_H
#include "multiplexing.h"
#include <cstddef>
#include <stdexcept>
namespace dbc_detail {
/** Validation failure associated with a signal in declaration order. */
class MultiplexError : public std::invalid_argument {
public:
MultiplexError(std::size_t signalIndex, const std::string &message)
: std::invalid_argument(message), index(signalIndex) {}
std::size_t index;
};
/** Resolved parent links shared by parser validation and runtime decoding. */
class MultiplexGraph {
public:
template<class Signal>
explicit MultiplexGraph(const std::vector<Signal> &signals)
: parents(signals.size(), signals.size()), ranges(signals.size()) {
const std::size_t count = signals.size();
for (std::size_t index = 0U; index < count; ++index) {
const Multiplexing &mux = signals[index].multiplexing;
if (mux.role != MultiplexRole::Unconditional && mux.role != MultiplexRole::Selector &&
mux.role != MultiplexRole::Branch && mux.role != MultiplexRole::BranchSelector)
throw MultiplexError(index, "Invalid multiplexing role.");
if (mux.selector.empty() != mux.ranges.empty())
throw MultiplexError(index, "Selector name and ranges must be supplied together.");
if (mux.selector.empty() && !IsMultiplexBranch(mux.role))
continue;
std::size_t parent = count;
for (std::size_t candidate = 0U; candidate < count; ++candidate) {
const Multiplexing &other = signals[candidate].multiplexing;
const bool matches = !mux.selector.empty()
? signals[candidate].name == mux.selector
: other.role == MultiplexRole::Selector && other.selector.empty();
if (!matches)
continue;
if (parent != count)
throw MultiplexError(index, "Ambiguous multiplex selector; use SG_MUL_VAL_.");
parent = candidate;
}
if (parent == count)
throw MultiplexError(index, "Multiplexed branch has no selector.");
if (!IsMultiplexSelector(signals[parent].multiplexing.role))
throw MultiplexError(index, "Referenced signal is not a multiplex selector.");
parents[index] = parent;
ranges[index] = mux.selector.empty() ? std::vector<MultiplexRange>{{mux.value, mux.value}} : mux.ranges;
for (const MultiplexRange &range: ranges[index]) {
if (range.minimum > range.maximum)
throw MultiplexError(index, "Reversed multiplex range.");
if (signals[parent].length < 64U && range.maximum >= (std::uint64_t(1U) << signals[parent].length))
throw MultiplexError(index, "Branch value exceeds selector raw range.");
}
}
for (std::size_t index = 0U; index < count; ++index) {
std::size_t current = index;
for (std::size_t depth = 0U; current != count; ++depth) {
if (depth >= count)
throw MultiplexError(index, "Cyclic multiplex selector dependency.");
current = parents[current];
}
}
}
bool Matches(std::size_t index, std::uint64_t value) const {
for (const MultiplexRange &range: ranges[index]) {
if (range.minimum <= value && value <= range.maximum)
return true;
}
return false;
}
bool AreMutuallyExclusive(std::size_t first, std::size_t second) const {
const std::size_t count = parents.size();
for (std::size_t left = first; parents[left] != count; left = parents[left]) {
for (std::size_t right = second; parents[right] != count; right = parents[right]) {
if (parents[left] != parents[right])
continue;
bool intersects = false;
for (const MultiplexRange &a: ranges[left]) {
for (const MultiplexRange &b: ranges[right]) {
if (a.minimum <= b.maximum && b.minimum <= a.maximum)
intersects = true;
}
}
if (!intersects)
return true;
}
}
return false;
}
std::vector<std::size_t> parents;
std::vector<std::vector<MultiplexRange>> ranges;
};
}// namespace dbc_detail
#endif