102 lines
4.9 KiB
C++
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
|