Skip to main content

hid_types/
encoding.rs

1//! Details of descriptor item encoding.
2
3use bitfield_struct::bitfield;
4use num_enum::TryFromPrimitive;
5
6use crate::id::tag::IntoTagType;
7
8/// A one-byte tag+type+size value
9#[bitfield(u8, debug = false)]
10pub struct TagTypeSize {
11    /// The bSize field.
12    #[bits(2)]
13    pub encoded_size: SizeBits,
14    /// The bType field.
15    #[bits(2)]
16    pub ty: TypeBits,
17    /// the bTag field.
18    #[bits(4)]
19    pub tag: u8,
20}
21
22impl TagTypeSize {
23    /// Create a `TagTypeSize` from a tag and data length.
24    pub fn from_tag<T: IntoTagType>(tag: T, data_len: usize) -> Self {
25        let size = SizeBits::from_size(data_len);
26        tag.encode_tag().with_encoded_size(size)
27    }
28
29    /// Decode the data size.
30    pub fn size(self) -> Size {
31        if self.ty() == TypeBits::Reserved
32            && self.tag() == 0xF
33            && self.encoded_size() == SizeBits::Two
34        {
35            // This is a "long item format" with a different size encoding
36            Size::Long
37        } else {
38            Size::Short(self.encoded_size().size_bytes())
39        }
40    }
41}
42
43/// An integer value, in little-endian variable length form.
44///
45/// Drops any trailing zero bytes, but always returns a value of at least one byte.
46pub fn encode_unsigned(data: &[u8]) -> &[u8] {
47    if data.is_empty() {
48        return data;
49    }
50    assert!(data.len() <= 4 && data.len() != 3);
51    let count_zero = data.iter().rev().take_while(|&&b| b == 0).count();
52    let mut truncated_len = data.len() - count_zero;
53    // We choose to encode zero as [0] rather than [].
54    if truncated_len == 0 {
55        truncated_len = 1;
56    }
57    // We can't truncate to 3 bytes, since that length can't be encoded.
58    if truncated_len == 3 {
59        truncated_len = 4;
60    }
61    &data[..truncated_len]
62}
63
64/// An integer value, in little-endian variable length form.
65///
66/// Drops any trailing zero bytes, if that leaves the last byte with its
67/// most-significant bit 0.
68///
69/// Drops any trailing `0xFF` bytes, if that leaves the last byte with the
70/// most-significant bit set.
71///
72/// Because this is a signed value, leading zeros can only be dropped if
73/// the following byte doesn't have the sign bit (MSB) set.
74pub fn encode_signed(data: &[u8]) -> &[u8] {
75    // look at a window of 2 bytes and determine if we can drop the MSB.
76    fn can_drop(&&[lsb, msb]: &&[u8; 2]) -> bool {
77        // A positive value that can be shortened
78        (msb == 0 && ((lsb & 0x80) == 0)) ||
79        // A negative value that can be shortened
80        (msb == 0xFF) && ((lsb & 0x80) != 0)
81    }
82
83    if data.is_empty() {
84        return data;
85    }
86
87    let count_zero = data.array_windows::<2>().rev().take_while(can_drop).count();
88    let mut truncated_len = data.len() - count_zero;
89    // We can't truncate to 3 bytes, since that length can't be encoded.
90    if truncated_len == 3 {
91        truncated_len = 4;
92    }
93    &data[..truncated_len]
94}
95
96/// A value indicating the encoded data size.
97pub enum Size {
98    /// A short payload, 0-4 bytes.
99    Short(usize),
100    /// A long payload.
101    Long,
102}
103
104/// The bits specifying the data size.
105#[expect(missing_docs)]
106#[derive(Clone, Copy, Debug, PartialEq, Eq)]
107#[repr(u8)]
108pub enum SizeBits {
109    Zero = 0,
110    One = 1,
111    Two = 2,
112    Four = 3,
113}
114
115impl SizeBits {
116    /// The size, encoded for a binary report descriptor.
117    pub const fn into_bits(self) -> u8 {
118        self as _
119    }
120
121    /// Decode the size from an encoded byte value.
122    ///
123    /// The input values allowed are the same as those in the binary report descriptor (0-3).
124    ///
125    /// # Panics
126    /// This function will panic if the input is out of the range 0-3.
127    // Note: this needs to be a `const fn` for compatibility with the `bitfield` macro.
128    pub const fn from_bits(value: u8) -> Self {
129        match value {
130            0 => Self::Zero,
131            1 => Self::One,
132            2 => Self::Two,
133            3 => Self::Four,
134            _ => panic!("SizeBits value out of range"),
135        }
136    }
137
138    /// Return the number of bytes this field represents.
139    ///
140    /// Note: this value is only correct for the "short item format".
141    pub fn size_bytes(self) -> usize {
142        match self {
143            SizeBits::Zero => 0,
144            SizeBits::One => 1,
145            SizeBits::Two => 2,
146            SizeBits::Four => 4,
147        }
148    }
149
150    /// Create `SizeBits` from an integer size.
151    pub fn from_size(size: usize) -> Self {
152        match size {
153            0 => SizeBits::Zero,
154            1 => SizeBits::One,
155            2 => SizeBits::Two,
156            4 => SizeBits::Four,
157            n => panic!("improper short item size ({n})"),
158        }
159    }
160}
161
162/// The bits specifying the type of an item.
163#[expect(missing_docs)]
164#[derive(Clone, Copy, Debug, PartialEq, Eq, TryFromPrimitive)]
165#[repr(u8)]
166pub enum TypeBits {
167    Main = 0,
168    Global = 1,
169    Local = 2,
170    Reserved = 3,
171}
172
173impl TypeBits {
174    /// Convert the `TypeBits` into an integer in the encoded form.
175    pub const fn into_bits(self) -> u8 {
176        self as _
177    }
178
179    /// Decode encoded type bits.
180    ///
181    /// The input values allowed are the same as those in the binary report descriptor (0-3).
182    ///
183    /// # Panics
184    /// This function will panic if the input is out of the range 0-3.
185    ///
186    // Note: this needs to be a `const fn` for compatibility with the `bitfield` macro.
187    pub const fn from_bits(value: u8) -> Self {
188        match value {
189            0 => Self::Main,
190            1 => Self::Global,
191            2 => Self::Local,
192            3 => Self::Reserved,
193            _ => panic!("TypeBits value out of range"),
194        }
195    }
196}
197
198#[cfg(feature = "std")]
199mod std_impls {
200    use super::*;
201    use std::fmt::{self, Debug};
202
203    impl Debug for TagTypeSize {
204        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
205            f.debug_struct("TagTypeSize")
206                .field("raw", &format_args!("{:#04x}", self.into_bits()))
207                .field("size", &self.encoded_size().size_bytes())
208                .field("type", &self.ty())
209                .field("tag", &format_args!("{:#x}", self.tag()))
210                .finish()
211        }
212    }
213}
214
215#[cfg(test)]
216mod tests {
217    use super::*;
218
219    #[test]
220    fn test_unsigned_encoding() {
221        // little-endian variable length encoding of 1, 2, or 4 bytes.
222        // trailing zeros are always removed.
223        let unsigned_values = [
224            (0u32, &[0x00][..]),
225            (0x7F, &[0x7F]),
226            (0x80, &[0x80]),
227            (0xFF, &[0xFF]),
228            (0x100, &[0x00, 0x01]),
229            (0xFFFF, &[0xFF, 0xFF]),
230            (0x123456, &[0x56, 0x34, 0x12, 0x00]),
231        ];
232        for (value, expected) in unsigned_values {
233            assert_eq!(expected, encode_unsigned(&value.to_le_bytes()))
234        }
235    }
236
237    #[test]
238    fn test_signed_encoding() {
239        // little-endian variable length encoding of 1, 2, or 4 bytes.
240        // trailing zeros or 0xFF bytes may be removed, as long as
241        // the sign bit is preserved.
242        let signed_values = [
243            (0i32, &[0x00][..]),
244            (1i32, &[0x01]),
245            (0x7F, &[0x7F]),
246            (0x80, &[0x80, 0x00]),
247            (0xFF, &[0xFF, 0x00]),
248            (0x100, &[0x00, 0x01]),
249            (0xFFFF, &[0xFF, 0xFF, 0x00, 0x00]),
250            (0x123456, &[0x56, 0x34, 0x12, 0x00]),
251            (-1, &[0xFF]),
252            (-128, &[0x80]),
253            (-129, &[0x7F, 0xFF]),
254            (-32768, &[0x00, 0x80]),
255            (-32769, &[0xFF, 0x7F, 0xFF, 0xFF]),
256            (-2147483648, &[0x00, 0x00, 0x00, 0x80]),
257        ];
258        for (value, expected) in signed_values {
259            let bytes = value.to_le_bytes();
260            let encoded = encode_signed(&bytes);
261            assert_eq!(
262                expected, encoded,
263                "expected {:X?}, got {:X?}",
264                expected, encoded
265            )
266        }
267    }
268}