// Copyright 2022 Google LLC // // Licensed under the Apache License, Version 2.0 or the MIT license // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. use crate::util::{byte_to_ascii_hex_lower, parse_byte_from_ascii_str_at}; use crate::GuidFromStrError; use core::fmt::{self, Display, Formatter}; use core::str::{self, FromStr}; #[cfg(feature = "serde")] use { serde::de::{self, Visitor}, serde::{Deserialize, Deserializer, Serialize, Serializer}, }; #[cfg(feature = "bytemuck")] use bytemuck::{Pod, Zeroable}; /// Globally-unique identifier. /// /// The format is defined in [RFC 4122]. However, unlike "normal" UUIDs /// (such as those provided by the [`uuid`] crate), the first three /// fields are little-endian. See also [Appendix A] of the UEFI /// Specification. /// /// This type is 4-byte aligned. The UEFI Specification says the GUID /// type should be 8-byte aligned, but most C implementations have /// 4-byte alignment, so we do the same here for compatibility. /// /// [Appendix A]: https://uefi.org/specs/UEFI/2.10/Apx_A_GUID_and_Time_Formats.html /// [RFC 4122]: https://datatracker.ietf.org/doc/html/rfc4122 /// [`uuid`]: https://docs.rs/uuid/latest/uuid #[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)] #[cfg_attr(feature = "bytemuck", derive(Pod, Zeroable))] #[repr(C)] pub struct Guid { // Use `u32` rather than `[u8; 4]` here so that the natural // alignment of the struct is four bytes. This is better for the end // user than setting `repr(align(4))` because it doesn't prevent use // of the type in a `repr(packed)` struct. For more discussion, see // https://github.com/rust-lang/rfcs/pull/1358#issuecomment-217582887 time_low: u32, time_mid: [u8; 2], time_high_and_version: [u8; 2], clock_seq_high_and_reserved: u8, clock_seq_low: u8, node: [u8; 6], } impl Guid { /// GUID with all fields set to zero. pub const ZERO: Self = Self { time_low: 0, time_mid: [0, 0], time_high_and_version: [0, 0], clock_seq_high_and_reserved: 0, clock_seq_low: 0, node: [0; 6], }; /// Create a new GUID. #[must_use] pub const fn new( time_low: [u8; 4], time_mid: [u8; 2], time_high_and_version: [u8; 2], clock_seq_high_and_reserved: u8, clock_seq_low: u8, node: [u8; 6], ) -> Self { Self { time_low: u32::from_ne_bytes([ time_low[0], time_low[1], time_low[2], time_low[3], ]), time_mid: [time_mid[0], time_mid[1]], time_high_and_version: [ time_high_and_version[0], time_high_and_version[1], ], clock_seq_high_and_reserved, clock_seq_low, node, } } /// Create a version 4 GUID from provided random bytes. /// /// See [RFC 4122 section 4.4][rfc] for the definition of a version /// 4 GUID. /// /// This constructor does not itself generate random bytes, but /// instead expects the caller to provide suitably random bytes. /// /// # Example /// /// ``` /// use uguid::{Guid, Variant}; /// /// let guid = Guid::from_random_bytes([ /// 104, 192, 95, 215, 120, 33, 249, 1, 102, 21, 171, 84, 233, 204, 68, 176, /// ]); /// assert_eq!(guid.variant(), Variant::Rfc4122); /// assert_eq!(guid.version(), 4); /// ``` /// /// [rfc]: https://datatracker.ietf.org/doc/html/rfc4122#section-4.4 #[must_use] pub const fn from_random_bytes(mut random_bytes: [u8; 16]) -> Self { // Set the variant in byte 8: set bit 7, clear bit 6. random_bytes[8] &= 0b1011_1111; random_bytes[8] |= 0b1000_0000; // Set the version in byte 7: set the most-significant-nibble to 4. random_bytes[7] &= 0b0000_1111; random_bytes[7] |= 0b0100_1111; Self::from_bytes(random_bytes) } /// True if all bits are zero, false otherwise. /// /// # Example /// /// ``` /// use uguid::guid; /// /// assert!(guid!("00000000-0000-0000-0000-000000000000").is_zero()); /// assert!(!guid!("308bbc16-a308-47e8-8977-5e5646c5291f").is_zero()); /// ``` #[must_use] pub const fn is_zero(self) -> bool { let b = self.to_bytes(); b[0] == 0 && b[1] == 0 && b[2] == 0 && b[3] == 0 && b[4] == 0 && b[5] == 0 && b[6] == 0 && b[7] == 0 && b[8] == 0 && b[9] == 0 && b[10] == 0 && b[11] == 0 && b[12] == 0 && b[13] == 0 && b[14] == 0 && b[15] == 0 } /// The little-endian low field of the timestamp. #[must_use] pub const fn time_low(self) -> [u8; 4] { self.time_low.to_ne_bytes() } /// The little-endian middle field of the timestamp. #[must_use] pub const fn time_mid(self) -> [u8; 2] { self.time_mid } /// The little-endian high field of the timestamp multiplexed with /// the version number. #[must_use] pub const fn time_high_and_version(self) -> [u8; 2] { self.time_high_and_version } /// The high field of the clock sequence multiplexed with the /// variant. #[must_use] pub const fn clock_seq_high_and_reserved(self) -> u8 { self.clock_seq_high_and_reserved } /// The low field of the clock sequence. #[must_use] pub const fn clock_seq_low(self) -> u8 { self.clock_seq_low } /// The spatially unique node identifier. #[must_use] pub const fn node(self) -> [u8; 6] { self.node } /// Get the GUID variant. /// /// # Example /// /// ``` /// use uguid::{guid, Variant}; /// /// assert_eq!( /// guid!("308bbc16-a308-47e8-8977-5e5646c5291f").variant(), /// Variant::Rfc4122 /// ); /// ``` #[must_use] pub const fn variant(self) -> Variant { // Get the 3 most significant bits of `clock_seq_high_and_reserved`. let bits = (self.clock_seq_high_and_reserved & 0b1110_0000) >> 5; if (bits & 0b100) == 0 { Variant::ReservedNcs } else if (bits & 0b010) == 0 { Variant::Rfc4122 } else if (bits & 0b001) == 0 { Variant::ReservedMicrosoft } else { Variant::ReservedFuture } } /// Get the GUID version. This is a sub-type of the variant as /// defined in [RFC4122]. /// /// [RFC4122]: https://datatracker.ietf.org/doc/html/rfc4122#section-4.1.3 /// /// # Example /// /// ``` /// use uguid::guid; /// /// assert_eq!(guid!("308bbc16-a308-47e8-8977-5e5646c5291f").version(), 4); /// ``` #[must_use] pub const fn version(self) -> u8 { (self.time_high_and_version[1] & 0b1111_0000) >> 4 } /// Parse a GUID from a string. /// /// This is functionally the same as [`Self::from_str`], but is /// exposed separately to provide a `const` method for parsing. pub const fn try_parse(s: &str) -> Result { // Treat input as ASCII. let s = s.as_bytes(); if s.len() != 36 { return Err(GuidFromStrError::Length); } let sep = b'-'; if s[8] != sep { return Err(GuidFromStrError::Separator(8)); } if s[13] != sep { return Err(GuidFromStrError::Separator(13)); } if s[18] != sep { return Err(GuidFromStrError::Separator(18)); } if s[23] != sep { return Err(GuidFromStrError::Separator(23)); } Ok(Self::from_bytes([ mtry!(parse_byte_from_ascii_str_at(s, 6)), mtry!(parse_byte_from_ascii_str_at(s, 4)), mtry!(parse_byte_from_ascii_str_at(s, 2)), mtry!(parse_byte_from_ascii_str_at(s, 0)), mtry!(parse_byte_from_ascii_str_at(s, 11)), mtry!(parse_byte_from_ascii_str_at(s, 9)), mtry!(parse_byte_from_ascii_str_at(s, 16)), mtry!(parse_byte_from_ascii_str_at(s, 14)), mtry!(parse_byte_from_ascii_str_at(s, 19)), mtry!(parse_byte_from_ascii_str_at(s, 21)), mtry!(parse_byte_from_ascii_str_at(s, 24)), mtry!(parse_byte_from_ascii_str_at(s, 26)), mtry!(parse_byte_from_ascii_str_at(s, 28)), mtry!(parse_byte_from_ascii_str_at(s, 30)), mtry!(parse_byte_from_ascii_str_at(s, 32)), mtry!(parse_byte_from_ascii_str_at(s, 34)), ])) } /// Parse a GUID from a string, panicking on failure. /// /// The input must be in "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" /// format, where each `x` is a hex digit (any of `0-9`, `a-f`, or /// `A-F`). /// /// This function is marked `track_caller` so that error messages /// point directly to the invalid GUID string. /// /// # Panics /// /// This function will panic if the input is not in the format shown /// above. In particular, it will panic if the input is not exactly /// 36 bytes long, or if the input does not have separators at the /// expected positions, or if any of the remaining characters are /// not valid hex digits. #[must_use] #[track_caller] pub const fn parse_or_panic(s: &str) -> Self { match Self::try_parse(s) { Ok(g) => g, Err(GuidFromStrError::Length) => { panic!("GUID string has wrong length (expected 36 bytes)"); } Err(GuidFromStrError::Separator(_)) => { panic!("GUID string is missing one or more separators (`-`)"); } Err(GuidFromStrError::Hex(_)) => { panic!("GUID string contains one or more invalid characters"); } } } /// Create a GUID from a 16-byte array. No changes to byte order are made. #[must_use] pub const fn from_bytes(bytes: [u8; 16]) -> Self { Self::new( [bytes[0], bytes[1], bytes[2], bytes[3]], [bytes[4], bytes[5]], [bytes[6], bytes[7]], bytes[8], bytes[9], [ bytes[10], bytes[11], bytes[12], bytes[13], bytes[14], bytes[15], ], ) } /// Convert to a 16-byte array. #[must_use] pub const fn to_bytes(self) -> [u8; 16] { let time_low = self.time_low(); [ time_low[0], time_low[1], time_low[2], time_low[3], self.time_mid[0], self.time_mid[1], self.time_high_and_version[0], self.time_high_and_version[1], self.clock_seq_high_and_reserved, self.clock_seq_low, self.node[0], self.node[1], self.node[2], self.node[3], self.node[4], self.node[5], ] } /// Convert to a lower-case hex ASCII string. /// /// The output is in "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" format. #[must_use] pub const fn to_ascii_hex_lower(self) -> [u8; 36] { let bytes = self.to_bytes(); let mut buf = [0; 36]; (buf[0], buf[1]) = byte_to_ascii_hex_lower(bytes[3]); (buf[2], buf[3]) = byte_to_ascii_hex_lower(bytes[2]); (buf[4], buf[5]) = byte_to_ascii_hex_lower(bytes[1]); (buf[6], buf[7]) = byte_to_ascii_hex_lower(bytes[0]); buf[8] = b'-'; (buf[9], buf[10]) = byte_to_ascii_hex_lower(bytes[5]); (buf[11], buf[12]) = byte_to_ascii_hex_lower(bytes[4]); buf[13] = b'-'; (buf[14], buf[15]) = byte_to_ascii_hex_lower(bytes[7]); (buf[16], buf[17]) = byte_to_ascii_hex_lower(bytes[6]); buf[18] = b'-'; (buf[19], buf[20]) = byte_to_ascii_hex_lower(bytes[8]); (buf[21], buf[22]) = byte_to_ascii_hex_lower(bytes[9]); buf[23] = b'-'; (buf[24], buf[25]) = byte_to_ascii_hex_lower(bytes[10]); (buf[26], buf[27]) = byte_to_ascii_hex_lower(bytes[11]); (buf[28], buf[29]) = byte_to_ascii_hex_lower(bytes[12]); (buf[30], buf[31]) = byte_to_ascii_hex_lower(bytes[13]); (buf[32], buf[33]) = byte_to_ascii_hex_lower(bytes[14]); (buf[34], buf[35]) = byte_to_ascii_hex_lower(bytes[15]); buf } } impl Default for Guid { fn default() -> Self { Self::ZERO } } impl Display for Guid { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { let ascii = self.to_ascii_hex_lower(); // OK to unwrap since the ascii output is valid utf-8. let s = str::from_utf8(&ascii).unwrap(); f.write_str(s) } } impl FromStr for Guid { type Err = GuidFromStrError; /// Parse a GUID from a string, panicking on failure. /// /// The input must be in "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" /// format, where each `x` is a hex digit (any of `0-9`, `a-f`, or /// `A-F`). fn from_str(s: &str) -> Result { Self::try_parse(s) } } #[cfg(feature = "serde")] impl Serialize for Guid { fn serialize(&self, serializer: S) -> Result where S: Serializer, { let ascii = self.to_ascii_hex_lower(); // OK to unwrap since the ascii output is valid utf-8. let s = str::from_utf8(&ascii).unwrap(); serializer.serialize_str(s) } } #[cfg(feature = "serde")] struct DeserializerVisitor; #[cfg(feature = "serde")] impl<'de> Visitor<'de> for DeserializerVisitor { type Value = Guid; fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { formatter.write_str( "a string in the format \"xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx\"", ) } fn visit_str(self, value: &str) -> Result where E: de::Error, { Guid::try_parse(value).map_err(E::custom) } } #[cfg(feature = "serde")] impl<'de> Deserialize<'de> for Guid { fn deserialize(deserializer: D) -> Result where D: Deserializer<'de>, { deserializer.deserialize_str(DeserializerVisitor) } } /// Variant or type of GUID, as defined in [RFC4122]. /// /// [RFC4122]: https://datatracker.ietf.org/doc/html/rfc4122#section-4.1.3 #[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd)] pub enum Variant { /// Reserved, NCS backward compatibility. ReservedNcs, /// The GUID variant described by RFC4122. Rfc4122, /// Reserved, Microsoft Corporation backward compatibility. ReservedMicrosoft, /// Reserved for future use. ReservedFuture, }