#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ReportItemType {
Main,
Global,
Local,
Reserved,
}
#[derive(Debug, PartialEq, Eq)]
pub struct ReportItem<'a> {
pub item_type: ReportItemType,
pub tag: u8,
pub data: &'a [u8],
}
pub struct DescriptorItemTokenizer<'a> {
descriptor: &'a [u8],
position: usize,
}
impl<'a> DescriptorItemTokenizer<'a> {
pub fn new(descriptor: &'a [u8]) -> Self {
DescriptorItemTokenizer { descriptor, position: 0 }
}
}
impl<'a> Iterator for DescriptorItemTokenizer<'a> {
type Item = ReportItem<'a>;
fn next(&mut self) -> Option<Self::Item> {
let item_header = self.descriptor.get(self.position)?;
let mut size = item_header & 0x3;
let item_type = match (item_header & 0xC) >> 2 {
0 => ReportItemType::Main,
1 => ReportItemType::Global,
2 => ReportItemType::Local,
3 => ReportItemType::Reserved,
_ => unreachable!(),
};
let mut tag = (item_header & 0xF0) >> 4;
if size == 3 {
size = 4;
}
self.position += 1;
if (size == 2) && (tag == 0xF) && (item_type) == ReportItemType::Reserved {
size = *self.descriptor.get(self.position)?;
self.position += 1;
tag = *self.descriptor.get(self.position)?;
self.position += 1;
}
let data = self.descriptor.get(self.position..self.position + size as usize)?;
self.position += size as usize;
Some(ReportItem { item_type, tag, data })
}
}
#[cfg(test)]
mod tests {
use super::{DescriptorItemTokenizer, ReportItem, ReportItemType};
use alloc::vec::Vec;
static TEST_REPORT_DESCRIPTOR: &[u8] = &[
0x05, 0x0d, 0x09, 0x02, 0xa1, 0x01, 0x85, 0x01, 0x09, 0x20, 0xa1, 0x00, 0x09, 0x42, 0x09, 0x44, 0x09, 0x3c, 0x09, 0x45, 0x15, 0x00, 0x25, 0x01, 0x75, 0x01, 0x95, 0x04, 0x81, 0x02, 0x95, 0x01, 0x81, 0x03, 0x09, 0x32, 0x81, 0x02, 0x95, 0x02, 0x81, 0x03, 0x05, 0x01, 0x09, 0x30, 0x75, 0x10, 0x95, 0x01, 0xa4, 0x55, 0x0d, 0x65, 0x13, 0x35, 0x00, 0x46, 0x3a, 0x20, 0x26, 0xf8, 0x52, 0x81, 0x02, 0x09, 0x31, 0x46, 0x2c, 0x18, 0x26, 0x6c, 0x3e, 0x81, 0x02, 0xb4, 0x05, 0x0d, 0x09, 0x30, 0x26, 0xff, 0x00, 0x81, 0x02, 0x75, 0x08, 0x09, 0x3d, 0x15, 0x81, 0x25, 0x7f, 0x81, 0x02, 0x09, 0x3e, 0x15, 0x81, 0x25, 0x7f, 0x81, 0x02, 0xc0, 0xc0, ];
#[rustfmt::skip]
static EXPECTED_ITEMS: &[ReportItem] = &[
ReportItem {item_type: ReportItemType::Global, tag: 0x00, data: &[0x0d]}, ReportItem {item_type: ReportItemType::Local, tag: 0x00, data: &[0x02]}, ReportItem {item_type: ReportItemType::Main, tag: 0x0a, data: &[0x01]}, ReportItem {item_type: ReportItemType::Global, tag: 0x08, data: &[0x01]}, ReportItem {item_type: ReportItemType::Local, tag: 0x00, data: &[0x20]}, ReportItem {item_type: ReportItemType::Main, tag: 0x0a, data: &[0x00]}, ReportItem {item_type: ReportItemType::Local, tag: 0x00, data: &[0x42]}, ReportItem {item_type: ReportItemType::Local, tag: 0x00, data: &[0x44]}, ReportItem {item_type: ReportItemType::Local, tag: 0x00, data: &[0x3c]}, ReportItem {item_type: ReportItemType::Local, tag: 0x00, data: &[0x45]}, ReportItem {item_type: ReportItemType::Global, tag: 0x01, data: &[0x00]}, ReportItem {item_type: ReportItemType::Global, tag: 0x02, data: &[0x01]}, ReportItem {item_type: ReportItemType::Global, tag: 0x07, data: &[0x01]}, ReportItem {item_type: ReportItemType::Global, tag: 0x09, data: &[0x04]}, ReportItem {item_type: ReportItemType::Main, tag: 0x08, data: &[0x02]}, ReportItem {item_type: ReportItemType::Global, tag: 0x09, data: &[0x01]}, ReportItem {item_type: ReportItemType::Main, tag: 0x08, data: &[0x03]}, ReportItem {item_type: ReportItemType::Local, tag: 0x00, data: &[0x32]}, ReportItem {item_type: ReportItemType::Main, tag: 0x08, data: &[0x02]}, ReportItem {item_type: ReportItemType::Global, tag: 0x09, data: &[0x02]}, ReportItem {item_type: ReportItemType::Main, tag: 0x08, data: &[0x03]}, ReportItem {item_type: ReportItemType::Global, tag: 0x00, data: &[0x01]}, ReportItem {item_type: ReportItemType::Local, tag: 0x00, data: &[0x30]}, ReportItem {item_type: ReportItemType::Global, tag: 0x07, data: &[0x10]}, ReportItem {item_type: ReportItemType::Global, tag: 0x09, data: &[0x01]}, ReportItem {item_type: ReportItemType::Global, tag: 0x0a, data: &[]}, ReportItem {item_type: ReportItemType::Global, tag: 0x05, data: &[0x0d]}, ReportItem {item_type: ReportItemType::Global, tag: 0x06, data: &[0x13]}, ReportItem {item_type: ReportItemType::Global, tag: 0x03, data: &[0x00]}, ReportItem {item_type: ReportItemType::Global, tag: 0x04, data: &[0x3a, 0x20]}, ReportItem {item_type: ReportItemType::Global, tag: 0x02, data: &[0xf8, 0x52]}, ReportItem {item_type: ReportItemType::Main, tag: 0x08, data: &[0x02]}, ReportItem {item_type: ReportItemType::Local, tag: 0x00, data: &[0x31]}, ReportItem {item_type: ReportItemType::Global, tag: 0x04, data: &[0x2c, 0x18]}, ReportItem {item_type: ReportItemType::Global, tag: 0x02, data: &[0x6c, 0x3e]}, ReportItem {item_type: ReportItemType::Main, tag: 0x08, data: &[0x02]}, ReportItem {item_type: ReportItemType::Global, tag: 0x0b, data: &[]}, ReportItem {item_type: ReportItemType::Global, tag: 0x00, data: &[0x0d]}, ReportItem {item_type: ReportItemType::Local, tag: 0x00, data: &[0x30]}, ReportItem {item_type: ReportItemType::Global, tag: 0x02, data: &[0xff, 0x00]}, ReportItem {item_type: ReportItemType::Main, tag: 0x08, data: &[0x02]}, ReportItem {item_type: ReportItemType::Global, tag: 0x07, data: &[0x08]}, ReportItem {item_type: ReportItemType::Local, tag: 0x00, data: &[0x3d]}, ReportItem {item_type: ReportItemType::Global, tag: 0x01, data: &[0x81]}, ReportItem {item_type: ReportItemType::Global, tag: 0x02, data: &[0x7f]}, ReportItem {item_type: ReportItemType::Main, tag: 0x08, data: &[0x02]}, ReportItem {item_type: ReportItemType::Local, tag: 0x00, data: &[0x3e]}, ReportItem {item_type: ReportItemType::Global, tag: 0x01, data: &[0x81]}, ReportItem {item_type: ReportItemType::Global, tag: 0x02, data: &[0x7f]}, ReportItem {item_type: ReportItemType::Main, tag: 0x08, data: &[0x02]}, ReportItem {item_type: ReportItemType::Main, tag: 0x0c, data: &[]}, ReportItem {item_type: ReportItemType::Main, tag: 0x0c, data: &[]}, ];
#[test]
fn item_tokenizer_should_tokenize_items() {
let tokenizer = DescriptorItemTokenizer::new(TEST_REPORT_DESCRIPTOR);
let items: Vec<_> = tokenizer.collect();
assert_eq!(items.len(), EXPECTED_ITEMS.len(), "tokenizer did not produce the correct number of items");
for (index, (item, expected_item)) in items.iter().zip(EXPECTED_ITEMS.iter()).enumerate() {
assert_eq!(item, expected_item, "invalid tokenization of item at index {index:?}");
}
}
}