• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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