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