use alloc::vec::Vec;
const FLAG_COMM_ENABLE: u16 = 1 << 10;
const FLAG_READ_ENABLE: u16 = 1 << 11;
const FLAG_WRITE_ENABLE: u16 = 1 << 12;
const FLAG_READ_ON_INIT: u16 = 1 << 13;
const FLAG_TRANSMIT_ENABLE: u16 = 1 << 14;
const FLAG_UPDATE_ENABLE: u16 = 1 << 15;
const PRIORITY_SHIFT: u32 = 8;
const PRIORITY_MASK: u16 = 0x03;
const VALUE_TYPE_MASK: u16 = 0xFF;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct GroupObjectDescriptor {
pub(crate) raw: u16,
}
impl GroupObjectDescriptor {
pub const fn raw(self) -> u16 {
self.raw
}
pub const fn communication_enable(self) -> bool {
self.raw & FLAG_COMM_ENABLE != 0
}
pub const fn read_enable(self) -> bool {
self.raw & FLAG_READ_ENABLE != 0
}
pub const fn write_enable(self) -> bool {
self.raw & FLAG_WRITE_ENABLE != 0
}
pub const fn read_on_init(self) -> bool {
self.raw & FLAG_READ_ON_INIT != 0
}
pub const fn transmit_enable(self) -> bool {
self.raw & FLAG_TRANSMIT_ENABLE != 0
}
pub const fn update_enable(self) -> bool {
self.raw & FLAG_UPDATE_ENABLE != 0
}
pub const fn priority(self) -> u8 {
((self.raw >> PRIORITY_SHIFT) & PRIORITY_MASK) as u8
}
pub const fn value_type(self) -> u8 {
(self.raw & VALUE_TYPE_MASK) as u8
}
pub const fn value_size_bytes(self) -> usize {
let code = self.value_type();
match code {
0..=6 => 0,
7 => 1,
8..=10 | 21..=254 => (code - 6) as usize,
11 => 6,
12 => 8,
13 => 10,
14 => 14,
15 => 5,
16 => 7,
17 => 9,
18 => 11,
19 => 12,
20 => 13,
255 => 252,
}
}
pub const fn go_size(self) -> usize {
let s = self.value_size_bytes();
if s == 0 { 1 } else { s }
}
}
pub struct GroupObjectTable {
data: Vec<u8>,
}
impl Default for GroupObjectTable {
fn default() -> Self {
Self::new()
}
}
impl GroupObjectTable {
pub const fn new() -> Self {
Self { data: Vec::new() }
}
pub fn load(&mut self, data: &[u8]) {
self.data = data.to_vec();
}
pub fn entry_count(&self) -> u16 {
if self.data.len() < 2 {
return 0;
}
u16::from_be_bytes([self.data[0], self.data[1]])
}
pub fn get_descriptor(&self, asap: u16) -> Option<GroupObjectDescriptor> {
if asap == 0 || asap > self.entry_count() {
return None;
}
let offset = (asap as usize) * 2;
if offset + 2 > self.data.len() {
return None;
}
let raw = u16::from_be_bytes([self.data[offset], self.data[offset + 1]]);
Some(GroupObjectDescriptor { raw })
}
}
#[cfg(test)]
#[allow(clippy::unwrap_used)]
mod tests {
use super::*;
fn sample_table() -> GroupObjectTable {
let mut t = GroupObjectTable::new();
let go1: u16 = (1 << 15) | (1 << 14) | (1 << 11) | (1 << 10) | 1; let go2: u16 = (1 << 13) | (1 << 12) | (1 << 10) | 1; let mut data = Vec::new();
data.extend_from_slice(&2u16.to_be_bytes()); data.extend_from_slice(&go1.to_be_bytes());
data.extend_from_slice(&go2.to_be_bytes());
t.load(&data);
t
}
#[test]
fn entry_count() {
assert_eq!(sample_table().entry_count(), 2);
}
#[test]
fn get_descriptor() {
let t = sample_table();
let d1 = t.get_descriptor(1).unwrap();
assert!(d1.communication_enable());
assert!(d1.read_enable());
assert!(d1.transmit_enable());
assert!(d1.update_enable());
assert!(!d1.write_enable());
assert!(!d1.read_on_init());
assert_eq!(d1.value_type(), 1);
let d2 = t.get_descriptor(2).unwrap();
assert!(d2.communication_enable());
assert!(d2.write_enable());
assert!(d2.read_on_init());
assert!(!d2.read_enable());
assert!(!d2.transmit_enable());
assert!(!d2.update_enable());
}
#[test]
fn out_of_range() {
let t = sample_table();
assert!(t.get_descriptor(0).is_none());
assert!(t.get_descriptor(3).is_none());
}
#[test]
fn value_size_bytes_known_codes() {
let desc = |code: u8| GroupObjectDescriptor {
raw: u16::from(code),
};
assert_eq!(desc(0).value_size_bytes(), 0);
assert_eq!(desc(6).value_size_bytes(), 0);
assert_eq!(desc(7).value_size_bytes(), 1);
assert_eq!(desc(8).value_size_bytes(), 2);
assert_eq!(desc(9).value_size_bytes(), 3);
assert_eq!(desc(10).value_size_bytes(), 4);
assert_eq!(desc(11).value_size_bytes(), 6);
assert_eq!(desc(12).value_size_bytes(), 8);
assert_eq!(desc(13).value_size_bytes(), 10);
assert_eq!(desc(14).value_size_bytes(), 14);
assert_eq!(desc(15).value_size_bytes(), 5);
assert_eq!(desc(16).value_size_bytes(), 7);
assert_eq!(desc(17).value_size_bytes(), 9);
assert_eq!(desc(18).value_size_bytes(), 11);
assert_eq!(desc(19).value_size_bytes(), 12);
assert_eq!(desc(20).value_size_bytes(), 13);
assert_eq!(desc(21).value_size_bytes(), 15);
assert_eq!(desc(100).value_size_bytes(), 94);
assert_eq!(desc(255).value_size_bytes(), 252);
}
#[test]
fn go_size_minimum_one() {
let desc = |code: u8| GroupObjectDescriptor {
raw: u16::from(code),
};
assert_eq!(desc(0).go_size(), 1);
assert_eq!(desc(6).go_size(), 1);
assert_eq!(desc(7).go_size(), 1);
assert_eq!(desc(14).go_size(), 14);
}
}