uguid/
guid.rs

1// Copyright 2022 Google LLC
2//
3// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
4// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
5// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
6// option. This file may not be copied, modified, or distributed
7// except according to those terms.
8
9use crate::util::{byte_to_ascii_hex_lower, parse_byte_from_ascii_str_at};
10use crate::GuidFromStrError;
11use core::fmt::{self, Display, Formatter};
12use core::str::{self, FromStr};
13
14#[cfg(feature = "serde")]
15use {
16    serde::de::{self, Visitor},
17    serde::{Deserialize, Deserializer, Serialize, Serializer},
18};
19
20#[cfg(feature = "bytemuck")]
21use bytemuck::{Pod, Zeroable};
22
23/// Globally-unique identifier.
24///
25/// The format is defined in [RFC 4122]. However, unlike "normal" UUIDs
26/// (such as those provided by the [`uuid`] crate), the first three
27/// fields are little-endian. See also [Appendix A] of the UEFI
28/// Specification.
29///
30/// This type is 4-byte aligned. The UEFI Specification says the GUID
31/// type should be 8-byte aligned, but most C implementations have
32/// 4-byte alignment, so we do the same here for compatibility.
33///
34/// [Appendix A]: https://uefi.org/specs/UEFI/2.10/Apx_A_GUID_and_Time_Formats.html
35/// [RFC 4122]: https://datatracker.ietf.org/doc/html/rfc4122
36/// [`uuid`]: https://docs.rs/uuid/latest/uuid
37#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)]
38#[cfg_attr(feature = "bytemuck", derive(Pod, Zeroable))]
39#[repr(C)]
40pub struct Guid {
41    // Use `u32` rather than `[u8; 4]` here so that the natural
42    // alignment of the struct is four bytes. This is better for the end
43    // user than setting `repr(align(4))` because it doesn't prevent use
44    // of the type in a `repr(packed)` struct. For more discussion, see
45    // https://github.com/rust-lang/rfcs/pull/1358#issuecomment-217582887
46    time_low: u32,
47    time_mid: [u8; 2],
48    time_high_and_version: [u8; 2],
49    clock_seq_high_and_reserved: u8,
50    clock_seq_low: u8,
51    node: [u8; 6],
52}
53
54impl Guid {
55    /// GUID with all fields set to zero.
56    pub const ZERO: Self = Self {
57        time_low: 0,
58        time_mid: [0, 0],
59        time_high_and_version: [0, 0],
60        clock_seq_high_and_reserved: 0,
61        clock_seq_low: 0,
62        node: [0; 6],
63    };
64
65    /// Create a new GUID.
66    #[must_use]
67    pub const fn new(
68        time_low: [u8; 4],
69        time_mid: [u8; 2],
70        time_high_and_version: [u8; 2],
71        clock_seq_high_and_reserved: u8,
72        clock_seq_low: u8,
73        node: [u8; 6],
74    ) -> Self {
75        Self {
76            time_low: u32::from_ne_bytes([
77                time_low[0],
78                time_low[1],
79                time_low[2],
80                time_low[3],
81            ]),
82            time_mid: [time_mid[0], time_mid[1]],
83            time_high_and_version: [
84                time_high_and_version[0],
85                time_high_and_version[1],
86            ],
87            clock_seq_high_and_reserved,
88            clock_seq_low,
89            node,
90        }
91    }
92
93    /// Create a version 4 GUID from provided random bytes.
94    ///
95    /// See [RFC 4122 section 4.4][rfc] for the definition of a version
96    /// 4 GUID.
97    ///
98    /// This constructor does not itself generate random bytes, but
99    /// instead expects the caller to provide suitably random bytes.
100    ///
101    /// # Example
102    ///
103    /// ```
104    /// use uguid::{Guid, Variant};
105    ///
106    /// let guid = Guid::from_random_bytes([
107    ///     104, 192, 95, 215, 120, 33, 249, 1, 102, 21, 171, 84, 233, 204, 68, 176,
108    /// ]);
109    /// assert_eq!(guid.variant(), Variant::Rfc4122);
110    /// assert_eq!(guid.version(), 4);
111    /// ```
112    ///
113    /// [rfc]: https://datatracker.ietf.org/doc/html/rfc4122#section-4.4
114    #[must_use]
115    pub const fn from_random_bytes(mut random_bytes: [u8; 16]) -> Self {
116        // Set the variant in byte 8: set bit 7, clear bit 6.
117        random_bytes[8] &= 0b1011_1111;
118        random_bytes[8] |= 0b1000_0000;
119        // Set the version in byte 7: set the most-significant-nibble to 4.
120        random_bytes[7] &= 0b0000_1111;
121        random_bytes[7] |= 0b0100_1111;
122
123        Self::from_bytes(random_bytes)
124    }
125
126    /// True if all bits are zero, false otherwise.
127    ///
128    /// # Example
129    ///
130    /// ```
131    /// use uguid::guid;
132    ///
133    /// assert!(guid!("00000000-0000-0000-0000-000000000000").is_zero());
134    /// assert!(!guid!("308bbc16-a308-47e8-8977-5e5646c5291f").is_zero());
135    /// ```
136    #[must_use]
137    pub const fn is_zero(self) -> bool {
138        let b = self.to_bytes();
139        b[0] == 0
140            && b[1] == 0
141            && b[2] == 0
142            && b[3] == 0
143            && b[4] == 0
144            && b[5] == 0
145            && b[6] == 0
146            && b[7] == 0
147            && b[8] == 0
148            && b[9] == 0
149            && b[10] == 0
150            && b[11] == 0
151            && b[12] == 0
152            && b[13] == 0
153            && b[14] == 0
154            && b[15] == 0
155    }
156
157    /// The little-endian low field of the timestamp.
158    #[must_use]
159    pub const fn time_low(self) -> [u8; 4] {
160        self.time_low.to_ne_bytes()
161    }
162
163    /// The little-endian middle field of the timestamp.
164    #[must_use]
165    pub const fn time_mid(self) -> [u8; 2] {
166        self.time_mid
167    }
168
169    /// The little-endian high field of the timestamp multiplexed with
170    /// the version number.
171    #[must_use]
172    pub const fn time_high_and_version(self) -> [u8; 2] {
173        self.time_high_and_version
174    }
175
176    /// The high field of the clock sequence multiplexed with the
177    /// variant.
178    #[must_use]
179    pub const fn clock_seq_high_and_reserved(self) -> u8 {
180        self.clock_seq_high_and_reserved
181    }
182
183    /// The low field of the clock sequence.
184    #[must_use]
185    pub const fn clock_seq_low(self) -> u8 {
186        self.clock_seq_low
187    }
188
189    /// The spatially unique node identifier.
190    #[must_use]
191    pub const fn node(self) -> [u8; 6] {
192        self.node
193    }
194
195    /// Get the GUID variant.
196    ///
197    /// # Example
198    ///
199    /// ```
200    /// use uguid::{guid, Variant};
201    ///
202    /// assert_eq!(
203    ///     guid!("308bbc16-a308-47e8-8977-5e5646c5291f").variant(),
204    ///     Variant::Rfc4122
205    /// );
206    /// ```
207    #[must_use]
208    pub const fn variant(self) -> Variant {
209        // Get the 3 most significant bits of `clock_seq_high_and_reserved`.
210        let bits = (self.clock_seq_high_and_reserved & 0b1110_0000) >> 5;
211
212        if (bits & 0b100) == 0 {
213            Variant::ReservedNcs
214        } else if (bits & 0b010) == 0 {
215            Variant::Rfc4122
216        } else if (bits & 0b001) == 0 {
217            Variant::ReservedMicrosoft
218        } else {
219            Variant::ReservedFuture
220        }
221    }
222
223    /// Get the GUID version. This is a sub-type of the variant as
224    /// defined in [RFC4122].
225    ///
226    /// [RFC4122]: https://datatracker.ietf.org/doc/html/rfc4122#section-4.1.3
227    ///
228    /// # Example
229    ///
230    /// ```
231    /// use uguid::guid;
232    ///
233    /// assert_eq!(guid!("308bbc16-a308-47e8-8977-5e5646c5291f").version(), 4);
234    /// ```
235    #[must_use]
236    pub const fn version(self) -> u8 {
237        (self.time_high_and_version[1] & 0b1111_0000) >> 4
238    }
239
240    /// Parse a GUID from a string.
241    ///
242    /// This is functionally the same as [`Self::from_str`], but is
243    /// exposed separately to provide a `const` method for parsing.
244    pub const fn try_parse(s: &str) -> Result<Self, GuidFromStrError> {
245        // Treat input as ASCII.
246        let s = s.as_bytes();
247
248        if s.len() != 36 {
249            return Err(GuidFromStrError::Length);
250        }
251
252        let sep = b'-';
253        if s[8] != sep {
254            return Err(GuidFromStrError::Separator(8));
255        }
256        if s[13] != sep {
257            return Err(GuidFromStrError::Separator(13));
258        }
259        if s[18] != sep {
260            return Err(GuidFromStrError::Separator(18));
261        }
262        if s[23] != sep {
263            return Err(GuidFromStrError::Separator(23));
264        }
265
266        Ok(Self::from_bytes([
267            mtry!(parse_byte_from_ascii_str_at(s, 6)),
268            mtry!(parse_byte_from_ascii_str_at(s, 4)),
269            mtry!(parse_byte_from_ascii_str_at(s, 2)),
270            mtry!(parse_byte_from_ascii_str_at(s, 0)),
271            mtry!(parse_byte_from_ascii_str_at(s, 11)),
272            mtry!(parse_byte_from_ascii_str_at(s, 9)),
273            mtry!(parse_byte_from_ascii_str_at(s, 16)),
274            mtry!(parse_byte_from_ascii_str_at(s, 14)),
275            mtry!(parse_byte_from_ascii_str_at(s, 19)),
276            mtry!(parse_byte_from_ascii_str_at(s, 21)),
277            mtry!(parse_byte_from_ascii_str_at(s, 24)),
278            mtry!(parse_byte_from_ascii_str_at(s, 26)),
279            mtry!(parse_byte_from_ascii_str_at(s, 28)),
280            mtry!(parse_byte_from_ascii_str_at(s, 30)),
281            mtry!(parse_byte_from_ascii_str_at(s, 32)),
282            mtry!(parse_byte_from_ascii_str_at(s, 34)),
283        ]))
284    }
285
286    /// Parse a GUID from a string, panicking on failure.
287    ///
288    /// The input must be in "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
289    /// format, where each `x` is a hex digit (any of `0-9`, `a-f`, or
290    /// `A-F`).
291    ///
292    /// This function is marked `track_caller` so that error messages
293    /// point directly to the invalid GUID string.
294    ///
295    /// # Panics
296    ///
297    /// This function will panic if the input is not in the format shown
298    /// above. In particular, it will panic if the input is not exactly
299    /// 36 bytes long, or if the input does not have separators at the
300    /// expected positions, or if any of the remaining characters are
301    /// not valid hex digits.
302    #[must_use]
303    #[track_caller]
304    pub const fn parse_or_panic(s: &str) -> Self {
305        match Self::try_parse(s) {
306            Ok(g) => g,
307            Err(GuidFromStrError::Length) => {
308                panic!("GUID string has wrong length (expected 36 bytes)");
309            }
310            Err(GuidFromStrError::Separator(_)) => {
311                panic!("GUID string is missing one or more separators (`-`)");
312            }
313            Err(GuidFromStrError::Hex(_)) => {
314                panic!("GUID string contains one or more invalid characters");
315            }
316        }
317    }
318
319    /// Create a GUID from a 16-byte array. No changes to byte order are made.
320    #[must_use]
321    pub const fn from_bytes(bytes: [u8; 16]) -> Self {
322        Self::new(
323            [bytes[0], bytes[1], bytes[2], bytes[3]],
324            [bytes[4], bytes[5]],
325            [bytes[6], bytes[7]],
326            bytes[8],
327            bytes[9],
328            [
329                bytes[10], bytes[11], bytes[12], bytes[13], bytes[14],
330                bytes[15],
331            ],
332        )
333    }
334
335    /// Convert to a 16-byte array.
336    #[must_use]
337    pub const fn to_bytes(self) -> [u8; 16] {
338        let time_low = self.time_low();
339
340        [
341            time_low[0],
342            time_low[1],
343            time_low[2],
344            time_low[3],
345            self.time_mid[0],
346            self.time_mid[1],
347            self.time_high_and_version[0],
348            self.time_high_and_version[1],
349            self.clock_seq_high_and_reserved,
350            self.clock_seq_low,
351            self.node[0],
352            self.node[1],
353            self.node[2],
354            self.node[3],
355            self.node[4],
356            self.node[5],
357        ]
358    }
359
360    /// Convert to a lower-case hex ASCII string.
361    ///
362    /// The output is in "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" format.
363    #[must_use]
364    pub const fn to_ascii_hex_lower(self) -> [u8; 36] {
365        let bytes = self.to_bytes();
366
367        let mut buf = [0; 36];
368        (buf[0], buf[1]) = byte_to_ascii_hex_lower(bytes[3]);
369        (buf[2], buf[3]) = byte_to_ascii_hex_lower(bytes[2]);
370        (buf[4], buf[5]) = byte_to_ascii_hex_lower(bytes[1]);
371        (buf[6], buf[7]) = byte_to_ascii_hex_lower(bytes[0]);
372        buf[8] = b'-';
373        (buf[9], buf[10]) = byte_to_ascii_hex_lower(bytes[5]);
374        (buf[11], buf[12]) = byte_to_ascii_hex_lower(bytes[4]);
375        buf[13] = b'-';
376        (buf[14], buf[15]) = byte_to_ascii_hex_lower(bytes[7]);
377        (buf[16], buf[17]) = byte_to_ascii_hex_lower(bytes[6]);
378        buf[18] = b'-';
379        (buf[19], buf[20]) = byte_to_ascii_hex_lower(bytes[8]);
380        (buf[21], buf[22]) = byte_to_ascii_hex_lower(bytes[9]);
381        buf[23] = b'-';
382        (buf[24], buf[25]) = byte_to_ascii_hex_lower(bytes[10]);
383        (buf[26], buf[27]) = byte_to_ascii_hex_lower(bytes[11]);
384        (buf[28], buf[29]) = byte_to_ascii_hex_lower(bytes[12]);
385        (buf[30], buf[31]) = byte_to_ascii_hex_lower(bytes[13]);
386        (buf[32], buf[33]) = byte_to_ascii_hex_lower(bytes[14]);
387        (buf[34], buf[35]) = byte_to_ascii_hex_lower(bytes[15]);
388        buf
389    }
390}
391
392impl Default for Guid {
393    fn default() -> Self {
394        Self::ZERO
395    }
396}
397
398impl Display for Guid {
399    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
400        let ascii = self.to_ascii_hex_lower();
401        // OK to unwrap since the ascii output is valid utf-8.
402        let s = str::from_utf8(&ascii).unwrap();
403        f.write_str(s)
404    }
405}
406
407impl FromStr for Guid {
408    type Err = GuidFromStrError;
409
410    /// Parse a GUID from a string, panicking on failure.
411    ///
412    /// The input must be in "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
413    /// format, where each `x` is a hex digit (any of `0-9`, `a-f`, or
414    /// `A-F`).
415    fn from_str(s: &str) -> Result<Self, Self::Err> {
416        Self::try_parse(s)
417    }
418}
419
420#[cfg(feature = "serde")]
421impl Serialize for Guid {
422    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
423    where
424        S: Serializer,
425    {
426        let ascii = self.to_ascii_hex_lower();
427        // OK to unwrap since the ascii output is valid utf-8.
428        let s = str::from_utf8(&ascii).unwrap();
429        serializer.serialize_str(s)
430    }
431}
432
433#[cfg(feature = "serde")]
434struct DeserializerVisitor;
435
436#[cfg(feature = "serde")]
437impl<'de> Visitor<'de> for DeserializerVisitor {
438    type Value = Guid;
439
440    fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
441        formatter.write_str(
442            "a string in the format \"xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx\"",
443        )
444    }
445
446    fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
447    where
448        E: de::Error,
449    {
450        Guid::try_parse(value).map_err(E::custom)
451    }
452}
453
454#[cfg(feature = "serde")]
455impl<'de> Deserialize<'de> for Guid {
456    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
457    where
458        D: Deserializer<'de>,
459    {
460        deserializer.deserialize_str(DeserializerVisitor)
461    }
462}
463
464/// Variant or type of GUID, as defined in [RFC4122].
465///
466/// [RFC4122]: https://datatracker.ietf.org/doc/html/rfc4122#section-4.1.3
467#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd)]
468pub enum Variant {
469    /// Reserved, NCS backward compatibility.
470    ReservedNcs,
471
472    /// The GUID variant described by RFC4122.
473    Rfc4122,
474
475    /// Reserved, Microsoft Corporation backward compatibility.
476    ReservedMicrosoft,
477
478    /// Reserved for future use.
479    ReservedFuture,
480}