use crate::compat::{BTreeMap, Name, Vec};
use crate::{ExtendedMultiplexing, MAX_EXTENDED_MULTIPLEXING};
const MAX_INDEX_ENTRIES: usize = MAX_EXTENDED_MULTIPLEXING;
const MAX_ENTRIES_PER_SIGNAL: usize = 16;
type IndexKey = (u32, Name);
type IndexValue = Vec<usize, MAX_ENTRIES_PER_SIGNAL>;
type IndexMap = BTreeMap<IndexKey, IndexValue, MAX_INDEX_ENTRIES>;
#[derive(Debug, Clone, PartialEq)]
pub struct ExtMuxIndex {
index: Option<IndexMap>,
}
impl ExtMuxIndex {
#[cfg(feature = "alloc")]
pub fn build(entries: &[ExtendedMultiplexing]) -> Self {
if entries.is_empty() {
return Self { index: None };
}
let mut pairs: Vec<(IndexKey, usize), { MAX_INDEX_ENTRIES }> = Vec::new();
for (i, entry) in entries.iter().enumerate() {
let signal_name: Name = match Name::try_from(entry.signal_name()) {
Ok(name) => name,
Err(_) => continue,
};
let key = (entry.message_id(), signal_name);
let _ = pairs.push((key, i));
}
pairs.as_mut_slice().sort_by(|a, b| a.0.cmp(&b.0));
let mut index = IndexMap::new();
let mut i = 0;
while i < pairs.len() {
let key = pairs[i].0.clone();
let mut indices = IndexValue::new();
while i < pairs.len() && pairs[i].0 == key {
let _ = indices.push(pairs[i].1);
i += 1;
}
let _ = index.insert(key, indices);
}
Self { index: Some(index) }
}
#[cfg(not(feature = "alloc"))]
pub fn build(entries: &[ExtendedMultiplexing]) -> Self {
if entries.is_empty() {
return Self { index: None };
}
let mut index = IndexMap::new();
for (i, entry) in entries.iter().enumerate() {
let signal_name: Name = match Name::try_from(entry.signal_name()) {
Ok(name) => name,
Err(_) => continue,
};
let key = (entry.message_id(), signal_name.clone());
if let Some(indices) = index.get(&key) {
let mut new_indices = indices.clone();
let _ = new_indices.push(i);
let _ = index.insert(key, new_indices);
} else {
let mut indices = IndexValue::new();
let _ = indices.push(i);
let _ = index.insert(key, indices);
}
}
Self { index: Some(index) }
}
pub fn empty() -> Self {
Self { index: None }
}
#[inline]
pub fn get(&self, message_id: u32, signal_name: &str) -> Option<&[usize]> {
let index = self.index.as_ref()?;
let signal_name_owned: Name = Name::try_from(signal_name).ok()?;
let key = (message_id, signal_name_owned);
index.get(&key).map(|v| v.as_slice())
}
}
impl Default for ExtMuxIndex {
fn default() -> Self {
Self::empty()
}
}
#[cfg(all(test, feature = "alloc"))]
mod tests {
extern crate alloc;
use alloc::vec;
use super::*;
use crate::ExtendedMultiplexing;
fn make_ext_mux(
msg_id: u32,
signal: &str,
switch: &str,
ranges: &[(u64, u64)],
) -> ExtendedMultiplexing {
let signal_name = Name::try_from(signal).unwrap();
let switch_name = Name::try_from(switch).unwrap();
let mut value_ranges = Vec::new();
for &r in ranges {
let _ = value_ranges.push(r);
}
ExtendedMultiplexing::new(msg_id, signal_name, switch_name, value_ranges)
}
#[test]
fn test_empty_index() {
let index = ExtMuxIndex::build(&[]);
assert!(index.get(100, "Signal").is_none());
}
#[test]
fn test_single_entry() {
let entries = vec![make_ext_mux(100, "Signal_A", "Mux1", &[(0, 10)])];
let index = ExtMuxIndex::build(&entries);
let result = index.get(100, "Signal_A");
assert!(result.is_some());
assert_eq!(result.unwrap(), &[0]);
assert!(index.get(100, "Signal_B").is_none());
assert!(index.get(200, "Signal_A").is_none());
}
#[test]
fn test_multiple_entries_same_signal() {
let entries = vec![
make_ext_mux(100, "Signal_A", "Mux1", &[(0, 10)]),
make_ext_mux(100, "Signal_A", "Mux2", &[(5, 15)]),
];
let index = ExtMuxIndex::build(&entries);
let result = index.get(100, "Signal_A");
assert!(result.is_some());
let indices = result.unwrap();
assert_eq!(indices.len(), 2);
assert!(indices.contains(&0));
assert!(indices.contains(&1));
}
#[test]
fn test_multiple_signals() {
let entries = vec![
make_ext_mux(100, "Signal_A", "Mux1", &[(0, 10)]),
make_ext_mux(100, "Signal_B", "Mux1", &[(5, 15)]),
make_ext_mux(200, "Signal_A", "Mux1", &[(0, 5)]),
];
let index = ExtMuxIndex::build(&entries);
assert_eq!(index.get(100, "Signal_A").unwrap(), &[0]);
assert_eq!(index.get(100, "Signal_B").unwrap(), &[1]);
assert_eq!(index.get(200, "Signal_A").unwrap(), &[2]);
assert!(index.get(200, "Signal_B").is_none());
}
}