1 //! A UUID (See Core Spec 5.3 Vol 1E 2.9.1. Basic Types) 2 3 use crate::packets::att; 4 5 /// A UUID (See Core Spec 5.3 Vol 1E 2.9.1. Basic Types) 6 /// 7 /// Note that the underlying storage is BIG-ENDIAN! But this should be viewed 8 /// as an implementation detail for C++ interop ONLY - all converters etc. 9 /// should act as though the backing storage is LITTLE-ENDIAN. 10 #[derive(PartialEq, Eq, Clone, Copy, Debug)] 11 #[repr(transparent)] 12 pub struct Uuid([u8; 16]); 13 14 const BASE_UUID: u128 = 0x00000000_0000_1000_8000_0080_5F9B_34FB; 15 16 impl Uuid { 17 /// Constructor from a u32. new(val: u32) -> Self18 pub const fn new(val: u32) -> Self { 19 Self((BASE_UUID + ((val as u128) << 96)).to_be_bytes()) 20 } 21 new_from_le_bytes(mut bytes: [u8; 16]) -> Self22 fn new_from_le_bytes(mut bytes: [u8; 16]) -> Self { 23 bytes.reverse(); 24 Self(bytes) 25 } 26 le_bytes(&self) -> [u8; 16]27 fn le_bytes(&self) -> [u8; 16] { 28 let mut out = self.0; 29 out.reverse(); 30 out 31 } 32 } 33 34 impl TryFrom<att::Uuid> for Uuid { 35 type Error = att::Uuid; 36 try_from(value: att::Uuid) -> Result<Self, att::Uuid>37 fn try_from(value: att::Uuid) -> Result<Self, att::Uuid> { 38 let bytes = value.data.as_slice(); 39 Ok(match bytes.len() { 40 2 => Self::new(u16::from_le_bytes([bytes[0], bytes[1]]) as u32), 41 4 => Self::new(u32::from_le_bytes([bytes[0], bytes[1], bytes[2], bytes[3]])), 42 // TODO(aryarahul) - should we handle >16 byte Uuids and drop extra bytes? 43 _ => Self::new_from_le_bytes(bytes.try_into().map_err(|_| value)?), 44 }) 45 } 46 } 47 48 impl From<att::Uuid16> for Uuid { from(uuid: att::Uuid16) -> Self49 fn from(uuid: att::Uuid16) -> Self { 50 Self::new(uuid.data as u32) 51 } 52 } 53 54 impl From<att::Uuid128> for Uuid { from(uuid: att::Uuid128) -> Self55 fn from(uuid: att::Uuid128) -> Self { 56 Self::new_from_le_bytes(uuid.data) 57 } 58 } 59 60 impl From<Uuid> for att::Uuid { from(value: Uuid) -> Self61 fn from(value: Uuid) -> Self { 62 // TODO(aryarahul): compress to UUID-16 if possible 63 att::Uuid { data: value.le_bytes().to_vec() } 64 } 65 } 66 67 impl TryFrom<Uuid> for att::Uuid16 { 68 type Error = Uuid; 69 try_from(value: Uuid) -> Result<Self, Self::Error>70 fn try_from(value: Uuid) -> Result<Self, Self::Error> { 71 let backing = u128::from_be_bytes(value.0); 72 if backing & ((1u128 << 96) - 1) == BASE_UUID { 73 if let Ok(data) = u16::try_from(backing >> 96) { 74 return Ok(att::Uuid16 { data }); 75 } 76 } 77 Err(value) 78 } 79 } 80 81 impl From<Uuid> for att::Uuid128 { from(value: Uuid) -> Self82 fn from(value: Uuid) -> Self { 83 att::Uuid128 { data: value.le_bytes() } 84 } 85 } 86 87 #[cfg(test)] 88 mod test { 89 use super::*; 90 91 #[test] test_uuid16_builder_successful()92 fn test_uuid16_builder_successful() { 93 let uuid = Uuid::new(0x0102); 94 let builder: att::Uuid16 = uuid.try_into().unwrap(); 95 assert_eq!(builder.data, 0x0102); 96 } 97 98 #[test] test_uuid16_builder_fail_nonzero_trailing_bytes()99 fn test_uuid16_builder_fail_nonzero_trailing_bytes() { 100 let uuid = Uuid::new(0x01020304); 101 let res: Result<att::Uuid16, _> = uuid.try_into(); 102 assert!(res.is_err()); 103 } 104 105 #[test] test_uuid16_builder_fail_invalid_prefix()106 fn test_uuid16_builder_fail_invalid_prefix() { 107 let mut uuid = Uuid::new(0x0102); 108 uuid.0[0] = 1; 109 110 let res: Result<att::Uuid16, _> = uuid.try_into(); 111 assert!(res.is_err()); 112 } 113 114 #[test] test_uuid128_builder()115 fn test_uuid128_builder() { 116 let uuid = Uuid::new(0x01020304); 117 let builder: att::Uuid128 = uuid.into(); 118 assert_eq!(builder.data[..12], BASE_UUID.to_le_bytes()[..12]); 119 assert_eq!(builder.data[12..], [4, 3, 2, 1]); 120 } 121 122 #[test] test_uuid_builder()123 fn test_uuid_builder() { 124 let uuid = Uuid::new(0x01020304); 125 let builder: att::Uuid = uuid.into(); 126 assert_eq!(builder.data[..12], BASE_UUID.to_le_bytes()[..12]); 127 assert_eq!(builder.data[12..], [4, 3, 2, 1]); 128 } 129 130 #[test] test_uuid_from_16_fixed_view()131 fn test_uuid_from_16_fixed_view() { 132 let expected = Uuid::new(0x0102); 133 let actual: Uuid = att::Uuid16 { data: 0x0102 }.try_into().unwrap(); 134 assert_eq!(expected, actual); 135 } 136 137 #[test] test_uuid_from_128_fixed_view()138 fn test_uuid_from_128_fixed_view() { 139 let data = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]; 140 let expected = Uuid::new_from_le_bytes(data); 141 let actual: Uuid = att::Uuid128 { data }.try_into().unwrap(); 142 assert_eq!(expected, actual); 143 } 144 145 #[test] test_uuid_from_16_view()146 fn test_uuid_from_16_view() { 147 let expected = Uuid::new(0x0102); 148 let actual: Uuid = att::Uuid { data: vec![2, 1] }.try_into().unwrap(); 149 assert_eq!(expected, actual); 150 } 151 152 #[test] test_uuid_from_32_view()153 fn test_uuid_from_32_view() { 154 let expected = Uuid::new(0x01020304); 155 let actual: Uuid = att::Uuid { data: vec![4, 3, 2, 1] }.try_into().unwrap(); 156 assert_eq!(expected, actual); 157 } 158 159 #[test] test_uuid_from_128_view()160 fn test_uuid_from_128_view() { 161 let data = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]; 162 let expected = Uuid::new_from_le_bytes(data); 163 let actual: Uuid = att::Uuid { data: data.into() }.try_into().unwrap(); 164 assert_eq!(expected, actual); 165 } 166 167 #[test] test_uuid_from_invalid_view()168 fn test_uuid_from_invalid_view() { 169 let packet = att::Uuid { data: vec![10, 9, 8, 7, 6, 5, 4, 3, 2, 1] }; 170 let res = Uuid::try_from(packet); 171 assert!(res.is_err()); 172 } 173 } 174