burble_const/
uuid.rs

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/// 16-, 32-, or 128-bit UUID ([Vol 3] Part B, Section 2.5.1).
16#[derive(Clone, Copy, Eq, Hash, Ord, PartialEq, PartialOrd)]
17#[repr(transparent)]
18pub struct Uuid(NonZeroU128);
19
20impl Uuid {
21    /// UUID size in bytes.
22    pub const BYTES: usize = std::mem::size_of::<Self>();
23    /// Maximum UUID value.
24    pub const MAX: Self = Self(
25        // SAFETY: Non-zero
26        unsafe { NonZeroU128::new_unchecked(u128::MAX) },
27    );
28
29    /// Creates a UUID from a `u128`.
30    #[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    /// Creates a UUID from a `u128` without checking whether the value is
40    /// non-zero.
41    ///
42    /// # Safety
43    ///
44    /// The value must not be zero.
45    #[inline]
46    #[must_use]
47    pub const unsafe fn new_unchecked(v: u128) -> Self {
48        Self(NonZeroU128::new_unchecked(v))
49    }
50
51    /// Returns the UUID type. Returns [`UuidType::NonSig`] for non-SIG UUID.
52    #[inline]
53    #[must_use]
54    pub fn typ(self) -> UuidType {
55        self.as_uuid16().map_or(UuidType::NonSig, Uuid16::typ)
56    }
57
58    /// Returns a [`Uuid16`] representation or [`None`] if the UUID is not an
59    /// assigned 16-bit UUID.
60    #[inline]
61    #[must_use]
62    pub fn as_uuid16(self) -> Option<Uuid16> {
63        self.as_u16().map(uuid16)
64    }
65
66    /// Converts an assigned 16-bit Bluetooth SIG UUID to `u16`. This is
67    /// mutually exclusive with `as_u32` and `as_u128`.
68    #[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    /// Converts an assigned 32-bit Bluetooth SIG UUID to `u32`. This is
77    /// mutually exclusive with `as_u16` and `as_u128`.
78    #[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    /// Converts an unassigned UUID to `u128`. This is mutually exclusive with
86    /// `as_u16` and `as_u32`.
87    #[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    /// Returns the UUID as a little-endian byte array.
94    #[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/// 16-bit Bluetooth SIG UUID.
162#[derive(Clone, Copy, Eq, Ord, PartialEq, PartialOrd)]
163#[repr(transparent)]
164pub struct Uuid16(NonZeroU16);
165
166impl Uuid16 {
167    /// UUID size in bytes.
168    pub const BYTES: usize = std::mem::size_of::<Self>();
169
170    /// Creates a 16-bit SIG UUID from a `u16`.
171    #[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    /// Returns the UUID type.
181    #[inline(always)]
182    pub fn typ(self) -> UuidType {
183        let u = self.0.get();
184        // SAFETY: UUID_MAP has 256 entries
185        (unsafe { &*UUID_MAP.as_ptr().add((u >> 8) as _) })(u)
186    }
187
188    /// Returns 128-bit UUID representation.
189    #[inline]
190    #[must_use]
191    pub const fn as_uuid(self) -> Uuid {
192        // TODO: Use NonZeroU128::from() when it is const
193        // SAFETY: Always non-zero
194        unsafe { Uuid::new_unchecked((self.0.get() as u128) << SHIFT | BASE) }
195    }
196
197    /// Returns the raw 16-bit UUID value.
198    #[inline(always)]
199    #[must_use]
200    pub(crate) const fn raw(self) -> u16 {
201        self.0.get()
202    }
203
204    /// Returns the UUID as a little-endian byte array.
205    #[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/// 16-bit UUID type.
242#[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    // TODO: Are Member Service UUIDs used anywhere?
253    // ([Assigned Numbers] Section 3.11)
254    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/// An owned little-endian vector representation of a UUID.
327#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
328pub struct UuidVec {
329    n: u8,
330    v: [u8; Uuid::BYTES],
331}
332
333impl UuidVec {
334    /// Creates a vector representation of a UUID.
335    #[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        // SAFETY: `n` is 0, 2, or 16
357        unsafe { &*ptr::slice_from_raw_parts(self.v.as_ptr().cast(), self.n as _) }
358    }
359}
360
361/// Packer extension functions.
362pub trait UuidPacker {
363    fn uuid(&mut self, u: impl Into<Uuid>);
364}
365
366impl UuidPacker for Packer<'_> {
367    /// Writes either a 16- or a 128-bit UUID at the current index.
368    #[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/// Creates an assigned 16-bit SIG UUID from a `u16`.
379#[inline]
380#[must_use]
381const fn uuid16(v: u16) -> Uuid16 {
382    // SAFETY: All crate uses guarantee that v != 0
383    Uuid16(unsafe { NonZeroU16::new_unchecked(v) })
384}
385
386/// Provides implementations for a 16-bit UUID enum.
387macro_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            /// Returns the `Uuid` representation of the variant.
419            #[inline]
420            #[must_use]
421            pub const fn uuid(self) -> $crate::Uuid {
422                self.uuid16().as_uuid()
423            }
424
425            /// Returns the `Uuid16` representation of the variant.
426            #[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                // Converting to 128-bit avoids branches
454                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}