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