Skip to main content

hid_decode/
item.rs

1//! Decode a single item inside an HID report descriptor.
2
3use hid_types::encoding::{Size, TagTypeSize, TypeBits};
4use hid_types::id::{
5    CollectionType, GlobalItem, InputFlags, IoFlags, LocalItem, MainItem, OutputFeatureFlags, Unit,
6    UnitExponent, UsagePage,
7};
8use hid_types::item::usage::{ExtendedUsage, Usage};
9use hid_types::item::{Global, Item, Local, LongItem, Main};
10use tinyvec::{ArrayVec, TinyVec};
11
12/// Context that can affect the way the decoder interprets subsequent descriptor items.
13#[derive(Default, Debug, Clone)]
14pub struct DecoderContext {
15    /// The current usage page.
16    usage_page: Option<UsagePage>,
17    /// Whether or not zero-length payloads are permitted for items with values.
18    ///
19    /// The HID specification is vague about where this is permitted, or what it
20    /// means (unchanged/default/zero).
21    pub(crate) reject_zero_length_values: bool,
22}
23
24/// A successfully decoded descriptor item.
25pub struct DecodedItem {
26    /// The decoded descriptor item.
27    pub item: Item,
28    /// The bytes this item was decoded from.
29    pub bytes: TinyVec<[u8; 4]>,
30}
31
32impl DecodedItem {
33    /// Capture an item along with the bytes that were used to decode it.
34    fn new<T>(item: Item, recorder: Recorder<T>) -> Self {
35        Self {
36            item,
37            bytes: recorder.take_bytes(),
38        }
39    }
40}
41
42/// Decode one item from its encoded bytes.
43///
44/// This will consume one or more bytes from the input iterator.
45pub fn decode_one<Iter>(
46    iter: Iter,
47    context: &mut DecoderContext,
48) -> Option<Result<DecodedItem, LengthError>>
49where
50    Iter: Iterator<Item = u8>,
51{
52    let mut iter = Recorder::new(iter);
53
54    let Some(tag) = iter.next() else {
55        // End of input iterator
56        return None;
57    };
58    Some(decode_one_inner(iter, context, tag))
59}
60
61/// Decode one item from its tag and any data bytes that follow.
62///
63/// This will consume zero or more bytes from the input iterator.
64fn decode_one_inner<I>(
65    mut iter: Recorder<I>,
66    context: &mut DecoderContext,
67    tag: u8,
68) -> Result<DecodedItem, LengthError>
69where
70    I: Iterator<Item = u8>,
71{
72    let tag = TagTypeSize::from_bits(tag);
73
74    let item_data = match tag.size() {
75        Size::Short(size_bytes) => {
76            ItemData::new(size_bytes, &mut iter, context.reject_zero_length_values)?
77        }
78        Size::Long => {
79            let (Some(size), Some(tag)) = (iter.next(), iter.next()) else {
80                return Err(LengthError::Truncated);
81            };
82            let iter_payload = (&mut iter).take(usize::from(size));
83            let data = iter_payload.collect::<Vec<_>>();
84            if data.len() != size as usize {
85                return Err(LengthError::Truncated);
86            }
87            let item = Item::Long(LongItem { tag, data });
88            return Ok(DecodedItem::new(item, iter));
89        }
90    };
91
92    match tag.ty() {
93        TypeBits::Main => {
94            let item = MainItem::from(tag.tag());
95            let decoded = match item {
96                MainItem::Input => {
97                    let data = item_data.to_u32()?;
98                    let flags = InputFlags(IoFlags::from(data));
99                    Main::Input(flags)
100                }
101                MainItem::Output => {
102                    let data = item_data.to_u32()?;
103                    let flags = OutputFeatureFlags(IoFlags::from(data));
104                    Main::Output(flags)
105                }
106                MainItem::Feature => {
107                    let data = item_data.to_u32()?;
108                    let flags = OutputFeatureFlags(IoFlags::from(data));
109                    Main::Feature(flags)
110                }
111                MainItem::Collection => {
112                    let data = item_data.to_u8()?;
113                    let coll_type = CollectionType::from_integer(data);
114                    Main::Collection(coll_type)
115                }
116                MainItem::EndCollection => {
117                    if !item_data.is_empty() {
118                        return Err(LengthError::DataTooBig);
119                    }
120                    Main::EndCollection
121                }
122                MainItem::Reserved => {
123                    let data = item_data.to_option_i32()?;
124                    Main::Reserved(tag.tag(), data)
125                }
126            };
127            Ok(DecodedItem::new(Item::Main(decoded), iter))
128        }
129        TypeBits::Global => {
130            let item = GlobalItem::from(tag.tag());
131            let decoded = match item {
132                GlobalItem::UsagePage => {
133                    let page_number = item_data.to_u16()?;
134                    let usage_page = UsagePage::from_integer(page_number);
135                    context.usage_page = Some(usage_page);
136                    Global::UsagePage(usage_page)
137                }
138                GlobalItem::LogicalMinimum => {
139                    let num = item_data.to_i32()?;
140                    Global::LogicalMinimum(num)
141                }
142                GlobalItem::LogicalMaximum => {
143                    let num = item_data.to_i32()?;
144                    Global::LogicalMaximum(num)
145                }
146                GlobalItem::PhysicalMinimum => {
147                    let num = item_data.to_i32()?;
148                    Global::PhysicalMinimum(num)
149                }
150                GlobalItem::PhysicalMaximum => {
151                    let num = item_data.to_i32()?;
152                    Global::PhysicalMaximum(num)
153                }
154                GlobalItem::UnitExponent => {
155                    // There's some disagreement about the allowed size of the
156                    // unit exponent value; the UnitExponent struct has methods
157                    // to paper over the worst of the problem.
158                    let num = item_data.to_i32()?;
159                    Global::UnitExponent(UnitExponent(num))
160                }
161                GlobalItem::ReportSize => {
162                    let num = item_data.to_u32()?;
163                    Global::ReportSize(num)
164                }
165                GlobalItem::ReportId => {
166                    let num = item_data.to_u8()?;
167                    Global::ReportId(num)
168                }
169                GlobalItem::ReportCount => {
170                    let num = item_data.to_u32()?;
171                    Global::ReportCount(num)
172                }
173                GlobalItem::Unit => {
174                    let num = item_data.to_u32()?;
175                    let unit = Unit::from_integer(num);
176                    Global::Unit(unit)
177                }
178                GlobalItem::Push => {
179                    if !item_data.is_empty() {
180                        return Err(LengthError::DataTooBig);
181                    }
182                    Global::Push
183                }
184                GlobalItem::Pop => {
185                    if !item_data.is_empty() {
186                        return Err(LengthError::DataTooBig);
187                    }
188                    Global::Pop
189                }
190                GlobalItem::Reserved => {
191                    let data = item_data.to_option_i32()?;
192                    Global::Reserved(tag.tag(), data)
193                }
194            };
195            Ok(DecodedItem::new(Item::Global(decoded), iter))
196        }
197        TypeBits::Local => {
198            let item = LocalItem::from(tag.tag());
199            let decoded = match item {
200                LocalItem::Usage => {
201                    // If the payload is 1 or 2 bytes, we use the
202                    // existing page number. If the payload is 4 bytes, it
203                    // contains both the page number and page id.
204                    if item_data.len() == 4 {
205                        let value = item_data.to_u32()?;
206                        Local::ExtendedUsage(ExtendedUsage::from_u32(value))
207                    } else {
208                        let usage = item_data.to_u16()?;
209                        match context.usage_page {
210                            Some(page) => Local::Usage(Usage::new(page, usage)),
211                            None => Local::Usage(Usage::without_page(usage)),
212                        }
213                    }
214                }
215                LocalItem::UsageMinimum => {
216                    let num = item_data.to_u32()?;
217                    Local::UsageMinimum(num)
218                }
219                LocalItem::UsageMaximum => {
220                    let num = item_data.to_u32()?;
221                    Local::UsageMaximum(num)
222                }
223                LocalItem::DesignatorIndex => {
224                    let num = item_data.to_u32()?;
225                    Local::DesignatorIndex(num)
226                }
227                LocalItem::DesignatorMinimum => {
228                    let num = item_data.to_u32()?;
229                    Local::DesignatorMinimum(num)
230                }
231                LocalItem::DesignatorMaximum => {
232                    let num = item_data.to_u32()?;
233                    Local::DesignatorMaximum(num)
234                }
235                LocalItem::StringIndex => {
236                    let num = item_data.to_u32()?;
237                    Local::StringIndex(num)
238                }
239                LocalItem::StringMinimum => {
240                    let num = item_data.to_u32()?;
241                    Local::StringMinimum(num)
242                }
243                LocalItem::StringMaximum => {
244                    let num = item_data.to_u32()?;
245                    Local::StringMaximum(num)
246                }
247                LocalItem::Delimiter => {
248                    let num = item_data.to_u8()?;
249                    let param = match num {
250                        0 => false,
251                        1 => true,
252                        _ => return Err(LengthError::DataTooBig),
253                    };
254                    Local::Delimiter(param)
255                }
256                LocalItem::Reserved => {
257                    let data = item_data.to_option_i32()?;
258                    Local::Reserved(tag.tag(), data)
259                }
260            };
261            Ok(DecodedItem::new(Item::Local(decoded), iter))
262        }
263        TypeBits::Reserved => {
264            let data = item_data.to_option_i32()?;
265            let decoded = Item::Reserved(tag.tag(), data);
266            Ok(DecodedItem::new(decoded, iter))
267        }
268    }
269}
270
271/// An iterator that yields values from an inner iterator, but also collects everything that was yielded.
272struct Recorder<Iter> {
273    inner: Iter,
274    bytes: TinyVec<[u8; 4]>,
275}
276
277impl<Iter> Recorder<Iter> {
278    /// Create a new recording iterator.
279    fn new(iter: Iter) -> Self {
280        Self {
281            inner: iter,
282            bytes: TinyVec::new(),
283        }
284    }
285
286    /// Take the recorded bytes, consuming the iterator.
287    fn take_bytes(self) -> TinyVec<[u8; 4]> {
288        self.bytes
289    }
290}
291
292impl<Iter> Iterator for Recorder<Iter>
293where
294    Iter: Iterator<Item = u8>,
295{
296    type Item = u8;
297
298    fn next(&mut self) -> Option<Self::Item> {
299        self.inner.next().inspect(|b| {
300            self.bytes.push(*b);
301        })
302    }
303}
304
305/// An error that resulted from improper item encoding.
306#[derive(Clone, Debug, PartialEq, thiserror::Error)]
307pub enum LengthError {
308    /// The data length was too big for this item.
309    #[error("item data too big")]
310    DataTooBig,
311    /// A data length of zero is not allowed in this item.
312    #[error("missing item data")]
313    ZeroLength,
314    /// Ran out of bytes trying to decode this item.
315    #[error("item data truncated")]
316    Truncated,
317}
318
319/// The data payload from an item using the short encoding.
320struct ItemData {
321    /// Item data will always be 0 to 4 bytes (using the short encoding).
322    data: ArrayVec<[u8; 4]>,
323    /// Should zero-length data be interpreted as a value of 0?
324    ///
325    /// If false, trying to interpret as an integer will return `LengthError::ZeroLength`.
326    reject_zero_length: bool,
327}
328
329impl ItemData {
330    /// Collect item bytes from an iterator.
331    fn new<I>(size: usize, iter: &mut I, reject_zero_length: bool) -> Result<Self, LengthError>
332    where
333        I: Iterator<Item = u8>,
334    {
335        if size > 4 {
336            panic!("ItemData::new can't be called with size > 4");
337        }
338
339        let mut data = ArrayVec::default();
340        for _ in 0..size {
341            let byte = iter.next().ok_or(LengthError::Truncated)?;
342            data.push(byte);
343        }
344        Ok(Self {
345            data,
346            reject_zero_length,
347        })
348    }
349
350    fn is_empty(&self) -> bool {
351        self.data.is_empty()
352    }
353
354    fn len(&self) -> usize {
355        self.data.len()
356    }
357
358    fn handle_zero<T: Default>(&self) -> Result<T, LengthError> {
359        if self.reject_zero_length {
360            Err(LengthError::ZeroLength)
361        } else {
362            Ok(T::default())
363        }
364    }
365
366    fn to_u8(&self) -> Result<u8, LengthError> {
367        match self.data.len() {
368            0 => self.handle_zero(),
369            1 => Ok(self.data[0]),
370            _ => Err(LengthError::DataTooBig),
371        }
372    }
373
374    fn to_u16(&self) -> Result<u16, LengthError> {
375        match self.data.len() {
376            0 => self.handle_zero(),
377            1 => Ok(self.data[0] as u16),
378            2 => {
379                let ar: &[u8; 2] = self.data.as_array().unwrap();
380                Ok(u16::from_le_bytes(*ar))
381            }
382            _ => Err(LengthError::DataTooBig),
383        }
384    }
385
386    fn to_u32(&self) -> Result<u32, LengthError> {
387        match self.data.len() {
388            0 => self.handle_zero(),
389            1 => Ok(self.data[0] as u32),
390            2 => {
391                let ar: &[u8; 2] = self.data.as_array().unwrap();
392                let value = u16::from_le_bytes(*ar);
393                Ok(value as u32)
394            }
395            3 => Err(LengthError::Truncated),
396            4 => {
397                let ar: &[u8; 4] = self.data.as_array().unwrap();
398                Ok(u32::from_le_bytes(*ar))
399            }
400            _ => Err(LengthError::DataTooBig),
401        }
402    }
403
404    fn to_i32(&self) -> Result<i32, LengthError> {
405        match self.data.len() {
406            0 => self.handle_zero(),
407            1 => Ok(self.data[0] as i8 as i32),
408            2 => {
409                let ar: &[u8; 2] = self.data.as_array().unwrap();
410                let value = i16::from_le_bytes(*ar);
411                Ok(value as i32)
412            }
413            3 => Err(LengthError::Truncated),
414            4 => {
415                let ar: &[u8; 4] = self.data.as_array().unwrap();
416                Ok(i32::from_le_bytes(*ar))
417            }
418            _ => Err(LengthError::DataTooBig),
419        }
420    }
421
422    fn to_option_i32(&self) -> Result<Option<i32>, LengthError> {
423        if self.is_empty() {
424            Ok(None)
425        } else {
426            let value = self.to_i32()?;
427            Ok(Some(value))
428        }
429    }
430}
431
432#[cfg(test)]
433mod tests {
434    use super::*;
435
436    #[test]
437    fn _error_traits() {
438        use std::error::Error;
439        use std::fmt::Display;
440        let _err: Box<dyn Error> = Box::new(LengthError::DataTooBig);
441        let _err: Box<dyn Display> = Box::new(LengthError::DataTooBig);
442        assert_eq!(format!("{}", LengthError::Truncated), "item data truncated");
443    }
444
445    #[test]
446    fn test_integer_decoding() {
447        #[track_caller]
448        fn idata(slice: &[u8]) -> ItemData {
449            ItemData::new(slice.len(), &mut slice.iter().copied(), true)
450                .expect("malformed item data")
451        }
452
453        assert_eq!(idata(&[0xFF]).to_u8().unwrap(), 0xFF);
454        idata(&[]).to_u8().unwrap_err();
455        idata(&[0xFF, 0xFF]).to_u8().unwrap_err();
456
457        #[track_caller]
458        fn test_u16(slice: &[u8], expected: Result<u16, LengthError>) {
459            assert_eq!(idata(slice).to_u16(), expected);
460        }
461
462        test_u16(&[0xFF], Ok(0xFF));
463        test_u16(&[0xFF, 0xFF], Ok(0xFFFF));
464        test_u16(&[1, 2], Ok(0x0201));
465        test_u16(&[], Err(LengthError::ZeroLength));
466        test_u16(&[0xFF; 3], Err(LengthError::DataTooBig));
467        test_u16(&[0xFF; 4], Err(LengthError::DataTooBig));
468
469        #[track_caller]
470        fn test_u32(slice: &[u8], expected: Result<u32, LengthError>) {
471            assert_eq!(idata(slice).to_u32(), expected);
472        }
473
474        test_u32(&[0xFF], Ok(0xFF));
475        test_u32(&[0xFF, 0xFF], Ok(0xFFFF));
476        test_u32(&[1, 2], Ok(0x0201));
477        test_u32(&[0xFF; 4], Ok(0xFFFFFFFF));
478        test_u32(&[1, 2, 3, 4], Ok(0x04030201));
479        test_u32(&[], Err(LengthError::ZeroLength));
480        test_u32(&[0xFF; 3], Err(LengthError::Truncated));
481
482        #[track_caller]
483        fn test_i32(slice: &[u8], expected: Result<i32, LengthError>) {
484            assert_eq!(idata(slice).to_i32(), expected);
485        }
486
487        test_i32(&[0], Ok(0));
488        test_i32(&[0, 0], Ok(0));
489        test_i32(&[0, 0, 0, 0], Ok(0));
490        test_i32(&[1, 2], Ok(0x0201));
491        test_i32(&[0; 4], Ok(0));
492        test_i32(&[1, 2, 3, 4], Ok(0x04030201));
493        test_i32(&[0xFF], Ok(-1));
494        test_i32(&[0xFF, 0xFF], Ok(-1));
495        test_i32(&[0xFF; 4], Ok(-1));
496        test_i32(&[], Err(LengthError::ZeroLength));
497        test_i32(&[0xFF; 3], Err(LengthError::Truncated));
498    }
499}