1use std::fmt::{Debug, Display, Formatter};
2use std::hash::{Hash, Hasher};
3use std::num::{NonZeroU128, NonZeroU16};
4use std::ops::Deref;
5use std::ptr;
6
7use num_enum::TryFromPrimitive;
8use structbuf::{Packer, Unpack};
9
10const SHIFT: u32 = u128::BITS - u32::BITS;
11const BASE: u128 = 0x00000000_0000_1000_8000_00805F9B34FB;
12const MASK_16: u128 = !((u16::MAX as u128) << SHIFT);
13const MASK_32: u128 = !((u32::MAX as u128) << SHIFT);
14
15#[derive(Clone, Copy, Eq, Hash, Ord, PartialEq, PartialOrd)]
17#[repr(transparent)]
18pub struct Uuid(NonZeroU128);
19
20impl Uuid {
21 pub const BYTES: usize = std::mem::size_of::<Self>();
23 pub const MAX: Self = Self(
25 unsafe { NonZeroU128::new_unchecked(u128::MAX) },
27 );
28
29 #[inline]
31 #[must_use]
32 pub const fn new(v: u128) -> Option<Self> {
33 match NonZeroU128::new(v) {
34 Some(nz) => Some(Self(nz)),
35 None => None,
36 }
37 }
38
39 #[inline]
46 #[must_use]
47 pub const unsafe fn new_unchecked(v: u128) -> Self {
48 Self(NonZeroU128::new_unchecked(v))
49 }
50
51 #[inline]
53 #[must_use]
54 pub fn typ(self) -> UuidType {
55 self.as_uuid16().map_or(UuidType::NonSig, Uuid16::typ)
56 }
57
58 #[inline]
61 #[must_use]
62 pub fn as_uuid16(self) -> Option<Uuid16> {
63 self.as_u16().map(uuid16)
64 }
65
66 #[inline]
69 #[must_use]
70 pub fn as_u16(self) -> Option<u16> {
71 #[allow(clippy::cast_possible_truncation)]
72 let v = (self.0.get() >> SHIFT) as u16;
73 (self.0.get() & MASK_16 == BASE && v > 0).then_some(v)
74 }
75
76 #[inline]
79 #[must_use]
80 pub fn as_u32(self) -> Option<u32> {
81 let v = (self.0.get() >> SHIFT) as u32;
82 (self.0.get() & MASK_32 == BASE && v > u32::from(u16::MAX)).then_some(v)
83 }
84
85 #[inline]
88 #[must_use]
89 pub fn as_u128(self) -> Option<u128> {
90 (self.0.get() & MASK_32 != BASE).then_some(self.0.get())
91 }
92
93 #[inline]
95 #[must_use]
96 pub const fn to_bytes(self) -> [u8; Self::BYTES] {
97 self.0.get().to_le_bytes()
98 }
99}
100
101impl From<Uuid16> for Uuid {
102 #[inline]
103 fn from(u: Uuid16) -> Self {
104 u.as_uuid()
105 }
106}
107
108impl TryFrom<&[u8]> for Uuid {
109 type Error = ();
110
111 #[inline]
112 fn try_from(v: &[u8]) -> Result<Self, Self::Error> {
113 match v.len() {
114 Self::BYTES => Self::new(v.unpack().u128()),
115 Uuid16::BYTES => Uuid16::new(v.unpack().u16()).map(Uuid16::as_uuid),
116 _ => None,
117 }
118 .ok_or(())
119 }
120}
121
122impl Debug for Uuid {
123 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
124 #[allow(clippy::cast_possible_truncation)]
125 if let Some(v) = self.as_u16() {
126 write!(f, "{v:#06X}")
127 } else if let Some(v) = self.as_u32() {
128 write!(f, "{v:#010X}")
129 } else {
130 let v = self.0.get();
131 write!(
132 f,
133 "{:08X}-{:04X}-{:04X}-{:04X}-{:012X}",
134 (v >> 96) as u32,
135 (v >> 80) as u16,
136 (v >> 64) as u16,
137 (v >> 48) as u16,
138 (v & ((1 << 48) - 1)) as u64
139 )
140 }
141 }
142}
143
144impl Display for Uuid {
145 #[inline]
146 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
147 match self.typ() {
148 UuidType::NonSig => Debug::fmt(self, f),
149 typ => Debug::fmt(&typ, f),
150 }
151 }
152}
153
154impl From<Uuid> for u128 {
155 #[inline]
156 fn from(u: Uuid) -> Self {
157 u.0.get()
158 }
159}
160
161#[derive(Clone, Copy, Eq, Ord, PartialEq, PartialOrd)]
163#[repr(transparent)]
164pub struct Uuid16(NonZeroU16);
165
166impl Uuid16 {
167 pub const BYTES: usize = std::mem::size_of::<Self>();
169
170 #[inline]
172 #[must_use]
173 pub const fn new(v: u16) -> Option<Self> {
174 match NonZeroU16::new(v) {
175 Some(nz) => Some(Self(nz)),
176 None => None,
177 }
178 }
179
180 #[inline(always)]
182 pub fn typ(self) -> UuidType {
183 let u = self.0.get();
184 (unsafe { &*UUID_MAP.as_ptr().add((u >> 8) as _) })(u)
186 }
187
188 #[inline]
190 #[must_use]
191 pub const fn as_uuid(self) -> Uuid {
192 unsafe { Uuid::new_unchecked((self.0.get() as u128) << SHIFT | BASE) }
195 }
196
197 #[inline(always)]
199 #[must_use]
200 pub(crate) const fn raw(self) -> u16 {
201 self.0.get()
202 }
203
204 #[inline]
206 #[must_use]
207 pub const fn to_bytes(self) -> [u8; Self::BYTES] {
208 self.0.get().to_le_bytes()
209 }
210}
211
212impl Debug for Uuid16 {
213 #[inline]
214 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
215 write!(f, "{:#06X}", self.0.get())
216 }
217}
218
219impl Display for Uuid16 {
220 #[inline(always)]
221 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
222 Debug::fmt(&self.typ(), f)
223 }
224}
225
226#[allow(clippy::derived_hash_with_manual_eq)]
227impl Hash for Uuid16 {
228 #[inline]
229 fn hash<H: Hasher>(&self, state: &mut H) {
230 self.as_uuid().hash(state);
231 }
232}
233
234impl From<Uuid16> for u16 {
235 #[inline]
236 fn from(u: Uuid16) -> Self {
237 u.raw()
238 }
239}
240
241#[derive(Clone, Copy, Eq, Ord, PartialEq, PartialOrd)]
243#[non_exhaustive]
244pub enum UuidType {
245 Protocol(u16),
246 ServiceClass(ServiceClass),
247 Service(Service),
248 Unit(Unit),
249 Declaration(Declaration),
250 Descriptor(Descriptor),
251 Characteristic(Characteristic),
252 Member(u16),
255 Unknown(u16),
256 NonSig,
257}
258
259impl From<Uuid> for UuidType {
260 #[inline(always)]
261 fn from(u: Uuid) -> Self {
262 u.typ()
263 }
264}
265
266impl From<Uuid16> for UuidType {
267 #[inline(always)]
268 fn from(u: Uuid16) -> Self {
269 u.typ()
270 }
271}
272
273type UuidMap = [fn(u16) -> UuidType; 256];
274
275static UUID_MAP: UuidMap = {
276 use UuidType::*;
277 #[inline(always)]
278 fn is<T: TryFromPrimitive<Primitive = u16>>(u: u16, f: impl FnOnce(T) -> UuidType) -> UuidType {
279 T::try_from_primitive(u).map_or(Unknown(u), f)
280 }
281 let mut m: UuidMap = [Unknown; 256];
282 m[0x00] = Protocol;
283 m[0x01] = Protocol;
284 m[0x10] = |u| is(u, ServiceClass);
285 m[0x11] = |u| is(u, ServiceClass);
286 m[0x12] = |u| is(u, ServiceClass);
287 m[0x13] = |u| is(u, ServiceClass);
288 m[0x14] = |u| is(u, ServiceClass);
289 m[0x18] = |u| is(u, Service);
290 m[0x27] = |u| is(u, Unit);
291 m[0x28] = |u| is(u, Declaration);
292 m[0x29] = |u| is(u, Descriptor);
293 m[0x2A] = |u| is(u, Characteristic);
294 m[0x2B] = |u| is(u, Characteristic);
295 m[0xFC] = Member;
296 m[0xFD] = Member;
297 m[0xFE] = Member;
298 m
299};
300
301impl Debug for UuidType {
302 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
303 use UuidType::*;
304 match *self {
305 Protocol(u) => (f.debug_tuple("Protocol").field(&format_args!("{u:#06X}"))).finish(),
306 ServiceClass(ref u) => f.debug_tuple("ServiceClass").field(u).finish(),
307 Service(ref u) => f.debug_tuple("Service").field(u).finish(),
308 Unit(ref u) => f.debug_tuple("Unit").field(u).finish(),
309 Declaration(ref u) => f.debug_tuple("Declaration").field(u).finish(),
310 Descriptor(ref u) => f.debug_tuple("Descriptor").field(u).finish(),
311 Characteristic(ref u) => f.debug_tuple("Characteristic").field(u).finish(),
312 Member(id) => f.debug_tuple("Company").field(&id).finish(),
313 Unknown(u) => (f.debug_tuple("Unknown").field(&format_args!("{u:#06X}"))).finish(),
314 NonSig => f.write_str("NonSig"),
315 }
316 }
317}
318
319impl Display for UuidType {
320 #[inline(always)]
321 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
322 Debug::fmt(self, f)
323 }
324}
325
326#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
328pub struct UuidVec {
329 n: u8,
330 v: [u8; Uuid::BYTES],
331}
332
333impl UuidVec {
334 #[inline]
336 #[must_use]
337 pub fn new(u: Uuid) -> Self {
338 let (n, v) = u.as_uuid16().map_or_else(
339 || (Uuid::BYTES, u.to_bytes()),
340 |u| {
341 let mut v = [0; Uuid::BYTES];
342 v[..Uuid16::BYTES].copy_from_slice(&u.to_bytes());
343 (Uuid16::BYTES, v)
344 },
345 );
346 #[allow(clippy::cast_possible_truncation)]
347 Self { n: n as _, v }
348 }
349}
350
351impl Deref for UuidVec {
352 type Target = [u8];
353
354 #[inline(always)]
355 fn deref(&self) -> &Self::Target {
356 unsafe { &*ptr::slice_from_raw_parts(self.v.as_ptr().cast(), self.n as _) }
358 }
359}
360
361pub trait UuidPacker {
363 fn uuid(&mut self, u: impl Into<Uuid>);
364}
365
366impl UuidPacker for Packer<'_> {
367 #[inline]
369 fn uuid(&mut self, u: impl Into<Uuid>) {
370 let u = u.into();
371 match u.as_u16() {
372 Some(u) => self.u16(u),
373 None => self.u128(u),
374 };
375 }
376}
377
378#[inline]
380#[must_use]
381const fn uuid16(v: u16) -> Uuid16 {
382 Uuid16(unsafe { NonZeroU16::new_unchecked(v) })
384}
385
386macro_rules! uuid16_enum {
388 (
389 $(#[$outer:meta])*
390 $vis:vis enum $typ:ident {
391 $($item:ident = $uuid:literal,)+
392 }
393 ) => {
394 $(#[$outer])*
395 #[derive(
396 Clone,
397 Copy,
398 Debug,
399 Eq,
400 Ord,
401 PartialEq,
402 PartialOrd,
403 ::num_enum::IntoPrimitive,
404 ::num_enum::TryFromPrimitive,
405 )]
406 #[cfg_attr(test, derive(enum_iterator::Sequence))]
407 #[non_exhaustive]
408 #[repr(u16)]
409 $vis enum $typ {
410 $($item = $uuid,)+
411 }
412
413 impl $typ {
414 ::paste::paste! {$(
415 pub const [<$item:snake:upper>]: $crate::Uuid16 = Self::$item.uuid16();
416 )+}
417
418 #[inline]
420 #[must_use]
421 pub const fn uuid(self) -> $crate::Uuid {
422 self.uuid16().as_uuid()
423 }
424
425 #[inline(always)]
427 #[must_use]
428 pub const fn uuid16(self) -> $crate::Uuid16 {
429 uuid16(self as _)
430 }
431 }
432
433 impl ::core::fmt::Display for $typ {
434 #[inline(always)]
435 fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
436 ::core::fmt::Debug::fmt(self, f)
437 }
438 }
439
440 impl ::core::convert::TryFrom<$crate::Uuid16> for $typ {
441 type Error = ::num_enum::TryFromPrimitiveError<Self>;
442
443 #[inline]
444 fn try_from(u: $crate::Uuid16) -> Result<Self, Self::Error> {
445 use ::num_enum::TryFromPrimitive;
446 Self::try_from_primitive(u.raw())
447 }
448 }
449
450 impl ::core::cmp::PartialEq<$crate::Uuid> for $typ {
451 #[inline(always)]
452 fn eq(&self, rhs: &$crate::Uuid) -> bool {
453 self.uuid() == *rhs
455 }
456 }
457
458 impl ::core::cmp::PartialEq<$crate::Uuid16> for $typ {
459 #[inline(always)]
460 fn eq(&self, rhs: &$crate::Uuid16) -> bool {
461 *self as u16 == rhs.raw()
462 }
463 }
464
465 impl ::core::cmp::PartialEq<$typ> for $crate::Uuid {
466 #[inline(always)]
467 fn eq(&self, rhs: &$typ) -> bool {
468 *self == rhs.uuid()
469 }
470 }
471
472 impl ::core::cmp::PartialEq<$typ> for $crate::Uuid16 {
473 #[inline(always)]
474 fn eq(&self, rhs: &$typ) -> bool {
475 self.raw() == *rhs as u16
476 }
477 }
478
479 impl ::core::convert::From<$typ> for $crate::Uuid {
480 #[inline]
481 fn from(v: $typ) -> Self {
482 v.uuid()
483 }
484 }
485
486 impl ::core::convert::From<$typ> for $crate::Uuid16 {
487 #[inline]
488 fn from(v: $typ) -> Self {
489 v.uuid16()
490 }
491 }
492 }
493}
494
495include!("uuid16.rs");
496
497#[cfg(test)]
498mod tests {
499 use enum_iterator::all;
500
501 use super::*;
502
503 #[test]
504 fn uuid_type() {
505 assert_eq!(uuid16(0x0001).typ(), UuidType::Protocol(0x0001));
506 for v in all::<ServiceClass>() {
507 assert_eq!(v.uuid16().typ(), UuidType::ServiceClass(v));
508 }
509 for v in all::<Service>() {
510 assert_eq!(v.uuid16().typ(), UuidType::Service(v));
511 }
512 for v in all::<Unit>() {
513 assert_eq!(v.uuid16().typ(), UuidType::Unit(v));
514 }
515 for v in all::<Declaration>() {
516 assert_eq!(v.uuid16().typ(), UuidType::Declaration(v));
517 }
518 for v in all::<Descriptor>() {
519 assert_eq!(v.uuid16().typ(), UuidType::Descriptor(v));
520 }
521 for v in all::<Characteristic>() {
522 assert_eq!(v.uuid16().typ(), UuidType::Characteristic(v));
523 }
524 assert_eq!(uuid16(0xFEFF).typ(), UuidType::Member(0xFEFF));
525 assert_eq!(uuid16(0xFFFF).typ(), UuidType::Unknown(0xFFFF));
526 }
527}