use hid_types::encoding::{Size, TagTypeSize, TypeBits};
use hid_types::id::{
CollectionType, GlobalItem, InputFlags, IoFlags, LocalItem, MainItem, OutputFeatureFlags, Unit,
UnitExponent, UsagePage,
};
use hid_types::item::usage::{ExtendedUsage, Usage};
use hid_types::item::{Global, Item, Local, LongItem, Main};
use tinyvec::{ArrayVec, TinyVec};
#[derive(Default, Debug, Clone)]
pub struct DecoderContext {
usage_page: Option<UsagePage>,
pub(crate) reject_zero_length_values: bool,
}
pub struct DecodedItem {
pub item: Item,
pub bytes: TinyVec<[u8; 4]>,
}
impl DecodedItem {
fn new<T>(item: Item, recorder: Recorder<T>) -> Self {
Self {
item,
bytes: recorder.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 item_data = match tag.size() {
Size::Short(size_bytes) => {
ItemData::new(size_bytes, &mut iter, context.reject_zero_length_values)?
}
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<_>>();
if data.len() != size as usize {
return Err(LengthError::Truncated);
}
let item = Item::Long(LongItem { tag, data });
return Ok(DecodedItem::new(item, iter));
}
};
match tag.ty() {
TypeBits::Main => {
let item = MainItem::from(tag.tag());
let decoded = match item {
MainItem::Input => {
let data = item_data.to_u32()?;
let flags = InputFlags(IoFlags::from(data));
Main::Input(flags)
}
MainItem::Output => {
let data = item_data.to_u32()?;
let flags = OutputFeatureFlags(IoFlags::from(data));
Main::Output(flags)
}
MainItem::Feature => {
let data = item_data.to_u32()?;
let flags = OutputFeatureFlags(IoFlags::from(data));
Main::Feature(flags)
}
MainItem::Collection => {
let data = item_data.to_u8()?;
let coll_type = CollectionType::from_integer(data);
Main::Collection(coll_type)
}
MainItem::EndCollection => {
if !item_data.is_empty() {
return Err(LengthError::DataTooBig);
}
Main::EndCollection
}
MainItem::Reserved => {
let data = item_data.to_option_i32()?;
Main::Reserved(tag.tag(), data)
}
};
Ok(DecodedItem::new(Item::Main(decoded), iter))
}
TypeBits::Global => {
let item = GlobalItem::from(tag.tag());
let decoded = match item {
GlobalItem::UsagePage => {
let page_number = item_data.to_u16()?;
let usage_page = UsagePage::from_integer(page_number);
context.usage_page = Some(usage_page);
Global::UsagePage(usage_page)
}
GlobalItem::LogicalMinimum => {
let num = item_data.to_i32()?;
Global::LogicalMinimum(num)
}
GlobalItem::LogicalMaximum => {
let num = item_data.to_i32()?;
Global::LogicalMaximum(num)
}
GlobalItem::PhysicalMinimum => {
let num = item_data.to_i32()?;
Global::PhysicalMinimum(num)
}
GlobalItem::PhysicalMaximum => {
let num = item_data.to_i32()?;
Global::PhysicalMaximum(num)
}
GlobalItem::UnitExponent => {
let num = item_data.to_i32()?;
Global::UnitExponent(UnitExponent(num))
}
GlobalItem::ReportSize => {
let num = item_data.to_u32()?;
Global::ReportSize(num)
}
GlobalItem::ReportId => {
let num = item_data.to_u8()?;
Global::ReportId(num)
}
GlobalItem::ReportCount => {
let num = item_data.to_u32()?;
Global::ReportCount(num)
}
GlobalItem::Unit => {
let num = item_data.to_u32()?;
let unit = Unit::from_integer(num);
Global::Unit(unit)
}
GlobalItem::Push => {
if !item_data.is_empty() {
return Err(LengthError::DataTooBig);
}
Global::Push
}
GlobalItem::Pop => {
if !item_data.is_empty() {
return Err(LengthError::DataTooBig);
}
Global::Pop
}
GlobalItem::Reserved => {
let data = item_data.to_option_i32()?;
Global::Reserved(tag.tag(), data)
}
};
Ok(DecodedItem::new(Item::Global(decoded), iter))
}
TypeBits::Local => {
let item = LocalItem::from(tag.tag());
let decoded = match item {
LocalItem::Usage => {
if item_data.len() == 4 {
let value = item_data.to_u32()?;
Local::ExtendedUsage(ExtendedUsage::from_u32(value))
} else {
let usage = item_data.to_u16()?;
match context.usage_page {
Some(page) => Local::Usage(Usage::new(page, usage)),
None => Local::Usage(Usage::without_page(usage)),
}
}
}
LocalItem::UsageMinimum => {
let num = item_data.to_u32()?;
Local::UsageMinimum(num)
}
LocalItem::UsageMaximum => {
let num = item_data.to_u32()?;
Local::UsageMaximum(num)
}
LocalItem::DesignatorIndex => {
let num = item_data.to_u32()?;
Local::DesignatorIndex(num)
}
LocalItem::DesignatorMinimum => {
let num = item_data.to_u32()?;
Local::DesignatorMinimum(num)
}
LocalItem::DesignatorMaximum => {
let num = item_data.to_u32()?;
Local::DesignatorMaximum(num)
}
LocalItem::StringIndex => {
let num = item_data.to_u32()?;
Local::StringIndex(num)
}
LocalItem::StringMinimum => {
let num = item_data.to_u32()?;
Local::StringMinimum(num)
}
LocalItem::StringMaximum => {
let num = item_data.to_u32()?;
Local::StringMaximum(num)
}
LocalItem::Delimiter => {
let num = item_data.to_u8()?;
let param = match num {
0 => false,
1 => true,
_ => return Err(LengthError::DataTooBig),
};
Local::Delimiter(param)
}
LocalItem::Reserved => {
let data = item_data.to_option_i32()?;
Local::Reserved(tag.tag(), data)
}
};
Ok(DecodedItem::new(Item::Local(decoded), iter))
}
TypeBits::Reserved => {
let data = item_data.to_option_i32()?;
let decoded = Item::Reserved(tag.tag(), data);
Ok(DecodedItem::new(decoded, iter))
}
}
}
struct Recorder<Iter> {
inner: Iter,
bytes: TinyVec<[u8; 4]>,
}
impl<Iter> Recorder<Iter> {
fn new(iter: Iter) -> Self {
Self {
inner: iter,
bytes: TinyVec::new(),
}
}
fn take_bytes(self) -> TinyVec<[u8; 4]> {
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, PartialEq, thiserror::Error)]
pub enum LengthError {
#[error("item data too big")]
DataTooBig,
#[error("missing item data")]
ZeroLength,
#[error("item data truncated")]
Truncated,
}
struct ItemData {
data: ArrayVec<[u8; 4]>,
reject_zero_length: bool,
}
impl ItemData {
fn new<I>(size: usize, iter: &mut I, reject_zero_length: bool) -> Result<Self, LengthError>
where
I: Iterator<Item = u8>,
{
if size > 4 {
panic!("ItemData::new can't be called with size > 4");
}
let mut data = ArrayVec::default();
for _ in 0..size {
let byte = iter.next().ok_or(LengthError::Truncated)?;
data.push(byte);
}
Ok(Self {
data,
reject_zero_length,
})
}
fn is_empty(&self) -> bool {
self.data.is_empty()
}
fn len(&self) -> usize {
self.data.len()
}
fn handle_zero<T: Default>(&self) -> Result<T, LengthError> {
if self.reject_zero_length {
Err(LengthError::ZeroLength)
} else {
Ok(T::default())
}
}
fn to_u8(&self) -> Result<u8, LengthError> {
match self.data.len() {
0 => self.handle_zero(),
1 => Ok(self.data[0]),
_ => Err(LengthError::DataTooBig),
}
}
fn to_u16(&self) -> Result<u16, LengthError> {
match self.data.len() {
0 => self.handle_zero(),
1 => Ok(self.data[0] as u16),
2 => {
let ar: &[u8; 2] = self.data.as_array().unwrap();
Ok(u16::from_le_bytes(*ar))
}
_ => Err(LengthError::DataTooBig),
}
}
fn to_u32(&self) -> Result<u32, LengthError> {
match self.data.len() {
0 => self.handle_zero(),
1 => Ok(self.data[0] as u32),
2 => {
let ar: &[u8; 2] = self.data.as_array().unwrap();
let value = u16::from_le_bytes(*ar);
Ok(value as u32)
}
3 => Err(LengthError::Truncated),
4 => {
let ar: &[u8; 4] = self.data.as_array().unwrap();
Ok(u32::from_le_bytes(*ar))
}
_ => Err(LengthError::DataTooBig),
}
}
fn to_i32(&self) -> Result<i32, LengthError> {
match self.data.len() {
0 => self.handle_zero(),
1 => Ok(self.data[0] as i8 as i32),
2 => {
let ar: &[u8; 2] = self.data.as_array().unwrap();
let value = i16::from_le_bytes(*ar);
Ok(value as i32)
}
3 => Err(LengthError::Truncated),
4 => {
let ar: &[u8; 4] = self.data.as_array().unwrap();
Ok(i32::from_le_bytes(*ar))
}
_ => Err(LengthError::DataTooBig),
}
}
fn to_option_i32(&self) -> Result<Option<i32>, LengthError> {
if self.is_empty() {
Ok(None)
} else {
let value = self.to_i32()?;
Ok(Some(value))
}
}
}
#[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() {
#[track_caller]
fn idata(slice: &[u8]) -> ItemData {
ItemData::new(slice.len(), &mut slice.iter().copied(), true)
.expect("malformed item data")
}
assert_eq!(idata(&[0xFF]).to_u8().unwrap(), 0xFF);
idata(&[]).to_u8().unwrap_err();
idata(&[0xFF, 0xFF]).to_u8().unwrap_err();
#[track_caller]
fn test_u16(slice: &[u8], expected: Result<u16, LengthError>) {
assert_eq!(idata(slice).to_u16(), expected);
}
test_u16(&[0xFF], Ok(0xFF));
test_u16(&[0xFF, 0xFF], Ok(0xFFFF));
test_u16(&[1, 2], Ok(0x0201));
test_u16(&[], Err(LengthError::ZeroLength));
test_u16(&[0xFF; 3], Err(LengthError::DataTooBig));
test_u16(&[0xFF; 4], Err(LengthError::DataTooBig));
#[track_caller]
fn test_u32(slice: &[u8], expected: Result<u32, LengthError>) {
assert_eq!(idata(slice).to_u32(), expected);
}
test_u32(&[0xFF], Ok(0xFF));
test_u32(&[0xFF, 0xFF], Ok(0xFFFF));
test_u32(&[1, 2], Ok(0x0201));
test_u32(&[0xFF; 4], Ok(0xFFFFFFFF));
test_u32(&[1, 2, 3, 4], Ok(0x04030201));
test_u32(&[], Err(LengthError::ZeroLength));
test_u32(&[0xFF; 3], Err(LengthError::Truncated));
#[track_caller]
fn test_i32(slice: &[u8], expected: Result<i32, LengthError>) {
assert_eq!(idata(slice).to_i32(), expected);
}
test_i32(&[0], Ok(0));
test_i32(&[0, 0], Ok(0));
test_i32(&[0, 0, 0, 0], Ok(0));
test_i32(&[1, 2], Ok(0x0201));
test_i32(&[0; 4], Ok(0));
test_i32(&[1, 2, 3, 4], Ok(0x04030201));
test_i32(&[0xFF], Ok(-1));
test_i32(&[0xFF, 0xFF], Ok(-1));
test_i32(&[0xFF; 4], Ok(-1));
test_i32(&[], Err(LengthError::ZeroLength));
test_i32(&[0xFF; 3], Err(LengthError::Truncated));
}
}