| 1 | //! A cursor over bytes from the buds. Every read returns a `Result`, so a short or malformed |
| 2 | //! message becomes an [`Error::Protocol`] instead of a panic. |
| 3 | |
| 4 | use crate::error::{Error, Result}; |
| 5 | |
| 6 | pub(crate) struct Reader<'a> { |
| 7 | bytes: &'a [u8], |
| 8 | /// What is being parsed, for error messages ("battery reply", "SDP response"…). |
| 9 | what: &'static str, |
| 10 | } |
| 11 | |
| 12 | impl<'a> Reader<'a> { |
| 13 | pub(crate) fn new(bytes: &'a [u8], what: &'static str) -> Self { |
| 14 | Self { bytes, what } |
| 15 | } |
| 16 | |
| 17 | pub(crate) fn u8(&mut self) -> Result<u8> { |
| 18 | let [byte] = self.array::<1>()?; |
| 19 | Ok(byte) |
| 20 | } |
| 21 | |
| 22 | pub(crate) fn i8(&mut self) -> Result<i8> { |
| 23 | let [byte] = self.array::<1>()?; |
| 24 | Ok(i8::from_le_bytes([byte])) |
| 25 | } |
| 26 | |
| 27 | pub(crate) fn u16_le(&mut self) -> Result<u16> { |
| 28 | Ok(u16::from_le_bytes(self.array()?)) |
| 29 | } |
| 30 | |
| 31 | pub(crate) fn u16_be(&mut self) -> Result<u16> { |
| 32 | Ok(u16::from_be_bytes(self.array()?)) |
| 33 | } |
| 34 | |
| 35 | pub(crate) fn u32_be(&mut self) -> Result<u32> { |
| 36 | Ok(u32::from_be_bytes(self.array()?)) |
| 37 | } |
| 38 | |
| 39 | /// The next `n` bytes. |
| 40 | pub(crate) fn take(&mut self, n: usize) -> Result<&'a [u8]> { |
| 41 | let (head, tail) = self.bytes.split_at_checked(n).ok_or_else(|| self.too_short())?; |
| 42 | self.bytes = tail; |
| 43 | Ok(head) |
| 44 | } |
| 45 | |
| 46 | /// Everything not read yet. |
| 47 | pub(crate) fn rest(&mut self) -> &'a [u8] { |
| 48 | std::mem::take(&mut self.bytes) |
| 49 | } |
| 50 | |
| 51 | pub(crate) fn is_empty(&self) -> bool { |
| 52 | self.bytes.is_empty() |
| 53 | } |
| 54 | |
| 55 | /// The next `N` bytes, as an array. |
| 56 | pub(crate) fn array<const N: usize>(&mut self) -> Result<[u8; N]> { |
| 57 | let (head, tail) = self.bytes.split_first_chunk::<N>().ok_or_else(|| self.too_short())?; |
| 58 | self.bytes = tail; |
| 59 | Ok(*head) |
| 60 | } |
| 61 | |
| 62 | fn too_short(&self) -> Error { |
| 63 | Error::Protocol(format!("{} is too short", self.what)) |
| 64 | } |
| 65 | } |
| 66 | |
| 67 | #[cfg(test)] |
| 68 | mod tests { |
| 69 | use super::*; |
| 70 | |
| 71 | #[test] |
| 72 | fn reads_in_order_and_reports_short_input() { |
| 73 | let mut r = Reader::new(&[0x01, 0x34, 0x12, 0x12, 0x34, 0xFF], "test"); |
| 74 | assert_eq!(r.u8().unwrap(), 0x01); |
| 75 | assert_eq!(r.u16_le().unwrap(), 0x1234); |
| 76 | assert_eq!(r.u16_be().unwrap(), 0x1234); |
| 77 | assert_eq!(r.i8().unwrap(), -1); |
| 78 | assert!(r.is_empty()); |
| 79 | assert!(matches!(r.u8(), Err(Error::Protocol(_)))); |
| 80 | } |
| 81 | |
| 82 | #[test] |
| 83 | fn take_and_rest() { |
| 84 | let mut r = Reader::new(&[1, 2, 3, 4], "test"); |
| 85 | assert_eq!(r.take(2).unwrap(), &[1, 2]); |
| 86 | assert!(r.take(3).is_err()); |
| 87 | assert_eq!(r.rest(), &[3, 4]); |
| 88 | assert_eq!(r.rest(), &[] as &[u8]); |
| 89 | } |
| 90 | } |