Remove no longer used BOOTSTRAP conditionals
[mono.git] / mcs / class / corlib / System / Guid.cs
1 //
2 // System.Guid.cs
3 //
4 // Authors:
5 //      Duco Fijma (duco@lorentz.xs4all.nl)
6 //      Sebastien Pouliot (sebastien@ximian.com)
7 //      Jb Evain (jbevain@novell.com)
8 //
9 // (C) 2002 Duco Fijma
10 // Copyright (C) 2004-2010 Novell, Inc (http://www.novell.com)
11 //
12 // References
13 // 1.   UUIDs and GUIDs (DRAFT), Section 3.4
14 //      http://www.ics.uci.edu/~ejw/authoring/uuid-guid/draft-leach-uuids-guids-01.txt 
15 //
16 // Permission is hereby granted, free of charge, to any person obtaining
17 // a copy of this software and associated documentation files (the
18 // "Software"), to deal in the Software without restriction, including
19 // without limitation the rights to use, copy, modify, merge, publish,
20 // distribute, sublicense, and/or sell copies of the Software, and to
21 // permit persons to whom the Software is furnished to do so, subject to
22 // the following conditions:
23 // 
24 // The above copyright notice and this permission notice shall be
25 // included in all copies or substantial portions of the Software.
26 // 
27 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
28 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
29 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
30 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
31 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
32 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
33 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
34 //
35
36 using System.Runtime.InteropServices;
37 using System.Security.Cryptography;
38 using System.Text;
39
40 namespace System {
41
42         [Serializable]
43         [StructLayout (LayoutKind.Sequential)]
44         [ComVisible (true)]
45         public struct Guid : IFormattable, IComparable, IComparable<Guid>, IEquatable<Guid> {
46 #if MONOTOUCH
47                 static Guid () {
48                         if (MonoTouchAOTHelper.FalseFlag) {
49                                 var comparer = new System.Collections.Generic.GenericComparer <Guid> ();
50                                 var eqcomparer = new System.Collections.Generic.GenericEqualityComparer <Guid> ();
51                         }
52                 }
53 #endif
54                 private int _a; //_timeLow;
55                 private short _b; //_timeMid;
56                 private short _c; //_timeHighAndVersion;
57                 private byte _d; //_clockSeqHiAndReserved;
58                 private byte _e; //_clockSeqLow;
59                 private byte _f; //_node0;
60                 private byte _g; //_node1;
61                 private byte _h; //_node2;
62                 private byte _i; //_node3;
63                 private byte _j; //_node4;
64                 private byte _k; //_node5;
65
66                 enum Format {
67                         N, // 00000000000000000000000000000000
68                         D, // 00000000-0000-0000-0000-000000000000
69                         B, // {00000000-0000-0000-0000-000000000000}
70                         P, // (00000000-0000-0000-0000-000000000000)
71                         X, // {0x00000000,0x0000,0x0000,{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}}
72                 }
73
74                 class GuidParser {
75
76                         private string _src;
77                         private int _length;
78                         private int _cur;
79
80                         public GuidParser (string src)
81                         {
82                                 _src = src;
83                                 Reset ();
84                         }
85
86                         void Reset ()
87                         {
88                                 _cur = 0;
89                                 _length = _src.Length;
90                         }
91
92                         bool Eof {
93                                 get { return _cur >= _length; }
94                         }
95
96                         static bool HasHyphen (Format format)
97                         {
98                                 switch (format) {
99                                 case Format.D:
100                                 case Format.B:
101                                 case Format.P:
102                                         return true;
103                                 default:
104                                         return false;
105                                 }
106                         }
107
108                         bool TryParseNDBP (Format format, out Guid guid)
109                         {
110                                 ulong a, b, c;
111                                 guid = new Guid ();
112
113                                 if (format == Format.B && !ParseChar ('{'))
114                                         return false;
115
116                                 if (format == Format.P && !ParseChar ('('))
117                                         return false;
118
119                                 if (!ParseHex (8, true, out a))
120                                         return false;
121
122                                 var has_hyphen = HasHyphen (format);
123
124                                 if (has_hyphen && !ParseChar ('-'))
125                                         return false;
126
127                                 if (!ParseHex (4, true, out b))
128                                         return false;
129
130                                 if (has_hyphen && !ParseChar ('-'))
131                                         return false;
132
133                                 if (!ParseHex (4, true, out c))
134                                         return false;
135
136                                 if (has_hyphen && !ParseChar ('-'))
137                                         return false;
138
139                                 var d = new byte [8];
140                                 for (int i = 0; i < d.Length; i++) {
141                                         ulong dd;
142                                         if (!ParseHex (2, true, out dd))
143                                                 return false;
144
145                                         if (i == 1 && has_hyphen && !ParseChar ('-'))
146                                                 return false;
147
148                                         d [i] = (byte) dd;
149                                 }
150
151                                 if (format == Format.B && !ParseChar ('}'))
152                                         return false;
153
154                                 if (format == Format.P && !ParseChar (')'))
155                                         return false;
156
157                                 if (!Eof)
158                                         return false;
159
160                                 guid = new Guid ((int) a, (short) b, (short) c, d);
161                                 return true;
162                         }
163
164                         bool TryParseX (out Guid guid)
165                         {
166                                 ulong a, b, c;
167                                 guid = new Guid ();
168
169                                 if (!(ParseChar ('{')
170                                         && ParseHexPrefix ()
171                                         && ParseHex (8, false, out a)
172                                         && ParseChar (',')
173                                         && ParseHexPrefix ()
174                                         && ParseHex (4, false, out b)
175                                         && ParseChar (',')
176                                         && ParseHexPrefix ()
177                                         && ParseHex (4, false, out c)
178                                         && ParseChar (',')
179                                         && ParseChar ('{'))) {
180
181                                         return false;
182                                 }
183
184                                 var d = new byte [8];
185                                 for (int i = 0; i < d.Length; ++i) {
186                                         ulong dd;
187
188                                         if (!(ParseHexPrefix () && ParseHex (2, false, out dd)))
189                                                 return false;
190
191                                         d [i] = (byte) dd;
192
193                                         if (i != 7 && !ParseChar (','))
194                                                 return false;
195                                 }
196
197                                 if (!(ParseChar ('}') && ParseChar ('}')))
198                                         return false;
199
200                                 if (!Eof)
201                                         return false;
202
203                                 guid = new Guid ((int) a, (short) b, (short) c, d);
204                                 return true;
205                         }
206
207                         bool ParseHexPrefix ()
208                         {
209                                 if (!ParseChar ('0'))
210                                         return false;
211
212                                 return ParseChar ('x');
213                         }
214
215                         bool ParseChar (char c)
216                         {
217                                 if (!Eof && _src [_cur] == c) {
218                                         _cur++;
219                                         return true;
220                                 }
221
222                                 return false;
223                         }
224
225                         bool ParseHex (int length, bool strict, out ulong res)
226                         {
227                                 res = 0;
228
229                                 for (int i = 0; i < length; i++) {
230                                         if (Eof)
231                                                 return !(strict && (i + 1 != length));
232
233                                         char c = Char.ToLowerInvariant (_src[_cur]);
234                                         if (Char.IsDigit (c)) {
235                                                 res = res * 16 + c - '0';
236                                                 _cur++;
237                                         } else if (c >= 'a' && c <= 'f') {
238                                                 res = res * 16 + c - 'a' + 10;
239                                                 _cur++;
240                                         } else {
241                                                 if (!strict)
242                                                         return true;
243
244                                                 return !(strict && (i + 1 != length));
245                                         }
246                                 }
247
248                                 return true;
249                         }
250
251                         public bool Parse (Format format, out Guid guid)
252                         {
253                                 if (format == Format.X)
254                                         return TryParseX (out guid);
255
256                                 return TryParseNDBP (format, out guid);
257                         }
258
259                         public bool Parse (out Guid guid)
260                         {
261                                 if (TryParseNDBP (Format.N, out guid))
262                                         return true;
263
264                                 Reset ();
265                                 if (TryParseNDBP (Format.D, out guid))
266                                         return true;
267
268                                 Reset ();
269                                 if (TryParseNDBP (Format.B, out guid))
270                                         return true;
271
272                                 Reset ();
273                                 if (TryParseNDBP (Format.P, out guid))
274                                         return true;
275
276                                 Reset ();
277                                 return TryParseX (out guid);
278                         }
279                 }
280
281                 private static void CheckNull (object o)
282                 {
283                         if (o == null) {
284                                 throw new ArgumentNullException (Locale.GetText ("Value cannot be null."));
285                         }
286                 }
287
288                 private static void CheckLength (byte[] o, int l)
289                 {
290                         if (o . Length != l) {
291                                 throw new ArgumentException (String.Format (Locale.GetText ("Array should be exactly {0} bytes long."), l));
292                         }
293                 }
294
295                 private static void CheckArray (byte[] o, int l)
296                 {
297                         CheckNull (o);
298                         CheckLength (o, l);
299                 }
300
301                 public Guid (byte[] b)
302                 {
303                         CheckArray (b, 16);
304                         _a = Mono.Security.BitConverterLE.ToInt32 (b, 0);
305                         _b = Mono.Security.BitConverterLE.ToInt16 (b, 4);
306                         _c = Mono.Security.BitConverterLE.ToInt16 (b, 6);
307                         _d = b [8];
308                         _e = b [9];
309                         _f = b [10];
310                         _g = b [11];
311                         _h = b [12];
312                         _i = b [13];
313                         _j = b [14];
314                         _k = b [15];
315                 }
316
317                 public Guid (string g)
318                 {
319                         CheckNull (g);
320                         g = g.Trim();
321                         var parser = new GuidParser (g);
322                         Guid guid;
323                         if (!parser.Parse (out guid))
324                                 throw CreateFormatException (g);
325
326                         this = guid;
327                 }
328
329                 static Exception CreateFormatException (string s)
330                 {
331                         return new FormatException (string.Format ("Invalid Guid format: {0}", s));
332                 }
333
334                 public Guid (int a, short b, short c, byte[] d)
335                 {
336                         CheckArray (d, 8);
337                         _a = (int) a;
338                         _b = (short) b;
339                         _c = (short) c;
340                         _d = d [0];
341                         _e = d [1];
342                         _f = d [2];
343                         _g = d [3];
344                         _h = d [4];
345                         _i = d [5];
346                         _j = d [6];
347                         _k = d [7];
348                 }
349
350                 public Guid (int a, short b, short c, byte d, byte e, byte f, byte g, byte h, byte i, byte j, byte k)
351                 {
352                         _a = a;
353                         _b = b;
354                         _c = c;
355                         _d = d;
356                         _e = e;
357                         _f = f;
358                         _g = g;
359                         _h = h;
360                         _i = i;
361                         _j = j;
362                         _k = k;
363                 }
364
365                 [CLSCompliant (false)]
366                 public Guid (uint a, ushort b, ushort c, byte d, byte e, byte f, byte g, byte h, byte i, byte j, byte k)
367                         : this((int) a, (short) b, (short) c, d, e, f, g, h, i, j, k)
368                 {
369                 }
370
371                 public static readonly Guid Empty = new Guid (0,0,0,0,0,0,0,0,0,0,0);
372
373                 private static int Compare (int x, int y)
374                 {
375                         if (x < y) {
376                                 return -1;
377                         }
378                         else {
379                                 return 1;
380                         }
381                 }
382
383                 public int CompareTo (object value)
384                 {
385                         if (value == null)
386                                 return 1;
387
388                         if (!(value is Guid)) {
389                                 throw new ArgumentException ("value", Locale.GetText (
390                                         "Argument of System.Guid.CompareTo should be a Guid."));
391                         }
392
393                         return CompareTo ((Guid)value);
394                 }
395
396                 public override bool Equals (object o)
397                 {
398                         if (o is Guid)
399                                 return CompareTo ((Guid)o) == 0;
400                         return false;
401                 }
402
403                 public int CompareTo (Guid value)
404                 {
405                         if (_a != value._a) {
406                                 return Compare (_a, value._a);
407                         }
408                         else if (_b != value._b) {
409                                 return Compare (_b, value._b);
410                         }
411                         else if (_c != value._c) {
412                                 return Compare (_c, value._c);
413                         }
414                         else if (_d != value._d) {
415                                 return Compare (_d, value._d);
416                         }
417                         else if (_e != value._e) {
418                                 return Compare (_e, value._e);
419                         }
420                         else if (_f != value._f) {
421                                 return Compare (_f, value._f);
422                         }
423                         else if (_g != value._g) {
424                                 return Compare (_g, value._g);
425                         }
426                         else if (_h != value._h) {
427                                 return Compare (_h, value._h);
428                         }
429                         else if (_i != value._i) {
430                                 return Compare (_i, value._i);
431                         }
432                         else if (_j != value._j) {
433                                 return Compare (_j, value._j);
434                         }
435                         else if (_k != value._k) {
436                                 return Compare (_k, value._k);
437                         }
438                         return 0;
439                 }
440
441                 public bool Equals (Guid g)
442                 {
443                         return CompareTo (g) == 0;
444                 }
445
446                 public override int GetHashCode ()
447                 {
448                         int res;
449         
450                         res = (int) _a; 
451                         res = res ^ ((int) _b << 16 | _c);
452                         res = res ^ ((int) _d << 24);
453                         res = res ^ ((int) _e << 16);
454                         res = res ^ ((int) _f << 8);
455                         res = res ^ ((int) _g);
456                         res = res ^ ((int) _h << 24);
457                         res = res ^ ((int) _i << 16);
458                         res = res ^ ((int) _j << 8);
459                         res = res ^ ((int) _k);
460
461                         return res;
462                 }
463
464                 private static char ToHex (int b)
465                 {
466                         return (char)((b<0xA)?('0' + b):('a' + b - 0xA));
467                 }
468
469                 private static object _rngAccess = new object ();
470                 private static RandomNumberGenerator _rng;
471                 private static RandomNumberGenerator _fastRng;
472
473                 // generated as per section 3.4 of the specification
474                 public static Guid NewGuid ()
475                 {
476                         byte[] b = new byte [16];
477
478                         // thread-safe access to the prng
479                         lock (_rngAccess) {
480                                 if (_rng == null)
481                                         _rng = RandomNumberGenerator.Create ();
482                                 _rng.GetBytes (b);
483                         }
484
485                         Guid res = new Guid (b);
486                         // Mask in Variant 1-0 in Bit[7..6]
487                         res._d = (byte) ((res._d & 0x3fu) | 0x80u);
488                         // Mask in Version 4 (random based Guid) in Bits[15..13]
489                         res._c = (short) ((res._c & 0x0fffu) | 0x4000u);
490
491                         return res;
492                 }
493
494                 // used in ModuleBuilder so mcs doesn't need to invoke 
495                 // CryptoConfig for simple assemblies.
496                 internal static byte[] FastNewGuidArray ()
497                 {
498                         byte[] guid = new byte [16];
499
500                         // thread-safe access to the prng
501                         lock (_rngAccess) {
502                                 // if known, use preferred RNG
503                                 if (_rng != null)
504                                         _fastRng = _rng;
505                                 // else use hardcoded default RNG (bypassing CryptoConfig)
506                                 if (_fastRng == null)
507                                         _fastRng = new RNGCryptoServiceProvider ();
508                                 _fastRng.GetBytes (guid);
509                         }
510
511                         // Mask in Variant 1-0 in Bit[7..6]
512                         guid [8] = (byte) ((guid [8] & 0x3f) | 0x80);
513                         // Mask in Version 4 (random based Guid) in Bits[15..13]
514                         guid [7] = (byte) ((guid [7] & 0x0f) | 0x40);
515
516                         return guid;
517                 }
518
519                 public byte[] ToByteArray ()
520                 {
521                         byte[] res = new byte[16];
522                         byte[] tmp;
523                         int d = 0;
524                         int s;
525
526                         tmp = Mono.Security.BitConverterLE.GetBytes(_a);
527                         for (s=0; s<4; ++s) {
528                                 res[d++] = tmp[s];
529                         }
530
531                         tmp = Mono.Security.BitConverterLE.GetBytes(_b);
532                         for (s=0; s<2; ++s) {
533                                 res[d++] = tmp[s];
534                         }
535
536                         tmp = Mono.Security.BitConverterLE.GetBytes(_c);
537                         for (s=0; s<2; ++s) {
538                                 res[d++] = tmp[s];
539                         }
540
541                         res[8] = _d;
542                         res[9] = _e;
543                         res[10] = _f;
544                         res[11] = _g;
545                         res[12] = _h;
546                         res[13] = _i;
547                         res[14] = _j;
548                         res[15] = _k;
549
550                         return res;
551                 }
552
553                 static void AppendInt (StringBuilder builder, int value) {
554                         builder.Append (ToHex ((value >> 28) & 0xf));
555                         builder.Append (ToHex ((value >> 24) & 0xf));
556                         builder.Append (ToHex ((value >> 20) & 0xf));
557                         builder.Append (ToHex ((value >> 16) & 0xf));
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));
562                 }
563
564                 static void AppendShort (StringBuilder builder, short value) {
565                         builder.Append (ToHex ((value >> 12) & 0xf));
566                         builder.Append (ToHex ((value >> 8) & 0xf));
567                         builder.Append (ToHex ((value >> 4) & 0xf));
568                         builder.Append (ToHex (value & 0xf));
569                 }
570
571                 static void AppendByte (StringBuilder builder, byte value) {
572                         builder.Append (ToHex ((value >> 4) & 0xf));
573                         builder.Append (ToHex (value & 0xf));
574                 }
575
576                 private string BaseToString (bool h, bool p, bool b)
577                 {
578                         StringBuilder res = new StringBuilder (40);
579                         
580                         if (p) {
581                                 res.Append ('(');
582                         } else if (b) {
583                                 res.Append ('{');
584                         }
585                 
586                         AppendInt (res, _a);
587                         if (h) {
588                                 res.Append ('-');
589                         }
590                         AppendShort (res, _b);
591                         if (h) {
592                                 res.Append ('-');
593                         }
594                         AppendShort (res, _c);
595                         if (h) {
596                                 res.Append ('-');
597                         }
598         
599                         AppendByte (res, _d);
600                         AppendByte (res, _e);
601
602                         if (h) {
603                                 res.Append ('-');
604                         }
605
606                         AppendByte (res, _f);
607                         AppendByte (res, _g);
608                         AppendByte (res, _h);
609                         AppendByte (res, _i);
610                         AppendByte (res, _j);
611                         AppendByte (res, _k);
612
613         
614                         if (p) {
615                                 res.Append (')');
616                         } else if (b) {
617                                 res.Append ('}');
618                         }
619                 
620                         return res.ToString ();
621                 }
622         
623                 public override string ToString ()
624                 {
625                         return BaseToString (true, false, false);
626                 }
627         
628                 public string ToString (string format)
629                 {
630                         bool h = true;
631                         bool p = false;
632                         bool b = false;
633         
634                         if (format != null) {
635                                 string f = format.ToLowerInvariant();
636         
637                                 if (f == "b") {
638                                         b = true;
639                                 }
640                                 else if (f == "p") {
641                                         p = true;
642                                 }
643                                 else if (f == "n") {
644                                         h = false;
645                                 }
646                                 else if (f != "d" && f != String.Empty) {
647                                         throw new FormatException (Locale.GetText (
648                                                 "Argument to Guid.ToString(string format) should be \"b\", \"B\", \"d\", \"D\", \"n\", \"N\", \"p\" or \"P\""));
649                                 }
650                         }
651
652                         return BaseToString (h, p, b);
653                 }
654
655                 public string ToString (string format, IFormatProvider provider)
656                 {
657                         return ToString (format);
658                 }
659
660                 public static bool operator == (Guid a, Guid b)
661                 {
662                         return a.Equals(b);
663                 }
664
665                 public static bool operator != (Guid a, Guid b)
666                 {
667                         return !( a.Equals (b) );
668                 }
669
670 #if NET_4_0 || MOONLIGHT
671                 public static Guid Parse (string input)
672                 {
673                         Guid guid;
674                         if (!TryParse (input, out guid))
675                                 throw CreateFormatException (input);
676
677                         return guid;
678                 }
679
680                 public static Guid ParseExact (string input, string format)
681                 {
682                         Guid guid;
683                         if (!TryParseExact (input, format, out guid))
684                                 throw CreateFormatException (input);
685
686                         return guid;
687                 }
688
689                 public static bool TryParse (string input, out Guid result)
690                 {
691                         if (input == null)
692                                 throw new ArgumentNullException ("input");
693
694                         var parser = new GuidParser (input);
695                         return parser.Parse (out result);
696                 }
697
698                 public static bool TryParseExact (string input, string format, out Guid result)
699                 {
700                         if (input == null)
701                                 throw new ArgumentNullException ("input");
702                         if (format == null)
703                                 throw new ArgumentNullException ("format");
704
705                         var parser = new GuidParser (input);
706                         return parser.Parse (ParseFormat (format), out result);
707                 }
708
709                 static Format ParseFormat (string format)
710                 {
711                         if (format.Length != 1)
712                                 throw new ArgumentException ("Wrong format");
713
714                         switch (format [0]) {
715                         case 'N':
716                                 return Format.N;
717                         case 'D':
718                                 return Format.D;
719                         case 'B':
720                                 return Format.B;
721                         case 'P':
722                                 return Format.P;
723                         case 'X':
724                                 return Format.X;
725                         }
726
727                         throw new ArgumentException ("Wrong format");
728                 }
729 #endif
730         }
731 }