diff --git a/src/key.rs b/src/key.rs index d10c607..c70bdf6 100644 --- a/src/key.rs +++ b/src/key.rs @@ -1,5 +1,4 @@ use defmt::Format; -use serde::{Deserialize, Serialize}; use usbd_human_interface_device::page::Keyboard; #[allow(dead_code)] @@ -221,7 +220,7 @@ pub enum Control { pub trait LayerIndex: Copy + Default + PartialEq + PartialOrd + Format + Into {} -#[derive(Clone, Copy, Debug, Default, Deserialize, Format, PartialEq, Serialize)] +#[derive(Clone, Copy, Debug, Default, Format, PartialEq)] pub enum Edge { #[default] None, diff --git a/src/matrix.rs b/src/matrix.rs index d7372cb..4aca081 100644 --- a/src/matrix.rs +++ b/src/matrix.rs @@ -29,14 +29,28 @@ impl Serialize for Result>>(), - )?; + + let mut packed_matrix = Vec::with_capacity((ROW_COUNT * COL_COUNT * 3) / 8); + let mut bit_accumulator: u32 = 0; // use u32 to avoid overflow + let mut bit_count = 0; + + for row in &self.matrix { + for bit in row { + bit_accumulator = (bit_accumulator << 3) | bit.pack() as u32; + bit_count += 3; + + if bit_count >= 8 { + packed_matrix.push((bit_accumulator >> (bit_count - 8)) as u8); + bit_count -= 8; + } + } + } + + if bit_count > 0 { + packed_matrix.push((bit_accumulator << (8 - bit_count)) as u8); + } + + state.serialize_field("matrix", &packed_matrix)?; state.end() } } @@ -51,36 +65,46 @@ impl<'de, const ROW_COUNT: usize, const COL_COUNT: usize> Deserialize<'de> #[derive(Deserialize)] struct ResultOwned { scan_time_ticks: u64, - matrix: Vec>, + matrix: Vec, } let temp = ResultOwned::deserialize(deserializer)?; - if temp.matrix.len() != ROW_COUNT || temp.matrix.iter().any(|row| row.len() != COL_COUNT) { + let expected_len = (ROW_COUNT * COL_COUNT * 3 + 7) / 8; // add 7 to round up to next byte + if temp.matrix.len() != expected_len { return Err(de::Error::custom( - "matrix dimensions do not match expectation", + "matrix length does not match expected length", )); } - let mut result = Result:: { - scan_time_ticks: temp.scan_time_ticks, - ..Default::default() - }; - for (i, row) in temp.matrix.into_iter().enumerate() { - for (j, bit) in row.into_iter().enumerate() { - result.matrix[i][j] = bit; + let mut matrix = [[Bit { + edge: Edge::None, + pressed: false, + }; COL_COUNT]; ROW_COUNT]; + let mut bit_accumulator: u32 = 0; // use u32 to avoid overflow + let mut bit_count = 0; + let mut byte_index = 0; + + #[allow(clippy::needless_range_loop)] + for i in 0..ROW_COUNT { + for j in 0..COL_COUNT { + while bit_count < 3 { + bit_accumulator = (bit_accumulator << 8) | temp.matrix[byte_index] as u32; + byte_index += 1; + bit_count += 8; + } + + matrix[i][j] = Bit::unpack((bit_accumulator >> (bit_count - 3)) as u8); + bit_count -= 3; } } - Ok(result) + Ok(Result { + scan_time_ticks: temp.scan_time_ticks, + matrix, + }) } } -#[derive(Clone, Copy, Debug, Deserialize, Format, Serialize)] -pub struct Bit { - pub edge: Edge, - pub pressed: bool, -} - impl Default for Result { fn default() -> Self { Result { @@ -93,6 +117,29 @@ impl Default for Result u8 { + (self.edge as u8) << 1 | (self.pressed as u8) + } + + fn unpack(packed: u8) -> Self { + let edge = match packed >> 1 { + 0 => Edge::None, + 1 => Edge::Rising, + 2 => Edge::Falling, + _ => Edge::None, // invalid + }; + let pressed = packed & 1 != 0; + Bit { edge, pressed } + } +} + #[async_trait] pub trait Scanner { async fn scan(&mut self) -> Result;