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 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 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}