| // Copyright 2023 The Fuchsia Authors. All rights reserved. | |
| // Use of this source code is governed by a BSD-style license that can be | |
| // found in the LICENSE file. | |
| use thiserror::Error; | |
| use crate::packet_encoding::{Decodable, Encodable}; | |
| /// Implement Ltv when a collection of types is represented in the Bluetooth | |
| /// specifications as a length-type-value structure. They should have an | |
| /// associated type which can be retrieved from a type byte. | |
| pub trait LtValue: Sized { | |
| type Type: Into<u8> + Copy + std::fmt::Debug; | |
| const NAME: &'static str; | |
| /// Given a type octet, return the associated Type if it is possible. | |
| /// Returns None if the value is unrecognized. | |
| fn type_from_octet(x: u8) -> Option<Self::Type>; | |
| /// Returns length bounds for the type indicated, **including** the type | |
| /// byte. Note that the assigned numbers from the Bluetooth SIG include | |
| /// the type byte in their Length specifications. | |
| // TODO: use impl std::ops::RangeBounds when RPITIT is sufficiently stable | |
| fn length_range_from_type(ty: Self::Type) -> std::ops::RangeInclusive<u8>; | |
| /// Retrieve the type of the current value. | |
| fn into_type(&self) -> Self::Type; | |
| /// The length of the encoded value, without the length and type byte. | |
| /// This cannot be 255 in practice, as the length byte is only one octet | |
| /// long. | |
| fn value_encoded_len(&self) -> u8; | |
| /// Decodes the value from a buffer, which does not include the type or | |
| /// length bytes. The `buf` slice length is exactly what was specified | |
| /// for this value in the encoded source. | |
| fn decode_value(ty: &Self::Type, buf: &[u8]) -> Result<Self, crate::packet_encoding::Error>; | |
| /// Encodes a value into `buf`, which is verified to be the correct length | |
| /// as indicated by [LtValue::value_encoded_len]. | |
| fn encode_value(&self, buf: &mut [u8]) -> Result<(), crate::packet_encoding::Error>; | |
| /// Decode a collection of LtValue structures that are present in a buffer. | |
| /// If it is possible to continue decoding after encountering an error, does | |
| /// so and includes the error. If an unrecoverable error occurs, does | |
| /// not consume the final item and the last element in the result is the | |
| /// error. | |
| fn decode_all(buf: &[u8]) -> (Vec<Result<Self, Error<Self::Type>>>, usize) { | |
| let mut results = Vec::new(); | |
| let mut total_consumed = 0; | |
| loop { | |
| if buf.len() <= total_consumed { | |
| return (results, std::cmp::min(buf.len(), total_consumed)); | |
| } | |
| let indicated_len = buf[total_consumed] as usize; | |
| let range_end = std::cmp::min(buf.len() - 1, total_consumed + indicated_len); | |
| match Self::decode(&buf[total_consumed..=range_end]) { | |
| (Ok(item), consumed) => { | |
| results.push(Ok(item)); | |
| total_consumed += consumed; | |
| } | |
| (Err(e), consumed) => { | |
| results.push(Err(e)); | |
| total_consumed += consumed; | |
| } | |
| } | |
| } | |
| } | |
| /// Encode a collection of LtValue structures into a buffer. | |
| /// Even if the encoding fails, `buf` may still be modified by | |
| /// previous encoding successes. | |
| fn encode_all( | |
| iter: impl Iterator<Item = Self>, | |
| buf: &mut [u8], | |
| ) -> Result<(), crate::packet_encoding::Error> { | |
| let mut idx = 0; | |
| for item in iter { | |
| item.encode(&mut buf[idx..])?; | |
| idx += item.encoded_len(); | |
| } | |
| Ok(()) | |
| } | |
| } | |
| #[derive(Error, Debug, PartialEq)] | |
| pub enum Error<Type: std::fmt::Debug + Into<u8> + Copy> { | |
| #[error("Buffer too short for next type")] | |
| MissingType, | |
| #[error("Buffer missing data indicated by length (type {0:?})")] | |
| MissingData(Type), | |
| #[error("Unrecognized type value for {0}: {1}")] | |
| UnrecognizedType(String, u8), | |
| #[error("Length of item ({0}) is outside allowed range for {1:?}: {2:?}")] | |
| LengthOutOfRange(u8, Type, std::ops::RangeInclusive<u8>), | |
| #[error("Error decoding type {0:?}: {1}")] | |
| TypeFailedToDecode(Type, crate::packet_encoding::Error), | |
| } | |
| impl<Type: std::fmt::Debug + Into<u8> + Copy> Error<Type> { | |
| pub fn type_value(&self) -> Option<u8> { | |
| match self { | |
| Self::MissingType => None, | |
| Self::MissingData(t) | |
| | Self::LengthOutOfRange(_, t, _) | |
| | Self::TypeFailedToDecode(t, _) => Some((*t).into()), | |
| Self::UnrecognizedType(_, value) => Some(*value), | |
| } | |
| } | |
| } | |
| impl<T: LtValue> Encodable for T { | |
| type Error = crate::packet_encoding::Error; | |
| fn encoded_len(&self) -> core::primitive::usize { | |
| 2 + self.value_encoded_len() as usize | |
| } | |
| fn encode(&self, buf: &mut [u8]) -> core::result::Result<(), Self::Error> { | |
| if buf.len() < self.encoded_len() { | |
| return Err(crate::packet_encoding::Error::BufferTooSmall); | |
| } | |
| buf[0] = self.value_encoded_len() + 1; | |
| buf[1] = self.into_type().into(); | |
| self.encode_value(&mut buf[2..self.encoded_len()])?; | |
| Ok(()) | |
| } | |
| } | |
| impl<T> Decodable for T | |
| where | |
| T: LtValue, | |
| { | |
| type Error = Error<T::Type>; | |
| fn decode(buf: &[u8]) -> (core::result::Result<Self, Self::Error>, usize) { | |
| if buf.len() < 2 { | |
| return (Err(Error::MissingType), buf.len()); | |
| } | |
| let indicated_len = buf[0] as usize; | |
| let too_short = buf.len() < indicated_len + 1; | |
| let Some(ty) = Self::type_from_octet(buf[1]) else { | |
| return ( | |
| Err(Error::UnrecognizedType(Self::NAME.to_owned(), buf[1])), | |
| if too_short { buf.len() } else { indicated_len + 1 }, | |
| ); | |
| }; | |
| if too_short { | |
| return (Err(Error::MissingData(ty)), buf.len()); | |
| } | |
| let size_range = Self::length_range_from_type(ty); | |
| let remaining_len = (buf.len() - 1) as u8; | |
| if !size_range.contains(&remaining_len) { | |
| return (Err(Error::LengthOutOfRange(remaining_len, ty, size_range)), buf.len()); | |
| } | |
| match Self::decode_value(&ty, &buf[2..=indicated_len]) { | |
| Err(e) => (Err(Error::TypeFailedToDecode(ty, e)), indicated_len + 1), | |
| Ok(s) => (Ok(s), indicated_len + 1), | |
| } | |
| } | |
| } | |
| #[cfg(test)] | |
| mod tests { | |
| use super::*; | |
| #[derive(Copy, Clone, PartialEq, Debug)] | |
| enum TestType { | |
| OneByte, | |
| TwoBytes, | |
| TwoBytesLittleEndian, | |
| UnicodeString, | |
| AlwaysError, | |
| } | |
| impl From<TestType> for u8 { | |
| fn from(value: TestType) -> Self { | |
| match value { | |
| TestType::OneByte => 1, | |
| TestType::TwoBytes => 2, | |
| TestType::TwoBytesLittleEndian => 3, | |
| TestType::UnicodeString => 4, | |
| TestType::AlwaysError => 0xFF, | |
| } | |
| } | |
| } | |
| #[derive(PartialEq, Debug)] | |
| enum TestValues { | |
| OneByte(u8), | |
| TwoBytes(u16), | |
| TwoBytesLittleEndian(u16), | |
| UnicodeString(String), | |
| AlwaysError, | |
| } | |
| impl LtValue for TestValues { | |
| type Type = TestType; | |
| const NAME: &'static str = "TestValues"; | |
| fn type_from_octet(x: u8) -> Option<Self::Type> { | |
| match x { | |
| 1 => Some(TestType::OneByte), | |
| 2 => Some(TestType::TwoBytes), | |
| 3 => Some(TestType::TwoBytesLittleEndian), | |
| 4 => Some(TestType::UnicodeString), | |
| 0xFF => Some(TestType::AlwaysError), | |
| _ => None, | |
| } | |
| } | |
| fn length_range_from_type(ty: Self::Type) -> std::ops::RangeInclusive<u8> { | |
| match ty { | |
| TestType::OneByte => 2..=2, | |
| TestType::TwoBytes => 3..=3, | |
| TestType::TwoBytesLittleEndian => 3..=3, | |
| TestType::UnicodeString => 2..=255, | |
| // AlwaysError fields can be any length (value will be thrown away) | |
| TestType::AlwaysError => 1..=255, | |
| } | |
| } | |
| fn into_type(&self) -> Self::Type { | |
| match self { | |
| TestValues::TwoBytes(_) => TestType::TwoBytes, | |
| TestValues::TwoBytesLittleEndian(_) => TestType::TwoBytesLittleEndian, | |
| TestValues::OneByte(_) => TestType::OneByte, | |
| TestValues::UnicodeString(_) => TestType::UnicodeString, | |
| TestValues::AlwaysError => TestType::AlwaysError, | |
| } | |
| } | |
| fn value_encoded_len(&self) -> u8 { | |
| match self { | |
| TestValues::TwoBytes(_) => 2, | |
| TestValues::TwoBytesLittleEndian(_) => 2, | |
| TestValues::OneByte(_) => 1, | |
| TestValues::UnicodeString(s) => s.len() as u8, | |
| TestValues::AlwaysError => 0, | |
| } | |
| } | |
| fn decode_value( | |
| ty: &Self::Type, | |
| buf: &[u8], | |
| ) -> Result<Self, crate::packet_encoding::Error> { | |
| match ty { | |
| TestType::OneByte => Ok(TestValues::OneByte(buf[0])), | |
| TestType::TwoBytes => { | |
| Ok(TestValues::TwoBytes(u16::from_be_bytes([buf[0], buf[1]]))) | |
| } | |
| TestType::TwoBytesLittleEndian => { | |
| Ok(TestValues::TwoBytesLittleEndian(u16::from_le_bytes([buf[0], buf[1]]))) | |
| } | |
| TestType::UnicodeString => { | |
| Ok(TestValues::UnicodeString(String::from_utf8_lossy(buf).into_owned())) | |
| } | |
| TestType::AlwaysError => Err(crate::packet_encoding::Error::OutOfRange), | |
| } | |
| } | |
| fn encode_value(&self, buf: &mut [u8]) -> Result<(), crate::packet_encoding::Error> { | |
| if buf.len() < self.value_encoded_len() as usize { | |
| return Err(crate::packet_encoding::Error::BufferTooSmall); | |
| } | |
| match self { | |
| TestValues::TwoBytes(x) => { | |
| [buf[0], buf[1]] = x.to_be_bytes(); | |
| } | |
| TestValues::TwoBytesLittleEndian(x) => { | |
| [buf[0], buf[1]] = x.to_le_bytes(); | |
| } | |
| TestValues::OneByte(x) => { | |
| buf[0] = *x; | |
| } | |
| TestValues::UnicodeString(s) => { | |
| buf.copy_from_slice(s.as_bytes()); | |
| } | |
| TestValues::AlwaysError => { | |
| return Err(crate::packet_encoding::Error::InvalidParameter("test".to_owned())); | |
| } | |
| } | |
| Ok(()) | |
| } | |
| } | |
| #[test] | |
| fn decode_twobytes() { | |
| let encoded = [0x03, 0x02, 0x10, 0x01, 0x03, 0x03, 0x10, 0x01]; | |
| let (decoded, consumed) = TestValues::decode_all(&encoded); | |
| assert_eq!(consumed, encoded.len()); | |
| assert_eq!(decoded[0], Ok(TestValues::TwoBytes(4097))); | |
| assert_eq!(decoded[1], Ok(TestValues::TwoBytesLittleEndian(272))); | |
| } | |
| #[test] | |
| fn decode_unrecognized() { | |
| let encoded = [0x03, 0x02, 0x10, 0x01, 0x03, 0x06, 0x10, 0x01]; | |
| let (decoded, consumed) = TestValues::decode_all(&encoded); | |
| assert_eq!(consumed, encoded.len()); | |
| assert_eq!(decoded[0], Ok(TestValues::TwoBytes(4097))); | |
| assert_eq!(decoded[1], Err(Error::UnrecognizedType("TestValues".to_owned(), 6))); | |
| } | |
| #[test] | |
| fn decode_wronglength() { | |
| let encoded = [0x03, 0x02, 0x10, 0x01, 0x04, 0x03, 0x10, 0x01]; | |
| let (decoded, consumed) = TestValues::decode_all(&encoded); | |
| assert_eq!(consumed, encoded.len()); | |
| assert_eq!(decoded[0], Ok(TestValues::TwoBytes(4097))); | |
| assert_eq!(decoded[1], Err(Error::MissingData(TestType::TwoBytesLittleEndian))); | |
| } | |
| #[test] | |
| fn encode_twobytes() { | |
| let value = TestValues::TwoBytes(0x0A0B); | |
| let mut buf = [0; 4]; | |
| value.encode(&mut buf[..]).expect("should succeed"); | |
| assert_eq!(buf, [0x03, 0x02, 0x0A, 0x0B]); | |
| } | |
| #[test] | |
| fn encode_all() { | |
| let value1 = TestValues::OneByte(0x0A); | |
| let value2 = TestValues::UnicodeString("Bluetooth".to_string()); | |
| let mut buf = [0; 14]; | |
| LtValue::encode_all(vec![value1, value2].into_iter(), &mut buf).expect("should succeed"); | |
| assert_eq!( | |
| buf, | |
| [0x02, 0x01, 0x0a, 0x0a, 0x04, 0x42, 0x6c, 0x75, 0x65, 0x74, 0x6f, 0x6f, 0x74, 0x68] | |
| ); | |
| } | |
| #[track_caller] | |
| fn u8char(c: char) -> u8 { | |
| c.try_into().unwrap() | |
| } | |
| #[test] | |
| fn decode_variable_lengths() { | |
| let encoded = [ | |
| 0x03, | |
| 0x02, | |
| 0x10, | |
| 0x01, | |
| 0x0A, | |
| 0x04, | |
| u8char('B'), | |
| u8char('l'), | |
| u8char('u'), | |
| u8char('e'), | |
| u8char('t'), | |
| u8char('o'), | |
| u8char('o'), | |
| u8char('t'), | |
| u8char('h'), | |
| 0x02, | |
| 0x01, | |
| 0x01, | |
| ]; | |
| let (decoded, consumed) = TestValues::decode_all(&encoded); | |
| assert_eq!(consumed, encoded.len()); | |
| assert_eq!(decoded[0], Ok(TestValues::TwoBytes(4097))); | |
| assert_eq!(decoded[1], Ok(TestValues::UnicodeString("Bluetooth".to_owned()))); | |
| assert_eq!(decoded[2], Ok(TestValues::OneByte(1))); | |
| } | |
| #[test] | |
| fn decode_with_error() { | |
| let encoded = [0x03, 0x02, 0x10, 0x01, 0x02, 0xFF, 0xFF, 0x02, 0x01, 0x03]; | |
| let (decoded, consumed) = TestValues::decode_all(&encoded); | |
| assert_eq!(consumed, encoded.len()); | |
| assert_eq!(decoded[0], Ok(TestValues::TwoBytes(4097))); | |
| assert_eq!( | |
| decoded[1], | |
| Err(Error::TypeFailedToDecode( | |
| TestType::AlwaysError, | |
| crate::packet_encoding::Error::OutOfRange | |
| )) | |
| ); | |
| assert_eq!(decoded[2], Ok(TestValues::OneByte(3))); | |
| } | |
| #[test] | |
| fn encode_with_error() { | |
| let value = TestValues::AlwaysError; | |
| let mut buf = [0; 10]; | |
| assert!(matches!( | |
| value.encode(&mut buf), | |
| Err(crate::packet_encoding::Error::InvalidParameter(_)), | |
| )); | |
| let value1 = TestValues::TwoBytes(0x0A0B); | |
| let value2 = TestValues::OneByte(0x0A); | |
| let mut buf = [0; 2]; // not enough buffer space. | |
| LtValue::encode_all(vec![value1, value2].into_iter(), &mut buf).expect_err("should fail"); | |
| } | |
| } |