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