td_shim_interface/td_uefi_pi/pi/
guid.rs

1use core::{convert::TryInto, mem::size_of, ptr::slice_from_raw_parts, str::FromStr};
2
3use scroll::{Pread, Pwrite};
4
5// use alloc::borrow::ToOwned;
6
7const GUID_STRING_LEN: usize = 36;
8const GUID_SPLITTER: u8 = b'-';
9
10// A GUID is a 128-bit integer (16 bytes) that can be
11// used as a unique identifier.
12#[repr(C)]
13#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Pwrite, Pread)]
14pub struct Guid {
15    f0: u32,
16    f1: u16,
17    f2: u16,
18    f3: [u8; 8],
19}
20
21#[derive(Debug)]
22pub enum GuidParseError {
23    InvalidInput,
24}
25
26impl Guid {
27    // Create a GUID instance from several fields
28    pub const fn from_fields(f0: u32, f1: u16, f2: u16, f3: [u8; 8]) -> Guid {
29        Self { f0, f1, f2, f3 }
30    }
31
32    pub fn as_bytes(&self) -> &[u8; 16] {
33        // Safe since the size of Guid is 16
34        unsafe {
35            (&*slice_from_raw_parts(self as *const Self as *const u8, size_of::<Self>()))
36                .try_into()
37                .unwrap()
38        }
39    }
40
41    pub fn from_bytes(buffer: &[u8; 16]) -> Guid {
42        let f0 = u32::from_le_bytes(buffer[0..4].try_into().unwrap());
43        let f1 = u16::from_le_bytes(buffer[4..6].try_into().unwrap());
44        let f2 = u16::from_le_bytes(buffer[6..8].try_into().unwrap());
45        let mut f3: [u8; 8] = [0; 8];
46        f3.copy_from_slice(&buffer[8..]);
47
48        Self { f0, f1, f2, f3 }
49    }
50}
51
52impl FromStr for Guid {
53    type Err = GuidParseError;
54
55    // Create a GUID instance from a string slice
56    // Input should follow format strictly: "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX"
57    // For example: "F9168C5E-CEB2-4faa-B6BF-329BF39FA1E4"
58    fn from_str(s: &str) -> Result<Self, Self::Err> {
59        let b = s.as_bytes();
60        if b.len() != GUID_STRING_LEN
61            || b[8] != GUID_SPLITTER
62            || b[13] != GUID_SPLITTER
63            || b[18] != GUID_SPLITTER
64            || b[23] != GUID_SPLITTER
65        {
66            return Err(GuidParseError::InvalidInput);
67        }
68
69        let parse_hex = |s: &str| -> Option<u64> {
70            for c in s.as_bytes() {
71                if !c.is_ascii_hexdigit() {
72                    return None;
73                }
74            }
75            u64::from_str_radix(s, 16).ok()
76        };
77
78        // Parse the string into fields
79        let f0 = parse_hex(&s[0..8]).ok_or(GuidParseError::InvalidInput)? as u32;
80        let f1 = parse_hex(&s[9..13]).ok_or(GuidParseError::InvalidInput)? as u16;
81        let f2 = parse_hex(&s[14..18]).ok_or(GuidParseError::InvalidInput)? as u16;
82        let mut f3 = parse_hex(&s[19..23]).ok_or(GuidParseError::InvalidInput)? << 48;
83        f3 |= parse_hex(&s[24..36]).ok_or(GuidParseError::InvalidInput)?;
84
85        // f3 is decoded from string so use big endian to encode into bytes
86        Ok(Self {
87            f0,
88            f1,
89            f2,
90            f3: u64::to_be_bytes(f3),
91        })
92    }
93}
94
95#[cfg(test)]
96mod test {
97    use super::*;
98
99    #[test]
100    fn test_guid() {
101        // F9168C5E-CEB2-4faa-B6BF-329BF39FA1E4
102        let guid_bytes = [
103            0x5E, 0x8C, 0x16, 0xF9, 0xB2, 0xCE, 0xaa, 0x4f, 0xB6, 0xBF, 0x32, 0x9B, 0xF3, 0x9F,
104            0xA1, 0xE4,
105        ];
106        let guid_field = Guid::from_fields(
107            0xF9168C5E,
108            0xCEB2,
109            0x4faa,
110            [0xB6, 0xBF, 0x32, 0x9B, 0xF3, 0x9F, 0xA1, 0xE4],
111        );
112
113        assert_eq!(&guid_bytes, guid_field.as_bytes());
114
115        let guid_str = Guid::from_str("F9168C5E-CEB2-4faa-B6BF-329BF39FA1E4").unwrap();
116        assert_eq!(&guid_bytes, guid_str.as_bytes());
117
118        let guid_str = Guid::from_str("F9168C5E");
119        assert!(guid_str.is_err());
120
121        let guid_str = Guid::from_str("F9168C5E-CEB2-4faa-B6BF-329");
122        assert!(guid_str.is_err());
123
124        let guid_str = Guid::from_str("F9168C5E-CEB2-4faaB6-BF-329BF39FA1E4");
125        assert!(guid_str.is_err());
126
127        let guid_str = Guid::from_str("+9168C5E-CEB2-4faa-B6BF-329BF39FA1E4");
128        assert!(guid_str.is_err());
129
130        let guid_str = Guid::from_str("F9168C5ECCEB2C4faaCB6BFC329BF39FA1E4");
131        assert!(guid_str.is_err());
132    }
133}