1#[repr(C)]
6#[derive(Eq, PartialEq, Clone)]
7pub struct GUID
8{
9 pub data1: u32,
10 pub data2: u16,
11 pub data3: u16,
12 pub data4: [u8; 8],
13}
14
15impl std::fmt::Debug for GUID
16{
17 fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error>
18 {
19 write!(f, "{}", self)
20 }
21}
22
23impl GUID
24{
25 pub fn zero_guid() -> GUID
26 {
27 GUID {
28 data1: 0,
29 data2: 0,
30 data3: 0,
31 data4: [0; 8],
32 }
33 }
34
35 pub fn parse(guid: &str) -> Result<GUID, String>
43 {
44 enum GuidFormat
52 {
53 Braces,
54 Hyphens,
55 Raw,
56 }
57 let guid_format = match guid.len() {
58 38 => GuidFormat::Braces,
59 36 => GuidFormat::Hyphens,
60 32 => GuidFormat::Raw,
61 _ => {
62 return Err(format!(
63 "Unrecognized GUID format: '{}' ({})",
64 guid,
65 guid.len()
66 ))
67 }
68 };
69
70 #[rustfmt::skip]
75 let format = match guid_format {
76 GuidFormat::Braces => vec![
77 Some( b'{' ), None, None, None, None, None, None, None, None,
78 Some( b'-' ), None, None, None, None,
79 Some( b'-' ), None, None, None, None,
80 Some( b'-' ), None, None, None, None,
81 Some( b'-' ), None, None, None, None, None, None, None, None, None, None, None, None,
82 Some( b'}' )
83 ],
84 GuidFormat::Hyphens => vec![
85 None, None, None, None, None, None, None, None,
86 Some( b'-' ), None, None, None, None,
87 Some( b'-' ), None, None, None, None,
88 Some( b'-' ), None, None, None, None,
89 Some( b'-' ), None, None, None, None, None, None, None, None, None, None, None, None
90 ],
91 GuidFormat::Raw => vec![
92 None, None, None, None, None, None, None, None,
93 None, None, None, None,
94 None, None, None, None,
95 None, None, None, None,
96 None, None, None, None, None, None, None, None, None, None, None, None
97 ]
98 };
99
100 let mut buffer = [0u8; 16];
102 let mut digit = 0;
103 for (i_char, chr) in guid.bytes().enumerate() {
104 if let Some(b) = format[i_char] {
109 if chr == b {
110 continue;
111 } else {
112 return Err(format!("Unexpected character in GUID: {}", chr));
113 }
114 }
115
116 let value: u8 = match chr {
118 b'0'..=b'9' => chr - b'0',
119 b'a'..=b'f' => chr - b'a' + 10,
120 b'A'..=b'F' => chr - b'A' + 10,
121 _ => return Err(format!("Unrecognized character in GUID: {}", chr)),
122 };
123
124 let half = digit % 2;
127 let byte = (digit - half) / 2;
128
129 if half == 0 {
132 buffer[byte] += value * 16;
133 } else {
134 buffer[byte] += value;
135 }
136
137 digit += 1;
141 }
142
143 Ok(GUID {
152 data1: (u32::from(buffer[0]) << 24)
153 + (u32::from(buffer[1]) << 16)
154 + (u32::from(buffer[2]) << 8)
155 + (u32::from(buffer[3])),
156 data2: (u16::from(buffer[4]) << 8) + (u16::from(buffer[5])),
157 data3: (u16::from(buffer[6]) << 8) + (u16::from(buffer[7])),
158 data4: [
159 buffer[8], buffer[9], buffer[10], buffer[11], buffer[12], buffer[13], buffer[14],
160 buffer[15],
161 ],
162 })
163 }
164
165 pub fn as_bytes(&self) -> &[u8; 16]
167 {
168 unsafe { &*(self as *const _ as *const [u8; 16]) }
171 }
172}
173
174impl Default for GUID
175{
176 fn default() -> GUID
177 {
178 GUID::zero_guid()
179 }
180}
181
182impl std::fmt::Display for GUID
183{
184 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result
186 {
187 fmt_guid(self, f, &GuidFmtCase::Upper, &GuidFmt::Braces)
188 }
189}
190
191impl std::fmt::LowerHex for GUID
192{
193 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result
195 {
196 let fmt = if f.sign_minus() {
197 GuidFmt::Hyphens
198 } else {
199 GuidFmt::Raw
200 };
201 fmt_guid(self, f, &GuidFmtCase::Lower, &fmt)
202 }
203}
204
205impl std::fmt::UpperHex for GUID
206{
207 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result
209 {
210 let fmt = if f.sign_minus() {
211 GuidFmt::Hyphens
212 } else {
213 GuidFmt::Raw
214 };
215 fmt_guid(self, f, &GuidFmtCase::Upper, &fmt)
216 }
217}
218
219enum GuidFmtCase
220{
221 Lower,
222 Upper,
223}
224enum GuidFmt
225{
226 Braces,
227 Hyphens,
228 Raw,
229}
230
231fn fmt_guid(
232 g: &GUID,
233 f: &mut std::fmt::Formatter,
234 case: &GuidFmtCase,
235 fmt: &GuidFmt,
236) -> std::fmt::Result
237{
238 match *case {
239 GuidFmtCase::Lower => {
240 match *fmt {
241 GuidFmt::Braces => {
242 write!( f,
243 "{{{:08x}-{:04x}-{:04x}-{:02x}{:02x}-{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}}}",
244 g.data1, g.data2, g.data3,
245 g.data4[0], g.data4[1], g.data4[2], g.data4[3],
246 g.data4[4], g.data4[5], g.data4[6], g.data4[7] )
247 }
248 GuidFmt::Hyphens => write!(
249 f,
250 "{:08x}-{:04x}-{:04x}-{:02x}{:02x}-{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}",
251 g.data1,
252 g.data2,
253 g.data3,
254 g.data4[0],
255 g.data4[1],
256 g.data4[2],
257 g.data4[3],
258 g.data4[4],
259 g.data4[5],
260 g.data4[6],
261 g.data4[7]
262 ),
263 GuidFmt::Raw => write!(
264 f,
265 "{:08x}{:04x}{:04x}{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}",
266 g.data1,
267 g.data2,
268 g.data3,
269 g.data4[0],
270 g.data4[1],
271 g.data4[2],
272 g.data4[3],
273 g.data4[4],
274 g.data4[5],
275 g.data4[6],
276 g.data4[7]
277 ),
278 }
279 }
280 GuidFmtCase::Upper => {
281 match *fmt {
282 GuidFmt::Braces => {
283 write!( f,
284 "{{{:08X}-{:04X}-{:04X}-{:02X}{:02X}-{:02X}{:02X}{:02X}{:02X}{:02X}{:02X}}}",
285 g.data1, g.data2, g.data3,
286 g.data4[0], g.data4[1], g.data4[2], g.data4[3],
287 g.data4[4], g.data4[5], g.data4[6], g.data4[7] )
288 }
289 GuidFmt::Hyphens => write!(
290 f,
291 "{:08X}-{:04X}-{:04X}-{:02X}{:02X}-{:02X}{:02X}{:02X}{:02X}{:02X}{:02X}",
292 g.data1,
293 g.data2,
294 g.data3,
295 g.data4[0],
296 g.data4[1],
297 g.data4[2],
298 g.data4[3],
299 g.data4[4],
300 g.data4[5],
301 g.data4[6],
302 g.data4[7]
303 ),
304 GuidFmt::Raw => write!(
305 f,
306 "{:08X}{:04X}{:04X}{:02X}{:02X}{:02X}{:02X}{:02X}{:02X}{:02X}{:02X}",
307 g.data1,
308 g.data2,
309 g.data3,
310 g.data4[0],
311 g.data4[1],
312 g.data4[2],
313 g.data4[3],
314 g.data4[4],
315 g.data4[5],
316 g.data4[6],
317 g.data4[7]
318 ),
319 }
320 }
321 }
322}
323
324#[cfg(test)]
325mod test
326{
327 use super::*;
328
329 #[test]
330 fn zero_guid()
331 {
332 let guid = GUID::zero_guid();
333
334 assert_eq!(0, guid.data1);
335 assert_eq!(0, guid.data2);
336 assert_eq!(0, guid.data3);
337 assert_eq!([0, 0, 0, 0, 0, 0, 0, 0], guid.data4);
338 }
339
340 #[test]
341 fn parse_braces()
342 {
343 let expected = GUID {
344 data1: 0x12345678,
345 data2: 0x90ab,
346 data3: 0xcdef,
347 data4: [0xfe, 0xdc, 0xba, 0x09, 0x87, 0x65, 0x43, 0x21],
348 };
349
350 let actual = GUID::parse("{12345678-90ab-cdef-fedc-ba0987654321}").unwrap();
351
352 assert_eq!(expected, actual);
353 }
354
355 #[test]
356 fn parse_hyphenated()
357 {
358 let expected = GUID {
359 data1: 0x12345678,
360 data2: 0x90ab,
361 data3: 0xcdef,
362 data4: [0xfe, 0xdc, 0xba, 0x09, 0x87, 0x65, 0x43, 0x21],
363 };
364
365 let actual = GUID::parse("12345678-90ab-cdef-fedc-ba0987654321").unwrap();
366
367 assert_eq!(expected, actual);
368 }
369
370 #[test]
371 fn parse_raw()
372 {
373 let expected = GUID {
374 data1: 0x12345678,
375 data2: 0x90ab,
376 data3: 0xcdef,
377 data4: [0xfe, 0xdc, 0xba, 0x09, 0x87, 0x65, 0x43, 0x21],
378 };
379
380 let actual = GUID::parse("1234567890abcdeffedcba0987654321").unwrap();
381
382 assert_eq!(expected, actual);
383 }
384
385 #[test]
386 fn format_default()
387 {
388 let expected = "{12345678-90AB-CDEF-FEDC-BA0987654321}";
389 let guid = GUID {
390 data1: 0x12345678,
391 data2: 0x90ab,
392 data3: 0xcdef,
393 data4: [0xfe, 0xdc, 0xba, 0x09, 0x87, 0x65, 0x43, 0x21],
394 };
395
396 assert_eq!(expected, format!("{}", guid));
397 }
398
399 #[test]
400 fn format_lowerhex()
401 {
402 let expected = "1234567890abcdeffedcba0987654321";
403 let guid = GUID {
404 data1: 0x12345678,
405 data2: 0x90ab,
406 data3: 0xcdef,
407 data4: [0xfe, 0xdc, 0xba, 0x09, 0x87, 0x65, 0x43, 0x21],
408 };
409
410 assert_eq!(expected, format!("{:x}", guid));
411 }
412
413 #[test]
414 fn format_lowerhex_hyphens()
415 {
416 let expected = "12345678-90ab-cdef-fedc-ba0987654321";
417 let guid = GUID {
418 data1: 0x12345678,
419 data2: 0x90ab,
420 data3: 0xcdef,
421 data4: [0xfe, 0xdc, 0xba, 0x09, 0x87, 0x65, 0x43, 0x21],
422 };
423
424 assert_eq!(expected, format!("{:-x}", guid));
425 }
426
427 #[test]
428 fn format_upperhex()
429 {
430 let expected = "1234567890ABCDEFFEDCBA0987654321";
431 let guid = GUID {
432 data1: 0x12345678,
433 data2: 0x90ab,
434 data3: 0xcdef,
435 data4: [0xfe, 0xdc, 0xba, 0x09, 0x87, 0x65, 0x43, 0x21],
436 };
437
438 assert_eq!(expected, format!("{:X}", guid));
439 }
440
441 #[test]
442 fn format_upperhex_hyphens()
443 {
444 let expected = "12345678-90AB-CDEF-FEDC-BA0987654321";
445 let guid = GUID {
446 data1: 0x12345678,
447 data2: 0x90ab,
448 data3: 0xcdef,
449 data4: [0xfe, 0xdc, 0xba, 0x09, 0x87, 0x65, 0x43, 0x21],
450 };
451
452 assert_eq!(expected, format!("{:-X}", guid));
453 }
454}