use hid_types::encoding::{Size, TagTypeSize, TypeBits};
use hid_types::hid::{CollectionType, InputFlags, IoFlags, OutputFeatureFlags, Unit};
use hid_types::id::tag::{GlobalItem, LocalItem, MainItem};
use hid_types::id::usage::UsagePage;
use hid_types::item::usage::{ExtendedUsage, Usage};
use hid_types::item::{Global, Item, Local, LongItem, Main};
use tinyvec::TinyVec;
#[derive(Default, Debug, Clone)]
pub struct DecoderContext {
usage_page: Option<UsagePage>,
}
pub struct DecodedItem {
pub item: Item,
pub bytes: TinyVec<[u8; 8]>,
}
impl DecodedItem {
fn yes<T, E>(item: Item, iter: Recorder<T>) -> Result<Self, E> {
Ok(Self {
item,
bytes: iter.take_bytes(),
})
}
}
pub fn decode_one<Iter>(
iter: Iter,
context: &mut DecoderContext,
) -> Option<Result<DecodedItem, LengthError>>
where
Iter: Iterator<Item = u8>,
{
let mut iter = Recorder::new(iter);
let Some(tag) = iter.next() else {
return None;
};
Some(decode_one_inner(iter, context, tag))
}
fn decode_one_inner<I>(
mut iter: Recorder<I>,
context: &mut DecoderContext,
tag: u8,
) -> Result<DecodedItem, LengthError>
where
I: Iterator<Item = u8>,
{
let tag = TagTypeSize::from_bits(tag);
let more: TinyVec<[u8; 8]> = match tag.size() {
Size::Short(size_bytes) => {
let iter_remaining = (&mut iter).take(size_bytes);
iter_remaining.collect()
}
Size::Long => {
let (Some(size), Some(tag)) = (iter.next(), iter.next()) else {
return Err(LengthError::Truncated);
};
let iter_payload = (&mut iter).take(usize::from(size));
let data = iter_payload.collect::<Vec<_>>();
let item = Item::Long(LongItem { tag, data });
return DecodedItem::yes(item, iter);
}
};
match tag.ty() {
TypeBits::Main => {
let item = MainItem::from(tag.tag());
let decoded = match item {
MainItem::Input => {
let data = slice_to_u32(&more)?;
let flags = InputFlags(IoFlags::from(data));
Main::Input(flags)
}
MainItem::Output => {
let data = slice_to_u32(&more)?;
let flags = OutputFeatureFlags(IoFlags::from(data));
Main::Output(flags)
}
MainItem::Feature => {
let data = slice_to_u32(&more)?;
let flags = OutputFeatureFlags(IoFlags::from(data));
Main::Feature(flags)
}
MainItem::Collection => {
assert_eq!(more.len(), 1);
let coll_type = CollectionType::from_integer(more[0]);
Main::Collection(coll_type)
}
MainItem::EndCollection => {
assert!(more.is_empty());
Main::EndCollection
}
MainItem::Reserved => Main::Reserved(tag.tag()),
};
DecodedItem::yes(Item::Main(decoded), iter)
}
TypeBits::Global => {
let item = GlobalItem::from(tag.tag());
let decoded = match item {
GlobalItem::UsagePage => {
let page_number = slice_to_u16(&more)?;
let usage_page = UsagePage::from_integer(page_number);
context.usage_page = Some(usage_page);
Global::UsagePage(usage_page)
}
GlobalItem::LogicalMinimum => {
let num = slice_to_i32(&more)?;
Global::LogicalMinimum(num)
}
GlobalItem::LogicalMaximum => {
let num = slice_to_i32(&more)?;
Global::LogicalMaximum(num)
}
GlobalItem::PhysicalMinimum => {
let num = slice_to_i32(&more)?;
Global::PhysicalMinimum(num)
}
GlobalItem::PhysicalMaximum => {
let num = slice_to_i32(&more)?;
Global::PhysicalMaximum(num)
}
GlobalItem::UnitExponent => {
let num = slice_to_i32(&more)?;
Global::UnitExponent(num)
}
GlobalItem::ReportSize => {
let num = slice_to_u32(&more)?;
Global::ReportSize(num)
}
GlobalItem::ReportId => {
let num = slice_to_u32(&more)?;
Global::ReportId(num)
}
GlobalItem::ReportCount => {
let num = slice_to_u32(&more)?;
Global::ReportCount(num)
}
GlobalItem::Unit => {
let num = slice_to_u32(&more)?;
let unit = Unit::from_integer(num);
Global::Unit(unit)
}
GlobalItem::Push => Global::Push,
GlobalItem::Pop => Global::Pop,
GlobalItem::Reserved => Global::Reserved(tag.tag()),
};
DecodedItem::yes(Item::Global(decoded), iter)
}
TypeBits::Local => {
let item = LocalItem::from(tag.tag());
let decoded = match item {
LocalItem::Usage => {
match slice_to_u16(&more) {
Ok(page_id) => match context.usage_page {
Some(page) => Local::Usage(Usage::new(page, page_id)),
None => Local::Usage(Usage::without_page(page_id)),
},
Err(LengthError::DataTooBig) => {
let value = slice_to_u32(&more)?;
Local::ExtendedUsage(ExtendedUsage::from_u32(value))
}
Err(e) => return Err(e),
}
}
LocalItem::UsageMinimum => {
let num = slice_to_u32(&more)?;
Local::UsageMinimum(num)
}
LocalItem::UsageMaximum => {
let num = slice_to_u32(&more)?;
Local::UsageMaximum(num)
}
LocalItem::DesignatorIndex => {
let num = slice_to_u32(&more)?;
Local::DesignatorIndex(num)
}
LocalItem::DesignatorMinimum => {
let num = slice_to_u32(&more)?;
Local::DesignatorMinimum(num)
}
LocalItem::DesignatorMaximum => {
let num = slice_to_u32(&more)?;
Local::DesignatorMaximum(num)
}
LocalItem::StringIndex => {
let num = slice_to_u32(&more)?;
Local::StringIndex(num)
}
LocalItem::StringMinimum => {
let num = slice_to_u32(&more)?;
Local::StringMinimum(num)
}
LocalItem::StringMaximum => {
let num = slice_to_u32(&more)?;
Local::StringMaximum(num)
}
LocalItem::Delimiter => {
let num = slice_to_u8(&more)?;
let param = match num {
0 => false,
1 => true,
_ => return Err(LengthError::DataTooBig),
};
Local::Delimiter(param)
}
LocalItem::Reserved => Local::Reserved(tag.tag()),
};
DecodedItem::yes(Item::Local(decoded), iter)
}
TypeBits::Reserved => {
let decoded = Item::Reserved(tag.ty().into_bits());
DecodedItem::yes(decoded, iter)
}
}
}
struct Recorder<Iter> {
inner: Iter,
bytes: TinyVec<[u8; 8]>,
}
impl<Iter> Recorder<Iter> {
fn new(iter: Iter) -> Self {
Self {
inner: iter,
bytes: TinyVec::new(),
}
}
fn take_bytes(self) -> TinyVec<[u8; 8]> {
self.bytes
}
}
impl<Iter> Iterator for Recorder<Iter>
where
Iter: Iterator<Item = u8>,
{
type Item = u8;
fn next(&mut self) -> Option<Self::Item> {
self.inner.next().inspect(|b| {
self.bytes.push(*b);
})
}
}
#[derive(Clone, Debug, thiserror::Error)]
pub enum LengthError {
#[error("item data too big")]
DataTooBig,
#[error("missing item data")]
ZeroLength,
#[error("item data truncated")]
Truncated,
}
fn slice_to_u8(slice: &[u8]) -> Result<u8, LengthError> {
match slice.len() {
0 => Err(LengthError::ZeroLength),
1 => Ok(slice[0]),
_ => Err(LengthError::DataTooBig),
}
}
fn slice_to_u16(slice: &[u8]) -> Result<u16, LengthError> {
match slice.len() {
0 => Err(LengthError::ZeroLength),
1 => Ok(slice[0] as u16),
2 => {
let ar: &[u8; 2] = slice.as_array().unwrap();
Ok(u16::from_le_bytes(*ar))
}
_ => Err(LengthError::DataTooBig),
}
}
fn slice_to_u32(slice: &[u8]) -> Result<u32, LengthError> {
match slice.len() {
0 => Err(LengthError::ZeroLength),
1 => Ok(slice[0] as u32),
2 => {
let ar: &[u8; 2] = slice.as_array().unwrap();
let value = u16::from_le_bytes(*ar);
Ok(value as u32)
}
3 => Err(LengthError::Truncated),
4 => {
let ar: &[u8; 4] = slice.as_array().unwrap();
Ok(u32::from_le_bytes(*ar))
}
_ => Err(LengthError::DataTooBig),
}
}
fn slice_to_i32(slice: &[u8]) -> Result<i32, LengthError> {
match slice.len() {
0 => Err(LengthError::ZeroLength),
1 => Ok(slice[0] as i8 as i32),
2 => {
let ar: &[u8; 2] = slice.as_array().unwrap();
let value = i16::from_le_bytes(*ar);
Ok(value as i32)
}
3 => Err(LengthError::Truncated),
4 => {
let ar: &[u8; 4] = slice.as_array().unwrap();
Ok(i32::from_le_bytes(*ar))
}
_ => Err(LengthError::DataTooBig),
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn _error_traits() {
use std::error::Error;
use std::fmt::Display;
let _err: Box<dyn Error> = Box::new(LengthError::DataTooBig);
let _err: Box<dyn Display> = Box::new(LengthError::DataTooBig);
assert_eq!(format!("{}", LengthError::Truncated), "item data truncated");
}
#[test]
fn test_integer_decoding() {
assert_eq!(slice_to_u8(&[0xFF]).unwrap(), 0xFF);
slice_to_u8(&[]).unwrap_err();
slice_to_u8(&[0xFF, 0xFF]).unwrap_err();
assert_eq!(slice_to_u16(&[0xFF]).unwrap(), 0xFF);
assert_eq!(slice_to_u16(&[0xFF, 0xFF]).unwrap(), 0xFFFF);
assert_eq!(slice_to_u16(&[1, 2]).unwrap(), 0x0201);
slice_to_u16(&[]).unwrap_err();
slice_to_u16(&[0xFF; 3]).unwrap_err();
slice_to_u16(&[0xFF; 4]).unwrap_err();
assert_eq!(slice_to_u32(&[0xFF]).unwrap(), 0xFF);
assert_eq!(slice_to_u32(&[0xFF, 0xFF]).unwrap(), 0xFFFF);
assert_eq!(slice_to_u32(&[1, 2]).unwrap(), 0x0201);
assert_eq!(slice_to_u32(&[0xFF; 4]).unwrap(), 0xFFFFFFFF);
assert_eq!(slice_to_u32(&[1, 2, 3, 4]).unwrap(), 0x04030201);
slice_to_u32(&[]).unwrap_err();
slice_to_u32(&[0xFF; 3]).unwrap_err();
slice_to_u32(&[0xFF; 5]).unwrap_err();
assert_eq!(slice_to_i32(&[0]).unwrap(), 0);
assert_eq!(slice_to_i32(&[0, 0]).unwrap(), 0);
assert_eq!(slice_to_i32(&[1, 2]).unwrap(), 0x0201);
assert_eq!(slice_to_i32(&[0; 4]).unwrap(), 0);
assert_eq!(slice_to_i32(&[1, 2, 3, 4]).unwrap(), 0x04030201);
assert_eq!(slice_to_i32(&[0xFF]).unwrap(), -1);
assert_eq!(slice_to_i32(&[0xFF, 0xFF]).unwrap(), -1);
assert_eq!(slice_to_i32(&[0xFF; 4]).unwrap(), -1);
slice_to_i32(&[]).unwrap_err();
slice_to_i32(&[0xFF; 3]).unwrap_err();
slice_to_i32(&[0xFF; 5]).unwrap_err();
}
}