gpt_partition_type/
lib.rs

1
2
3macro_rules! guid_lookup {
4    (
5        $([ $os:literal, $name:literal, $guid:literal ])*
6    ) => {
7        impl PartitionDescription {
8            pub const fn from_guid(guid: PartitionTypeGuid) -> Option<Self> {
9                match match guid {
10                    $(
11                        guid if eq(guid, parse_guid($guid)) => Some(Self {
12                            os: $os,
13                            type_description: $name,
14                        }),
15                    )*
16                    _ => None,
17                } {
18                    None => match guid.flip_endian() {
19                        $(
20                            guid if eq(guid, parse_guid($guid)) => Some(Self {
21                                os: $os,
22                                type_description: $name,
23                            }),
24                        )*
25                        _ => None,
26                    },
27                    Some(x) => Some(x),
28                }
29            }
30        }
31    };
32}
33
34const fn eq(x: PartitionTypeGuid, y: PartitionTypeGuid) -> bool {
35    x.time_low == y.time_low &&
36        x.time_mid == y.time_mid &&
37        x.time_hi_and_version == y.time_hi_and_version &&
38        x.clock_seq == y.clock_seq &&
39        x.node == y.node
40}
41
42type U48 = u64;
43
44#[derive(Copy, Clone, Debug, PartialEq, Eq, Default)]
45pub struct PartitionTypeGuid {
46    pub time_low: u32,
47    pub time_mid: u16,
48    pub time_hi_and_version: u16,
49    pub clock_seq: u16,
50    pub node: U48,
51}
52
53#[derive(Debug, PartialEq, Eq)]
54pub struct PartitionDescription {
55    pub os: &'static str,
56    pub type_description: &'static str,
57}
58
59impl PartitionTypeGuid {
60    pub const fn flip_endian(&self) -> Self {
61        Self {
62            // flipped
63            time_low: u32::from_le(self.time_low.to_be()),
64            time_mid: u16::from_le(self.time_mid.to_be()),
65            time_hi_and_version: u16::from_le(self.time_hi_and_version.to_be()),
66
67            // not flipped
68            clock_seq: self.clock_seq,
69            node: self.node,
70        }
71    }
72
73    pub const fn from_bytes(bytes: [u8; 0x10]) -> Self {
74        let [a, b, c, d,   e, f,   g, h,   i, j,   k, l, m, n, o, p] = bytes;
75
76        Self {
77            time_low: u32::from_le_bytes([a, b, c, d]),
78            time_mid: u16::from_le_bytes([e, f]),
79            time_hi_and_version: u16::from_le_bytes([g, h]),
80            clock_seq: u16::from_be_bytes([i, j]),
81            node: u64::from_be_bytes([0, 0, k, l, m, n, o, p]),
82        }
83    }
84
85    pub const fn into_bytes(self) -> [u8; 0x10] {
86        let [a, b, c, d] = self.time_low.to_le_bytes();
87        let [e, f] = self.time_mid.to_le_bytes();
88        let [g, h] = self.time_hi_and_version.to_le_bytes();
89        let [i, j] = self.clock_seq.to_be_bytes();
90        let [_, _, k, l, m, n, o, p] = self.node.to_be_bytes();
91
92        [a, b, c, d,   e, f,   g, h,   i, j,   k, l, m, n, o, p]
93    }
94
95    pub const fn description(self) -> Option<PartitionDescription> {
96        PartitionDescription::from_guid(self)
97    }
98}
99
100include!(concat!(env!("OUT_DIR"), "/guid_lookup.rs"));
101
102const UH_OH: [&str; 1] = ["Invalid hex digit"];
103
104const fn from_hex(byte: u8) -> u64 {
105    (match byte {
106        b'A'..=b'F' => (byte - b'A') + 0xA,
107        b'a'..=b'f' => (byte - b'a') + 0xa,
108        b'0'..=b'9' => (byte - b'0'),
109        byte => UH_OH[byte as usize + 1].as_bytes()[0],
110    }) as u64
111}
112
113const fn parse_hex_bytes<const BITS: usize>(bytes: &[u8], at: usize) -> u64 {
114    let nibble_count = BITS / 4;
115
116    let mut num = 0;
117    let mut pos = at;
118    let end_pos = at + nibble_count;
119
120    while pos < end_pos {
121        num = (num << 4) + from_hex(bytes[pos]);
122        pos += 1;
123    }
124
125    num
126}
127
128pub const fn parse_guid(guid: &str) -> PartitionTypeGuid {
129    let bytes = guid.as_bytes();
130    PartitionTypeGuid {
131        time_low: parse_hex_bytes::<32>(bytes, 0) as _,
132        time_mid: parse_hex_bytes::<16>(bytes, 9) as _,
133        time_hi_and_version: parse_hex_bytes::<16>(bytes, 14) as _,
134        clock_seq: parse_hex_bytes::<16>(bytes, 19) as _,
135        node: parse_hex_bytes::<48>(bytes, 24),
136    }
137}
138
139#[cfg(test)]
140mod tests {
141    use super::*;
142
143    #[test]
144    fn parse_hex() {
145        assert_eq!(0x123, parse_hex_bytes::<16>(b"0123", 0));
146        assert_eq!(0x23, parse_hex_bytes::<8>(b"0123", 2));
147        assert_eq!(
148            PartitionTypeGuid {
149                time_low: 264650159,
150                time_mid: 33923,
151                time_hi_and_version: 18290,
152                clock_seq: 36473,
153                node: 67524809424356
154            },
155            parse_guid("0FC63DAF-8483-4772-8E79-3D69D8477DE4")
156        );
157        assert_eq!(
158            parse_guid("0FC63DAF-8483-4772-8E79-3D69D8477DE4"),
159            PartitionTypeGuid::from_bytes(
160                parse_guid("0FC63DAF-8483-4772-8E79-3D69D8477DE4").into_bytes()
161            )
162        );
163        assert_eq!(
164            parse_guid("0FC63DAF-8483-4772-8E79-3D69D8477DE4").description().unwrap(),
165            PartitionDescription {
166                os: "Linux",
167                type_description: "Linux filesystem data"
168            }
169        );
170    }
171}