]> git.lizzy.rs Git - rust.git/blob - src/libuuid/lib.rs
d4a33d6782dd5531c60edd2fedafe22d59244674
[rust.git] / src / libuuid / lib.rs
1 // Copyright 2013 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
4 //
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
10
11 /*!
12 Generate and parse UUIDs
13
14 Provides support for Universally Unique Identifiers (UUIDs). A UUID is a
15 unique 128-bit number, stored as 16 octets.  UUIDs are used to  assign unique
16 identifiers to entities without requiring a central allocating authority.
17
18 They are particularly useful in distributed systems, though can be used in
19 disparate areas, such as databases and network protocols.  Typically a UUID is
20 displayed in a readable string form as a sequence of hexadecimal digits,
21 separated into groups by hyphens.
22
23 The uniqueness property is not strictly guaranteed, however for all practical
24 purposes, it can be assumed that an unintentional collision would be extremely
25 unlikely.
26
27 # Examples
28
29 To create a new random (V4) UUID and print it out in hexadecimal form:
30
31 ```rust
32 use uuid::Uuid;
33
34 fn main() {
35     let uuid1 = Uuid::new_v4();
36     println!("{}", uuid1.to_str());
37 }
38  ```
39
40 # Strings
41
42 Examples of string representations:
43
44 * simple: `936DA01F9ABD4d9d80C702AF85C822A8`
45 * hyphenated: `550e8400-e29b-41d4-a716-446655440000`
46 * urn: `urn:uuid:F9168C5E-CEB2-4faa-B6BF-329BF39FA1E4`
47
48 # References
49
50 * [Wikipedia: Universally Unique Identifier](
51     http://en.wikipedia.org/wiki/Universally_unique_identifier)
52 * [RFC4122: A Universally Unique IDentifier (UUID) URN Namespace](
53     http://tools.ietf.org/html/rfc4122)
54
55 */
56
57 #[crate_id = "uuid#0.10-pre"];
58 #[crate_type = "rlib"];
59 #[crate_type = "dylib"];
60 #[license = "MIT/ASL2"];
61 #[doc(html_logo_url = "http://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png",
62       html_favicon_url = "http://www.rust-lang.org/favicon.ico",
63       html_root_url = "http://static.rust-lang.org/doc/master")];
64
65 #[feature(default_type_params)];
66
67 // NOTE remove the following two attributes after the next snapshot.
68 #[allow(unrecognized_lint)];
69 #[allow(default_type_param_usage)];
70
71 // test harness access
72 #[cfg(test)]
73 extern crate test;
74
75 extern crate rand;
76 extern crate serialize;
77
78 use std::cast::{transmute,transmute_copy};
79 use std::char::Char;
80 use std::default::Default;
81 use std::fmt;
82 use std::from_str::FromStr;
83 use std::hash::Hash;
84 use std::num::FromStrRadix;
85 use std::str;
86 use std::slice;
87
88 use rand::Rng;
89
90 use serialize::{Encoder, Encodable, Decoder, Decodable};
91
92 /// A 128-bit (16 byte) buffer containing the ID
93 pub type UuidBytes = [u8, ..16];
94
95 /// The version of the UUID, denoting the generating algorithm
96 #[deriving(Eq)]
97 pub enum UuidVersion {
98     /// Version 1: MAC address
99     Version1Mac    = 1,
100     /// Version 2: DCE Security
101     Version2Dce    = 2,
102     /// Version 3: MD5 hash
103     Version3Md5    = 3,
104     /// Version 4: Random
105     Version4Random = 4,
106     /// Version 5: SHA-1 hash
107     Version5Sha1   = 5,
108 }
109
110 /// The reserved variants of UUIDs
111 #[deriving(Eq)]
112 pub enum UuidVariant {
113     /// Reserved by the NCS for backward compatibility
114     VariantNCS,
115     /// As described in the RFC4122 Specification (default)
116     VariantRFC4122,
117     /// Reserved by Microsoft for backward compatibility
118     VariantMicrosoft,
119     /// Reserved for future expansion
120     VariantFuture,
121 }
122
123 /// A Universally Unique Identifier (UUID)
124 pub struct Uuid {
125     /// The 128-bit number stored in 16 bytes
126     bytes: UuidBytes
127 }
128
129 impl<S: Writer> Hash<S> for Uuid {
130     fn hash(&self, state: &mut S) {
131         self.bytes.hash(state)
132     }
133 }
134
135 /// A UUID stored as fields (identical to UUID, used only for conversions)
136 struct UuidFields {
137     /// First field, 32-bit word
138     data1: u32,
139     /// Second field, 16-bit short
140     data2: u16,
141     /// Third field, 16-bit short
142     data3: u16,
143     /// Fourth field, 8 bytes
144     data4: [u8, ..8]
145 }
146
147 /// Error details for string parsing failures
148 #[allow(missing_doc)]
149 pub enum ParseError {
150     ErrorInvalidLength(uint),
151     ErrorInvalidCharacter(char, uint),
152     ErrorInvalidGroups(uint),
153     ErrorInvalidGroupLength(uint, uint, uint),
154 }
155
156 /// Converts a ParseError to a string
157 impl fmt::Show for ParseError {
158     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
159         match *self {
160             ErrorInvalidLength(found) =>
161                 write!(f.buf, "Invalid length; expecting 32, 36 or 45 chars, \
162                                found {}", found),
163             ErrorInvalidCharacter(found, pos) =>
164                 write!(f.buf, "Invalid character; found `{}` (0x{:02x}) at \
165                                offset {}", found, found as uint, pos),
166             ErrorInvalidGroups(found) =>
167                 write!(f.buf, "Malformed; wrong number of groups: expected 1 \
168                                or 5, found {}", found),
169             ErrorInvalidGroupLength(group, found, expecting) =>
170                 write!(f.buf, "Malformed; length of group {} was {}, \
171                                expecting {}", group, found, expecting),
172         }
173     }
174 }
175
176 // Length of each hyphenated group in hex digits
177 static UuidGroupLens: [uint, ..5] = [8u, 4u, 4u, 4u, 12u];
178
179 /// UUID support
180 impl Uuid {
181     /// Returns a nil or empty UUID (containing all zeroes)
182     pub fn nil() -> Uuid {
183         let uuid = Uuid{ bytes: [0, .. 16] };
184         uuid
185     }
186
187     /// Create a new UUID of the specified version
188     pub fn new(v: UuidVersion) -> Option<Uuid> {
189         match v {
190             Version4Random => Some(Uuid::new_v4()),
191             _ => None
192         }
193     }
194
195     /// Creates a new random UUID
196     ///
197     /// Uses the `rand` module's default RNG task as the source
198     /// of random numbers. Use the rand::Rand trait to supply
199     /// a custom generator if required.
200     pub fn new_v4() -> Uuid {
201         let ub = rand::task_rng().gen_vec(16);
202         let mut uuid = Uuid{ bytes: [0, .. 16] };
203         slice::bytes::copy_memory(uuid.bytes, ub);
204         uuid.set_variant(VariantRFC4122);
205         uuid.set_version(Version4Random);
206         uuid
207     }
208
209     /// Creates a UUID using the supplied field values
210     ///
211     /// # Arguments
212     /// * `d1` A 32-bit word
213     /// * `d2` A 16-bit word
214     /// * `d3` A 16-bit word
215     /// * `d4` Array of 8 octets
216     pub fn from_fields(d1: u32, d2: u16, d3: u16, d4: &[u8]) -> Uuid {
217         use std::mem::{to_be16, to_be32};
218
219         // First construct a temporary field-based struct
220         let mut fields = UuidFields {
221                 data1: 0,
222                 data2: 0,
223                 data3: 0,
224                 data4: [0, ..8]
225         };
226
227         fields.data1 = to_be32(d1 as i32) as u32;
228         fields.data2 = to_be16(d2 as i16) as u16;
229         fields.data3 = to_be16(d3 as i16) as u16;
230         slice::bytes::copy_memory(fields.data4, d4);
231
232         unsafe {
233             transmute(fields)
234         }
235     }
236
237     /// Creates a UUID using the supplied bytes
238     ///
239     /// # Arguments
240     /// * `b` An array or slice of 16 bytes
241     pub fn from_bytes(b: &[u8]) -> Option<Uuid> {
242         if b.len() != 16 {
243             return None
244         }
245
246         let mut uuid = Uuid{ bytes: [0, .. 16] };
247         slice::bytes::copy_memory(uuid.bytes, b);
248         Some(uuid)
249     }
250
251     /// Specifies the variant of the UUID structure
252     fn set_variant(&mut self, v: UuidVariant) {
253         // Octet 8 contains the variant in the most significant 3 bits
254         match v {
255             VariantNCS =>        // b0xx...
256                 self.bytes[8] =  self.bytes[8] & 0x7f,
257             VariantRFC4122 =>    // b10x...
258                 self.bytes[8] = (self.bytes[8] & 0x3f) | 0x80,
259             VariantMicrosoft =>  // b110...
260                 self.bytes[8] = (self.bytes[8] & 0x1f) | 0xc0,
261             VariantFuture =>     // b111...
262                 self.bytes[8] = (self.bytes[8] & 0x1f) | 0xe0,
263         }
264     }
265
266     /// Returns the variant of the UUID structure
267     ///
268     /// This determines the interpretation of the structure of the UUID.
269     /// Currently only the RFC4122 variant is generated by this module.
270     ///
271     /// * [Variant Reference](http://tools.ietf.org/html/rfc4122#section-4.1.1)
272     pub fn get_variant(&self) -> Option<UuidVariant> {
273         if self.bytes[8] & 0x80 == 0x00 {
274             Some(VariantNCS)
275         } else if self.bytes[8] & 0xc0 == 0x80 {
276             Some(VariantRFC4122)
277         } else if self.bytes[8] & 0xe0 == 0xc0  {
278             Some(VariantMicrosoft)
279         } else if self.bytes[8] & 0xe0 == 0xe0 {
280             Some(VariantFuture)
281         } else  {
282             None
283         }
284     }
285
286     /// Specifies the version number of the UUID
287     fn set_version(&mut self, v: UuidVersion) {
288         self.bytes[6] = (self.bytes[6] & 0xF) | ((v as u8) << 4);
289     }
290
291     /// Returns the version number of the UUID
292     ///
293     /// This represents the algorithm used to generate the contents.
294     ///
295     /// Currently only the Random (V4) algorithm is supported by this
296     /// module.  There are security and privacy implications for using
297     /// older versions - see [Wikipedia: Universally Unique Identifier](
298     /// http://en.wikipedia.org/wiki/Universally_unique_identifier) for
299     /// details.
300     ///
301     /// * [Version Reference](http://tools.ietf.org/html/rfc4122#section-4.1.3)
302     pub fn get_version_num(&self) -> uint {
303         (self.bytes[6] >> 4) as uint
304     }
305
306     /// Returns the version of the UUID
307     ///
308     /// This represents the algorithm used to generate the contents
309     pub fn get_version(&self) -> Option<UuidVersion> {
310         let v = self.bytes[6] >> 4;
311         match v {
312             1 => Some(Version1Mac),
313             2 => Some(Version2Dce),
314             3 => Some(Version3Md5),
315             4 => Some(Version4Random),
316             5 => Some(Version5Sha1),
317             _ => None
318         }
319     }
320
321     /// Return an array of 16 octets containing the UUID data
322     pub fn as_bytes<'a>(&'a self) -> &'a [u8] {
323         self.bytes.as_slice()
324     }
325
326     /// Returns the UUID as a string of 16 hexadecimal digits
327     ///
328     /// Example: `936DA01F9ABD4d9d80C702AF85C822A8`
329     pub fn to_simple_str(&self) -> ~str {
330         let mut s: ~[u8] = slice::from_elem(32, 0u8);
331         for i in range(0u, 16u) {
332             let digit = format!("{:02x}", self.bytes[i] as uint);
333             s[i*2+0] = digit[0];
334             s[i*2+1] = digit[1];
335         }
336         str::from_utf8_owned(s).unwrap()
337     }
338
339     /// Returns a string of hexadecimal digits, separated into groups with a hyphen.
340     ///
341     /// Example: `550e8400-e29b-41d4-a716-446655440000`
342     pub fn to_hyphenated_str(&self) -> ~str {
343         use std::mem::{to_be16, to_be32};
344         // Convert to field-based struct as it matches groups in output.
345         // Ensure fields are in network byte order, as per RFC.
346         let mut uf: UuidFields;
347         unsafe {
348             uf = transmute_copy(&self.bytes);
349         }
350         uf.data1 = to_be32(uf.data1 as i32) as u32;
351         uf.data2 = to_be16(uf.data2 as i16) as u16;
352         uf.data3 = to_be16(uf.data3 as i16) as u16;
353         let s = format!("{:08x}-{:04x}-{:04x}-{:02x}{:02x}-\
354                          {:02x}{:02x}{:02x}{:02x}{:02x}{:02x}",
355             uf.data1,
356             uf.data2, uf.data3,
357             uf.data4[0], uf.data4[1],
358             uf.data4[2], uf.data4[3], uf.data4[4],
359             uf.data4[5], uf.data4[6], uf.data4[7]);
360         s
361     }
362
363     /// Returns the UUID formatted as a full URN string
364     ///
365     /// This is the same as the hyphenated format, but with the "urn:uuid:" prefix.
366     ///
367     /// Example: `urn:uuid:F9168C5E-CEB2-4faa-B6BF-329BF39FA1E4`
368     pub fn to_urn_str(&self) -> ~str {
369         "urn:uuid:" + self.to_hyphenated_str()
370     }
371
372     /// Parses a UUID from a string of hexadecimal digits with optional hyphens
373     ///
374     /// Any of the formats generated by this module (simple, hyphenated, urn) are
375     /// supported by this parsing function.
376     pub fn parse_string(us: &str) -> Result<Uuid, ParseError> {
377
378         let mut us = us.clone();
379         let orig_len = us.len();
380
381         // Ensure length is valid for any of the supported formats
382         if orig_len != 32 && orig_len != 36 && orig_len != 45 {
383             return Err(ErrorInvalidLength(orig_len));
384         }
385
386         // Strip off URN prefix if present
387         if us.starts_with("urn:uuid:") {
388             us = us.slice(9, orig_len);
389         }
390
391         // Make sure all chars are either hex digits or hyphen
392         for (i, c) in us.chars().enumerate() {
393             match c {
394                 '0'..'9' | 'A'..'F' | 'a'..'f' | '-' => {},
395                 _ => return Err(ErrorInvalidCharacter(c, i)),
396             }
397         }
398
399         // Split string up by hyphens into groups
400         let hex_groups: ~[&str] = us.split_str("-").collect();
401
402         // Get the length of each group
403         let group_lens: ~[uint] = hex_groups.iter().map(|&v| v.len()).collect();
404
405         // Ensure the group lengths are valid
406         match group_lens.len() {
407             // Single group, no hyphens
408             1 => {
409                 if group_lens[0] != 32 {
410                     return Err(ErrorInvalidLength(group_lens[0]));
411                 }
412             },
413             // Five groups, hyphens in between each
414             5 => {
415                 // Ensure each group length matches the expected
416                 for (i, (&gl, &expected)) in
417                     group_lens.iter().zip(UuidGroupLens.iter()).enumerate() {
418                     if gl != expected {
419                         return Err(ErrorInvalidGroupLength(i, gl, expected))
420                     }
421                 }
422             },
423             _ => {
424                 return Err(ErrorInvalidGroups(group_lens.len()));
425             }
426         }
427
428         // Normalise into one long hex string
429         let vs = hex_groups.concat();
430
431         // At this point, we know we have a valid hex string, without hyphens
432         assert!(vs.len() == 32);
433         assert!(vs.chars().all(|c| c.is_digit_radix(16)));
434
435         // Allocate output UUID buffer
436         let mut ub = [0u8, ..16];
437
438         // Extract each hex digit from the string
439         for i in range(0u, 16u) {
440             ub[i] = FromStrRadix::from_str_radix(vs.slice(i*2, (i+1)*2), 16).unwrap();
441         }
442
443         Ok(Uuid::from_bytes(ub).unwrap())
444     }
445
446     /// Tests if the UUID is nil
447     pub fn is_nil(&self) -> bool {
448         return self.bytes.iter().all(|&b| b == 0);
449     }
450 }
451
452 impl Default for Uuid {
453     /// Returns the nil UUID, which is all zeroes
454     fn default() -> Uuid {
455         Uuid::nil()
456     }
457 }
458
459 impl Clone for Uuid {
460     /// Returns a copy of the UUID
461     fn clone(&self) -> Uuid { *self }
462 }
463
464 impl FromStr for Uuid {
465     /// Parse a hex string and interpret as a UUID
466     ///
467     /// Accepted formats are a sequence of 32 hexadecimal characters,
468     /// with or without hypens (grouped as 8, 4, 4, 4, 12).
469     fn from_str(us: &str) -> Option<Uuid> {
470         let result = Uuid::parse_string(us);
471         match result {
472             Ok(u) => Some(u),
473             Err(_) => None
474         }
475     }
476 }
477
478 /// Convert the UUID to a hexadecimal-based string representation
479 impl fmt::Show for Uuid {
480     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
481         write!(f.buf, "{}", self.to_simple_str())
482     }
483 }
484
485 /// Test two UUIDs for equality
486 ///
487 /// UUIDs are equal only when they are byte-for-byte identical
488 impl Eq for Uuid {
489     fn eq(&self, other: &Uuid) -> bool {
490         self.bytes == other.bytes
491     }
492 }
493
494 /// Test two UUIDs for equality
495 ///
496 /// UUIDs are equal only when they are byte-for-byte identical
497 impl TotalEq for Uuid {
498     fn equals(&self, other: &Uuid) -> bool {
499         self.bytes == other.bytes
500     }
501 }
502
503 // FIXME #9845: Test these more thoroughly
504 impl<T: Encoder> Encodable<T> for Uuid {
505     /// Encode a UUID as a hypenated string
506     fn encode(&self, e: &mut T) {
507         e.emit_str(self.to_hyphenated_str());
508     }
509 }
510
511 impl<T: Decoder> Decodable<T> for Uuid {
512     /// Decode a UUID from a string
513     fn decode(d: &mut T) -> Uuid {
514         from_str(d.read_str()).unwrap()
515     }
516 }
517
518 /// Generates a random instance of UUID (V4 conformant)
519 impl rand::Rand for Uuid {
520     #[inline]
521     fn rand<R: rand::Rng>(rng: &mut R) -> Uuid {
522         let ub = rng.gen_vec(16);
523         let mut uuid = Uuid{ bytes: [0, .. 16] };
524         slice::bytes::copy_memory(uuid.bytes, ub);
525         uuid.set_variant(VariantRFC4122);
526         uuid.set_version(Version4Random);
527         uuid
528     }
529 }
530
531 #[cfg(test)]
532 mod test {
533     extern crate collections;
534     extern crate rand;
535
536     use super::{Uuid, VariantMicrosoft, VariantNCS, VariantRFC4122,
537                 Version1Mac, Version2Dce, Version3Md5, Version4Random,
538                 Version5Sha1};
539     use std::str;
540     use std::io::MemWriter;
541
542     #[test]
543     fn test_nil() {
544         let nil = Uuid::nil();
545         let not_nil = Uuid::new_v4();
546
547         assert!(nil.is_nil());
548         assert!(!not_nil.is_nil());
549     }
550
551     #[test]
552     fn test_new() {
553         // Supported
554         let uuid1 = Uuid::new(Version4Random).unwrap();
555         let s = uuid1.to_simple_str();
556
557         assert!(s.len() == 32);
558         assert!(uuid1.get_version().unwrap() == Version4Random);
559
560         // Test unsupported versions
561         assert!(Uuid::new(Version1Mac) == None);
562         assert!(Uuid::new(Version2Dce) == None);
563         assert!(Uuid::new(Version3Md5) == None);
564         assert!(Uuid::new(Version5Sha1) == None);
565     }
566
567     #[test]
568     fn test_new_v4() {
569         let uuid1 = Uuid::new_v4();
570
571         assert!(uuid1.get_version().unwrap() == Version4Random);
572         assert!(uuid1.get_variant().unwrap() == VariantRFC4122);
573     }
574
575     #[test]
576     fn test_get_version() {
577         let uuid1 = Uuid::new_v4();
578
579         assert!(uuid1.get_version().unwrap() == Version4Random);
580         assert!(uuid1.get_version_num() == 4);
581     }
582
583     #[test]
584     fn test_get_variant() {
585         let uuid1 = Uuid::new_v4();
586         let uuid2 = Uuid::parse_string("550e8400-e29b-41d4-a716-446655440000").unwrap();
587         let uuid3 = Uuid::parse_string("67e55044-10b1-426f-9247-bb680e5fe0c8").unwrap();
588         let uuid4 = Uuid::parse_string("936DA01F9ABD4d9dC0C702AF85C822A8").unwrap();
589         let uuid5 = Uuid::parse_string("F9168C5E-CEB2-4faa-D6BF-329BF39FA1E4").unwrap();
590         let uuid6 = Uuid::parse_string("f81d4fae-7dec-11d0-7765-00a0c91e6bf6").unwrap();
591
592         assert!(uuid1.get_variant().unwrap() == VariantRFC4122);
593         assert!(uuid2.get_variant().unwrap() == VariantRFC4122);
594         assert!(uuid3.get_variant().unwrap() == VariantRFC4122);
595         assert!(uuid4.get_variant().unwrap() == VariantMicrosoft);
596         assert!(uuid5.get_variant().unwrap() == VariantMicrosoft);
597         assert!(uuid6.get_variant().unwrap() == VariantNCS);
598     }
599
600     #[test]
601     fn test_parse_uuid_v4() {
602         use super::{ErrorInvalidCharacter, ErrorInvalidGroups,
603                     ErrorInvalidGroupLength, ErrorInvalidLength};
604
605         // Invalid
606         assert!(Uuid::parse_string("").is_err());
607         assert!(Uuid::parse_string("!").is_err());
608         assert!(Uuid::parse_string("F9168C5E-CEB2-4faa-B6BF-329BF39FA1E45").is_err());
609         assert!(Uuid::parse_string("F9168C5E-CEB2-4faa-BBF-329BF39FA1E4").is_err());
610         assert!(Uuid::parse_string("F9168C5E-CEB2-4faa-BGBF-329BF39FA1E4").is_err());
611         assert!(Uuid::parse_string("F9168C5E-CEB2-4faa-B6BFF329BF39FA1E4").is_err());
612         assert!(Uuid::parse_string("F9168C5E-CEB2-4faa").is_err());
613         assert!(Uuid::parse_string("F9168C5E-CEB2-4faaXB6BFF329BF39FA1E4").is_err());
614         assert!(Uuid::parse_string("F9168C5E-CEB-24fa-eB6BFF32-BF39FA1E4").is_err());
615         assert!(Uuid::parse_string("01020304-1112-2122-3132-41424344").is_err());
616         assert!(Uuid::parse_string("67e5504410b1426f9247bb680e5fe0c").is_err());
617         assert!(Uuid::parse_string("67e5504410b1426f9247bb680e5fe0c88").is_err());
618         assert!(Uuid::parse_string("67e5504410b1426f9247bb680e5fe0cg8").is_err());
619         assert!(Uuid::parse_string("67e5504410b1426%9247bb680e5fe0c8").is_err());
620
621         // Valid
622         assert!(Uuid::parse_string("00000000000000000000000000000000").is_ok());
623         assert!(Uuid::parse_string("67e55044-10b1-426f-9247-bb680e5fe0c8").is_ok());
624         assert!(Uuid::parse_string("67e55044-10b1-426f-9247-bb680e5fe0c8").is_ok());
625         assert!(Uuid::parse_string("F9168C5E-CEB2-4faa-B6BF-329BF39FA1E4").is_ok());
626         assert!(Uuid::parse_string("67e5504410b1426f9247bb680e5fe0c8").is_ok());
627         assert!(Uuid::parse_string("01020304-1112-2122-3132-414243444546").is_ok());
628         assert!(Uuid::parse_string("urn:uuid:67e55044-10b1-426f-9247-bb680e5fe0c8").is_ok());
629
630         // Nil
631         let nil = Uuid::nil();
632         assert!(Uuid::parse_string("00000000000000000000000000000000").unwrap()  == nil);
633         assert!(Uuid::parse_string("00000000-0000-0000-0000-000000000000").unwrap() == nil);
634
635         // Round-trip
636         let uuid_orig = Uuid::new_v4();
637         let orig_str = uuid_orig.to_str();
638         let uuid_out = Uuid::parse_string(orig_str).unwrap();
639         assert!(uuid_orig == uuid_out);
640
641         // Test error reporting
642         let e = Uuid::parse_string("67e5504410b1426f9247bb680e5fe0c").unwrap_err();
643         assert!(match e { ErrorInvalidLength(n) => n==31, _ => false });
644
645         let e = Uuid::parse_string("67e550X410b1426f9247bb680e5fe0cd").unwrap_err();
646         assert!(match e { ErrorInvalidCharacter(c, n) => c=='X' && n==6, _ => false });
647
648         let e = Uuid::parse_string("67e550-4105b1426f9247bb680e5fe0c").unwrap_err();
649         assert!(match e { ErrorInvalidGroups(n) => n==2, _ => false });
650
651         let e = Uuid::parse_string("F9168C5E-CEB2-4faa-B6BF1-02BF39FA1E4").unwrap_err();
652         assert!(match e { ErrorInvalidGroupLength(g, n, e) => g==3 && n==5 && e==4, _ => false });
653     }
654
655     #[test]
656     fn test_to_simple_str() {
657         let uuid1 = Uuid::new_v4();
658         let s = uuid1.to_simple_str();
659
660         assert!(s.len() == 32);
661         assert!(s.chars().all(|c| c.is_digit_radix(16)));
662     }
663
664     #[test]
665     fn test_to_str() {
666         let uuid1 = Uuid::new_v4();
667         let s = uuid1.to_str();
668
669         assert!(s.len() == 32);
670         assert!(s.chars().all(|c| c.is_digit_radix(16)));
671     }
672
673     #[test]
674     fn test_to_hyphenated_str() {
675         let uuid1 = Uuid::new_v4();
676         let s = uuid1.to_hyphenated_str();
677
678         assert!(s.len() == 36);
679         assert!(s.chars().all(|c| c.is_digit_radix(16) || c == '-'));
680     }
681
682     #[test]
683     fn test_to_urn_str() {
684         let uuid1 = Uuid::new_v4();
685         let ss = uuid1.to_urn_str();
686         let s = ss.slice(9, ss.len());
687
688         assert!(ss.starts_with("urn:uuid:"));
689         assert!(s.len() == 36);
690         assert!(s.chars().all(|c| c.is_digit_radix(16) || c == '-'));
691     }
692
693     #[test]
694     fn test_to_str_matching() {
695         let uuid1 = Uuid::new_v4();
696
697         let hs = uuid1.to_hyphenated_str();
698         let ss = uuid1.to_str();
699
700         let hsn = str::from_chars(hs.chars().filter(|&c| c != '-').collect::<~[char]>());
701
702         assert!(hsn == ss);
703     }
704
705     #[test]
706     fn test_string_roundtrip() {
707         let uuid = Uuid::new_v4();
708
709         let hs = uuid.to_hyphenated_str();
710         let uuid_hs = Uuid::parse_string(hs).unwrap();
711         assert!(uuid_hs == uuid);
712
713         let ss = uuid.to_str();
714         let uuid_ss = Uuid::parse_string(ss).unwrap();
715         assert!(uuid_ss == uuid);
716     }
717
718     #[test]
719     fn test_compare() {
720         let uuid1 = Uuid::new_v4();
721         let uuid2 = Uuid::new_v4();
722
723         assert!(uuid1 == uuid1);
724         assert!(uuid2 == uuid2);
725         assert!(uuid1 != uuid2);
726         assert!(uuid2 != uuid1);
727     }
728
729     #[test]
730     fn test_from_fields() {
731         let d1: u32 = 0xa1a2a3a4;
732         let d2: u16 = 0xb1b2;
733         let d3: u16 = 0xc1c2;
734         let d4: ~[u8] = ~[0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8];
735
736         let u = Uuid::from_fields(d1, d2, d3, d4);
737
738         let expected = ~"a1a2a3a4b1b2c1c2d1d2d3d4d5d6d7d8";
739         let result = u.to_simple_str();
740         assert!(result == expected);
741     }
742
743     #[test]
744     fn test_from_bytes() {
745         let b = ~[ 0xa1, 0xa2, 0xa3, 0xa4, 0xb1, 0xb2, 0xc1, 0xc2,
746                    0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8 ];
747
748         let u = Uuid::from_bytes(b).unwrap();
749         let expected = ~"a1a2a3a4b1b2c1c2d1d2d3d4d5d6d7d8";
750
751         assert!(u.to_simple_str() == expected);
752     }
753
754     #[test]
755     fn test_as_bytes() {
756         let u = Uuid::new_v4();
757         let ub = u.as_bytes();
758
759         assert!(ub.len() == 16);
760         assert!(! ub.iter().all(|&b| b == 0));
761     }
762
763     #[test]
764     fn test_bytes_roundtrip() {
765         let b_in: [u8, ..16] = [ 0xa1, 0xa2, 0xa3, 0xa4, 0xb1, 0xb2, 0xc1, 0xc2,
766                                  0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8 ];
767
768         let u = Uuid::from_bytes(b_in.clone()).unwrap();
769
770         let b_out = u.as_bytes();
771
772         assert!(b_in == b_out);
773     }
774
775     #[test]
776     fn test_operator_eq() {
777         let u1 = Uuid::new_v4();
778         let u2 = u1.clone();
779         let u3 = Uuid::new_v4();
780
781         assert!(u1 == u1);
782         assert!(u1 == u2);
783         assert!(u2 == u1);
784
785         assert!(u1 != u3);
786         assert!(u3 != u1);
787         assert!(u2 != u3);
788         assert!(u3 != u2);
789     }
790
791     #[test]
792     fn test_rand_rand() {
793         let mut rng = rand::task_rng();
794         let u: ~Uuid = rand::Rand::rand(&mut rng);
795         let ub = u.as_bytes();
796
797         assert!(ub.len() == 16);
798         assert!(! ub.iter().all(|&b| b == 0));
799     }
800
801     #[test]
802     fn test_serialize_round_trip() {
803         use serialize::ebml;
804         use serialize::{Encodable, Decodable};
805
806         let u = Uuid::new_v4();
807         let mut wr = MemWriter::new();
808         u.encode(&mut ebml::writer::Encoder(&mut wr));
809         let doc = ebml::reader::Doc(wr.get_ref());
810         let u2 = Decodable::decode(&mut ebml::reader::Decoder(doc));
811         assert_eq!(u, u2);
812     }
813
814     #[test]
815     fn test_iterbytes_impl_for_uuid() {
816         use self::collections::HashSet;
817         let mut set = HashSet::new();
818         let id1 = Uuid::new_v4();
819         let id2 = Uuid::new_v4();
820         set.insert(id1);
821         assert!(set.contains(&id1));
822         assert!(!set.contains(&id2));
823     }
824 }
825
826 #[cfg(test)]
827 mod bench {
828     extern crate test;
829     use self::test::BenchHarness;
830     use super::Uuid;
831
832     #[bench]
833     pub fn create_uuids(bh: &mut BenchHarness) {
834         bh.iter(|| {
835             Uuid::new_v4();
836         })
837     }
838
839     #[bench]
840     pub fn uuid_to_str(bh: &mut BenchHarness) {
841         let u = Uuid::new_v4();
842         bh.iter(|| {
843             u.to_str();
844         })
845     }
846
847     #[bench]
848     pub fn parse_str(bh: &mut BenchHarness) {
849         let s = "urn:uuid:F9168C5E-CEB2-4faa-B6BF-329BF39FA1E4";
850         bh.iter(|| {
851             Uuid::parse_string(s).unwrap();
852         })
853     }
854 }