2010-04-15 Jb Evain <jbevain@novell.com>
[mcs.git] / class / corlib / System / Guid.cs
blob14acb73c0a6d5aed96d6fbc0d8aba7a9d65d779b
1 //
2 // System.Guid.cs
3 //
4 // Authors:
5 // Duco Fijma (duco@lorentz.xs4all.nl)
6 // Sebastien Pouliot (sebastien@ximian.com)
7 //
8 // (C) 2002 Duco Fijma
9 // Copyright (C) 2004-2005 Novell, Inc (http://www.novell.com)
11 // References
12 // 1. UUIDs and GUIDs (DRAFT), Section 3.4
13 // http://www.ics.uci.edu/~ejw/authoring/uuid-guid/draft-leach-uuids-guids-01.txt
15 // Permission is hereby granted, free of charge, to any person obtaining
16 // a copy of this software and associated documentation files (the
17 // "Software"), to deal in the Software without restriction, including
18 // without limitation the rights to use, copy, modify, merge, publish,
19 // distribute, sublicense, and/or sell copies of the Software, and to
20 // permit persons to whom the Software is furnished to do so, subject to
21 // the following conditions:
22 //
23 // The above copyright notice and this permission notice shall be
24 // included in all copies or substantial portions of the Software.
25 //
26 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
27 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
28 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
29 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
30 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
31 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
32 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
35 using System.Runtime.InteropServices;
36 using System.Security.Cryptography;
37 using System.Text;
39 namespace System {
41 [Serializable]
42 [StructLayout (LayoutKind.Sequential)]
43 [ComVisible (true)]
44 public struct Guid : IFormattable, IComparable, IComparable<Guid>, IEquatable<Guid> {
45 #if MONOTOUCH
46 static Guid () {
47 if (MonoTouchAOTHelper.FalseFlag) {
48 var comparer = new System.Collections.Generic.GenericComparer <Guid> ();
49 var eqcomparer = new System.Collections.Generic.GenericEqualityComparer <Guid> ();
52 #endif
53 private int _a; //_timeLow;
54 private short _b; //_timeMid;
55 private short _c; //_timeHighAndVersion;
56 private byte _d; //_clockSeqHiAndReserved;
57 private byte _e; //_clockSeqLow;
58 private byte _f; //_node0;
59 private byte _g; //_node1;
60 private byte _h; //_node2;
61 private byte _i; //_node3;
62 private byte _j; //_node4;
63 private byte _k; //_node5;
65 enum Format {
66 N, // 00000000000000000000000000000000
67 D, // 00000000-0000-0000-0000-000000000000
68 B, // {00000000-0000-0000-0000-000000000000}
69 P, // (00000000-0000-0000-0000-000000000000)
70 X, // {0x00000000,0x0000,0x0000,{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}}
73 class GuidParser {
75 private string _src;
76 private int _length;
77 private int _cur;
79 public GuidParser (string src)
81 _src = src;
82 Reset ();
85 void Reset ()
87 _cur = 0;
88 _length = _src.Length;
91 bool Eof {
92 get { return _cur >= _length; }
95 static bool HasHyphen (Format format)
97 switch (format) {
98 case Format.D:
99 case Format.B:
100 case Format.P:
101 return true;
102 default:
103 return false;
107 bool TryParseNDBP (Format format, out Guid guid)
109 ulong a, b, c;
110 guid = new Guid ();
112 if (format == Format.B && !ParseChar ('{'))
113 return false;
115 if (format == Format.P && !ParseChar ('('))
116 return false;
118 if (!ParseHex (8, true, out a))
119 return false;
121 var has_hyphen = HasHyphen (format);
123 if (has_hyphen && !ParseChar ('-'))
124 return false;
126 if (!ParseHex (4, true, out b))
127 return false;
129 if (has_hyphen && !ParseChar ('-'))
130 return false;
132 if (!ParseHex (4, true, out c))
133 return false;
135 if (has_hyphen && !ParseChar ('-'))
136 return false;
138 var d = new byte [8];
139 for (int i = 0; i < d.Length; i++) {
140 ulong dd;
141 if (!ParseHex (2, true, out dd))
142 return false;
144 if (i == 1 && has_hyphen && !ParseChar ('-'))
145 return false;
147 d [i] = (byte) dd;
150 if (format == Format.B && !ParseChar ('}'))
151 return false;
153 if (format == Format.P && !ParseChar (')'))
154 return false;
156 guid = new Guid ((int) a, (short) b, (short) c, d);
157 return true;
160 bool TryParseX (out Guid guid)
162 ulong a, b, c;
163 guid = new Guid ();
165 if (!(ParseChar ('{')
166 && ParseHexPrefix ()
167 && ParseHex (8, false, out a)
168 && ParseChar (',')
169 && ParseHexPrefix ()
170 && ParseHex (4, false, out b)
171 && ParseChar (',')
172 && ParseHexPrefix ()
173 && ParseHex (4, false, out c)
174 && ParseChar (',')
175 && ParseChar ('{'))) {
177 return false;
180 var d = new byte [8];
181 for (int i = 0; i < d.Length; ++i) {
182 ulong dd;
184 if (!(ParseHexPrefix () && ParseHex (2, false, out dd)))
185 return false;
187 d [i] = (byte) dd;
189 if (i != 7 && !ParseChar (','))
190 return false;
193 if (!(ParseChar ('}') && ParseChar ('}')))
194 return false;
196 guid = new Guid ((int) a, (short) b, (short) c, d);
197 return true;
200 bool ParseHexPrefix ()
202 if (!ParseChar ('0'))
203 return false;
205 return ParseChar ('x');
208 bool ParseChar (char c)
210 if (!Eof && _src [_cur] == c) {
211 _cur++;
212 return true;
215 return false;
218 bool ParseHex (int length, bool strict, out ulong res)
220 res = 0;
222 for (int i = 0; i < length; i++) {
223 if (Eof)
224 return !(strict && (i + 1 != length));
226 char c = Char.ToLowerInvariant (_src[_cur]);
227 if (Char.IsDigit (c)) {
228 res = res * 16 + c - '0';
229 _cur++;
230 } else if (c >= 'a' && c <= 'f') {
231 res = res * 16 + c - 'a' + 10;
232 _cur++;
233 } else {
234 if (!strict)
235 return true;
237 return !(strict && (i + 1 != length));
241 return true;
244 public bool Parse (Format format, out Guid guid)
246 if (format == Format.X)
247 return TryParseX (out guid);
249 return TryParseNDBP (format, out guid);
252 public bool Parse (out Guid guid)
254 if (TryParseNDBP (Format.N, out guid))
255 return true;
257 Reset ();
258 if (TryParseNDBP (Format.D, out guid))
259 return true;
261 Reset ();
262 if (TryParseNDBP (Format.B, out guid))
263 return true;
265 Reset ();
266 if (TryParseNDBP (Format.P, out guid))
267 return true;
269 Reset ();
270 return TryParseX (out guid);
274 private static void CheckNull (object o)
276 if (o == null) {
277 throw new ArgumentNullException (Locale.GetText ("Value cannot be null."));
281 private static void CheckLength (byte[] o, int l)
283 if (o . Length != l) {
284 throw new ArgumentException (String.Format (Locale.GetText ("Array should be exactly {0} bytes long."), l));
288 private static void CheckArray (byte[] o, int l)
290 CheckNull (o);
291 CheckLength (o, l);
294 public Guid (byte[] b)
296 CheckArray (b, 16);
297 _a = Mono.Security.BitConverterLE.ToInt32 (b, 0);
298 _b = Mono.Security.BitConverterLE.ToInt16 (b, 4);
299 _c = Mono.Security.BitConverterLE.ToInt16 (b, 6);
300 _d = b [8];
301 _e = b [9];
302 _f = b [10];
303 _g = b [11];
304 _h = b [12];
305 _i = b [13];
306 _j = b [14];
307 _k = b [15];
310 public Guid (string g)
312 CheckNull (g);
313 g = g.Trim();
314 var parser = new GuidParser (g);
315 Guid guid;
316 if (!parser.Parse (out guid))
317 throw CreateFormatException (g);
319 this = guid;
322 static Exception CreateFormatException (string s)
324 return new FormatException (string.Format ("Invalid Guid format: {0}", s));
327 public Guid (int a, short b, short c, byte[] d)
329 CheckArray (d, 8);
330 _a = (int) a;
331 _b = (short) b;
332 _c = (short) c;
333 _d = d [0];
334 _e = d [1];
335 _f = d [2];
336 _g = d [3];
337 _h = d [4];
338 _i = d [5];
339 _j = d [6];
340 _k = d [7];
343 public Guid (int a, short b, short c, byte d, byte e, byte f, byte g, byte h, byte i, byte j, byte k)
345 _a = a;
346 _b = b;
347 _c = c;
348 _d = d;
349 _e = e;
350 _f = f;
351 _g = g;
352 _h = h;
353 _i = i;
354 _j = j;
355 _k = k;
358 [CLSCompliant (false)]
359 public Guid (uint a, ushort b, ushort c, byte d, byte e, byte f, byte g, byte h, byte i, byte j, byte k)
360 : this((int) a, (short) b, (short) c, d, e, f, g, h, i, j, k)
364 public static readonly Guid Empty = new Guid (0,0,0,0,0,0,0,0,0,0,0);
366 private static int Compare (int x, int y)
368 if (x < y) {
369 return -1;
371 else {
372 return 1;
376 public int CompareTo (object value)
378 if (value == null)
379 return 1;
381 if (!(value is Guid)) {
382 throw new ArgumentException ("value", Locale.GetText (
383 "Argument of System.Guid.CompareTo should be a Guid."));
386 return CompareTo ((Guid)value);
389 public override bool Equals (object o)
391 if (o is Guid)
392 return CompareTo ((Guid)o) == 0;
393 return false;
396 public int CompareTo (Guid value)
398 if (_a != value._a) {
399 return Compare (_a, value._a);
401 else if (_b != value._b) {
402 return Compare (_b, value._b);
404 else if (_c != value._c) {
405 return Compare (_c, value._c);
407 else if (_d != value._d) {
408 return Compare (_d, value._d);
410 else if (_e != value._e) {
411 return Compare (_e, value._e);
413 else if (_f != value._f) {
414 return Compare (_f, value._f);
416 else if (_g != value._g) {
417 return Compare (_g, value._g);
419 else if (_h != value._h) {
420 return Compare (_h, value._h);
422 else if (_i != value._i) {
423 return Compare (_i, value._i);
425 else if (_j != value._j) {
426 return Compare (_j, value._j);
428 else if (_k != value._k) {
429 return Compare (_k, value._k);
431 return 0;
434 public bool Equals (Guid g)
436 return CompareTo (g) == 0;
439 public override int GetHashCode ()
441 int res;
443 res = (int) _a;
444 res = res ^ ((int) _b << 16 | _c);
445 res = res ^ ((int) _d << 24);
446 res = res ^ ((int) _e << 16);
447 res = res ^ ((int) _f << 8);
448 res = res ^ ((int) _g);
449 res = res ^ ((int) _h << 24);
450 res = res ^ ((int) _i << 16);
451 res = res ^ ((int) _j << 8);
452 res = res ^ ((int) _k);
454 return res;
457 private static char ToHex (int b)
459 return (char)((b<0xA)?('0' + b):('a' + b - 0xA));
462 private static object _rngAccess = new object ();
463 private static RandomNumberGenerator _rng;
464 private static RandomNumberGenerator _fastRng;
466 // generated as per section 3.4 of the specification
467 public static Guid NewGuid ()
469 byte[] b = new byte [16];
471 // thread-safe access to the prng
472 lock (_rngAccess) {
473 if (_rng == null)
474 _rng = RandomNumberGenerator.Create ();
475 _rng.GetBytes (b);
478 Guid res = new Guid (b);
479 // Mask in Variant 1-0 in Bit[7..6]
480 res._d = (byte) ((res._d & 0x3fu) | 0x80u);
481 // Mask in Version 4 (random based Guid) in Bits[15..13]
482 res._c = (short) ((res._c & 0x0fffu) | 0x4000u);
484 return res;
487 // used in ModuleBuilder so mcs doesn't need to invoke
488 // CryptoConfig for simple assemblies.
489 internal static byte[] FastNewGuidArray ()
491 byte[] guid = new byte [16];
493 // thread-safe access to the prng
494 lock (_rngAccess) {
495 // if known, use preferred RNG
496 if (_rng != null)
497 _fastRng = _rng;
498 // else use hardcoded default RNG (bypassing CryptoConfig)
499 if (_fastRng == null)
500 _fastRng = new RNGCryptoServiceProvider ();
501 _fastRng.GetBytes (guid);
504 // Mask in Variant 1-0 in Bit[7..6]
505 guid [8] = (byte) ((guid [8] & 0x3f) | 0x80);
506 // Mask in Version 4 (random based Guid) in Bits[15..13]
507 guid [7] = (byte) ((guid [7] & 0x0f) | 0x40);
509 return guid;
512 public byte[] ToByteArray ()
514 byte[] res = new byte[16];
515 byte[] tmp;
516 int d = 0;
517 int s;
519 tmp = Mono.Security.BitConverterLE.GetBytes(_a);
520 for (s=0; s<4; ++s) {
521 res[d++] = tmp[s];
524 tmp = Mono.Security.BitConverterLE.GetBytes(_b);
525 for (s=0; s<2; ++s) {
526 res[d++] = tmp[s];
529 tmp = Mono.Security.BitConverterLE.GetBytes(_c);
530 for (s=0; s<2; ++s) {
531 res[d++] = tmp[s];
534 res[8] = _d;
535 res[9] = _e;
536 res[10] = _f;
537 res[11] = _g;
538 res[12] = _h;
539 res[13] = _i;
540 res[14] = _j;
541 res[15] = _k;
543 return res;
546 static void AppendInt (StringBuilder builder, int value) {
547 builder.Append (ToHex ((value >> 28) & 0xf));
548 builder.Append (ToHex ((value >> 24) & 0xf));
549 builder.Append (ToHex ((value >> 20) & 0xf));
550 builder.Append (ToHex ((value >> 16) & 0xf));
551 builder.Append (ToHex ((value >> 12) & 0xf));
552 builder.Append (ToHex ((value >> 8) & 0xf));
553 builder.Append (ToHex ((value >> 4) & 0xf));
554 builder.Append (ToHex (value & 0xf));
557 static void AppendShort (StringBuilder builder, short value) {
558 builder.Append (ToHex ((value >> 12) & 0xf));
559 builder.Append (ToHex ((value >> 8) & 0xf));
560 builder.Append (ToHex ((value >> 4) & 0xf));
561 builder.Append (ToHex (value & 0xf));
564 static void AppendByte (StringBuilder builder, byte value) {
565 builder.Append (ToHex ((value >> 4) & 0xf));
566 builder.Append (ToHex (value & 0xf));
569 private string BaseToString (bool h, bool p, bool b)
571 StringBuilder res = new StringBuilder (40);
573 if (p) {
574 res.Append ('(');
575 } else if (b) {
576 res.Append ('{');
579 AppendInt (res, _a);
580 if (h) {
581 res.Append ('-');
583 AppendShort (res, _b);
584 if (h) {
585 res.Append ('-');
587 AppendShort (res, _c);
588 if (h) {
589 res.Append ('-');
592 AppendByte (res, _d);
593 AppendByte (res, _e);
595 if (h) {
596 res.Append ('-');
599 AppendByte (res, _f);
600 AppendByte (res, _g);
601 AppendByte (res, _h);
602 AppendByte (res, _i);
603 AppendByte (res, _j);
604 AppendByte (res, _k);
607 if (p) {
608 res.Append (')');
609 } else if (b) {
610 res.Append ('}');
613 return res.ToString ();
616 public override string ToString ()
618 return BaseToString (true, false, false);
621 public string ToString (string format)
623 bool h = true;
624 bool p = false;
625 bool b = false;
627 if (format != null) {
628 string f = format.ToLowerInvariant();
630 if (f == "b") {
631 b = true;
633 else if (f == "p") {
634 p = true;
636 else if (f == "n") {
637 h = false;
639 else if (f != "d" && f != String.Empty) {
640 throw new FormatException (Locale.GetText (
641 "Argument to Guid.ToString(string format) should be \"b\", \"B\", \"d\", \"D\", \"n\", \"N\", \"p\" or \"P\""));
645 return BaseToString (h, p, b);
648 public string ToString (string format, IFormatProvider provider)
650 return ToString (format);
653 public static bool operator == (Guid a, Guid b)
655 return a.Equals(b);
658 public static bool operator != (Guid a, Guid b)
660 return !( a.Equals (b) );
663 #if NET_4_0
664 public static Guid Parse (string input)
666 Guid guid;
667 if (!TryParse (input, out guid))
668 throw CreateFormatException (input);
670 return guid;
673 public static Guid ParseExact (string input, string format)
675 Guid guid;
676 if (!TryParseExact (input, format, out guid))
677 throw CreateFormatException (input);
679 return guid;
682 public static bool TryParse (string input, out Guid result)
684 if (input == null)
685 throw new ArgumentNullException ("input");
687 var parser = new GuidParser (input);
688 return parser.Parse (out result);
691 public static bool TryParseExact (string input, string format, out Guid result)
693 if (input == null)
694 throw new ArgumentNullException ("input");
695 if (format == null)
696 throw new ArgumentNullException ("format");
698 var parser = new GuidParser (input);
699 return parser.Parse (ParseFormat (format), out result);
702 static Format ParseFormat (string format)
704 if (format.Length != 1)
705 throw new ArgumentException ("Wrong format");
707 switch (format [0]) {
708 case 'N':
709 return Format.N;
710 case 'D':
711 return Format.D;
712 case 'B':
713 return Format.B;
714 case 'P':
715 return Format.P;
716 case 'X':
717 return Format.X;
720 throw new ArgumentException ("Wrong format");
722 #endif