New tests.
[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 //
8 // (C) 2002 Duco Fijma
9 // Copyright (C) 2004-2005 Novell, Inc (http://www.novell.com)
10 //
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 
14 //
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.
33 //
34
35 using System.Runtime.InteropServices;
36 using System.Security.Cryptography;
37 using System.Text;
38
39 namespace System {
40
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> ();
50                         }
51                 }
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;
64
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}}
71                 }
72
73                 class GuidParser {
74
75                         private string _src;
76                         private int _length;
77                         private int _cur;
78
79                         public GuidParser (string src)
80                         {
81                                 _src = src;
82                                 Reset ();
83                         }
84
85                         void Reset ()
86                         {
87                                 _cur = 0;
88                                 _length = _src.Length;
89                         }
90
91                         bool Eof {
92                                 get { return _cur >= _length; }
93                         }
94
95                         static bool HasHyphen (Format format)
96                         {
97                                 switch (format) {
98                                 case Format.D:
99                                 case Format.B:
100                                 case Format.P:
101                                         return true;
102                                 default:
103                                         return false;
104                                 }
105                         }
106
107                         bool TryParseNDBP (Format format, out Guid guid)
108                         {
109                                 ulong a, b, c;
110                                 guid = new Guid ();
111
112                                 if (format == Format.B && !ParseChar ('{'))
113                                         return false;
114
115                                 if (format == Format.P && !ParseChar ('('))
116                                         return false;
117
118                                 if (!ParseHex (8, true, out a))
119                                         return false;
120
121                                 var has_hyphen = HasHyphen (format);
122
123                                 if (has_hyphen && !ParseChar ('-'))
124                                         return false;
125
126                                 if (!ParseHex (4, true, out b))
127                                         return false;
128
129                                 if (has_hyphen && !ParseChar ('-'))
130                                         return false;
131
132                                 if (!ParseHex (4, true, out c))
133                                         return false;
134
135                                 if (has_hyphen && !ParseChar ('-'))
136                                         return false;
137
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;
143
144                                         if (i == 1 && has_hyphen && !ParseChar ('-'))
145                                                 return false;
146
147                                         d [i] = (byte) dd;
148                                 }
149
150                                 if (format == Format.B && !ParseChar ('}'))
151                                         return false;
152
153                                 if (format == Format.P && !ParseChar (')'))
154                                         return false;
155
156                                 guid = new Guid ((int) a, (short) b, (short) c, d);
157                                 return true;
158                         }
159
160                         bool TryParseX (out Guid guid)
161                         {
162                                 ulong a, b, c;
163                                 guid = new Guid ();
164
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 ('{'))) {
176
177                                         return false;
178                                 }
179
180                                 var d = new byte [8];
181                                 for (int i = 0; i < d.Length; ++i) {
182                                         ulong dd;
183
184                                         if (!(ParseHexPrefix () && ParseHex (2, false, out dd)))
185                                                 return false;
186
187                                         d [i] = (byte) dd;
188
189                                         if (i != 7 && !ParseChar (','))
190                                                 return false;
191                                 }
192
193                                 if (!(ParseChar ('}') && ParseChar ('}')))
194                                         return false;
195
196                                 guid = new Guid ((int) a, (short) b, (short) c, d);
197                                 return true;
198                         }
199
200                         bool ParseHexPrefix ()
201                         {
202                                 if (!ParseChar ('0'))
203                                         return false;
204
205                                 return ParseChar ('x');
206                         }
207
208                         bool ParseChar (char c)
209                         {
210                                 if (!Eof && _src [_cur] == c) {
211                                         _cur++;
212                                         return true;
213                                 }
214
215                                 return false;
216                         }
217
218                         bool ParseHex (int length, bool strict, out ulong res)
219                         {
220                                 res = 0;
221
222                                 for (int i = 0; i < length; i++) {
223                                         if (Eof)
224                                                 return !(strict && (i + 1 != length));
225
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;
236
237                                                 return !(strict && (i + 1 != length));
238                                         }
239                                 }
240
241                                 return true;
242                         }
243
244                         public bool Parse (Format format, out Guid guid)
245                         {
246                                 if (format == Format.X)
247                                         return TryParseX (out guid);
248
249                                 return TryParseNDBP (format, out guid);
250                         }
251
252                         public bool Parse (out Guid guid)
253                         {
254                                 if (TryParseNDBP (Format.N, out guid))
255                                         return true;
256
257                                 Reset ();
258                                 if (TryParseNDBP (Format.D, out guid))
259                                         return true;
260
261                                 Reset ();
262                                 if (TryParseNDBP (Format.B, out guid))
263                                         return true;
264
265                                 Reset ();
266                                 if (TryParseNDBP (Format.P, out guid))
267                                         return true;
268
269                                 Reset ();
270                                 return TryParseX (out guid);
271                         }
272                 }
273
274                 private static void CheckNull (object o)
275                 {
276                         if (o == null) {
277                                 throw new ArgumentNullException (Locale.GetText ("Value cannot be null."));
278                         }
279                 }
280
281                 private static void CheckLength (byte[] o, int l)
282                 {
283                         if (o . Length != l) {
284                                 throw new ArgumentException (String.Format (Locale.GetText ("Array should be exactly {0} bytes long."), l));
285                         }
286                 }
287
288                 private static void CheckArray (byte[] o, int l)
289                 {
290                         CheckNull (o);
291                         CheckLength (o, l);
292                 }
293
294                 public Guid (byte[] b)
295                 {
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];
308                 }
309
310                 public Guid (string g)
311                 {
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);
318
319                         this = guid;
320                 }
321
322                 static Exception CreateFormatException (string s)
323                 {
324                         return new FormatException (string.Format ("Invalid Guid format: {0}", s));
325                 }
326
327                 public Guid (int a, short b, short c, byte[] d)
328                 {
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];
341                 }
342
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)
344                 {
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;
356                 }
357
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)
361                 {
362                 }
363
364                 public static readonly Guid Empty = new Guid (0,0,0,0,0,0,0,0,0,0,0);
365
366                 private static int Compare (int x, int y)
367                 {
368                         if (x < y) {
369                                 return -1;
370                         }
371                         else {
372                                 return 1;
373                         }
374                 }
375
376                 public int CompareTo (object value)
377                 {
378                         if (value == null)
379                                 return 1;
380
381                         if (!(value is Guid)) {
382                                 throw new ArgumentException ("value", Locale.GetText (
383                                         "Argument of System.Guid.CompareTo should be a Guid."));
384                         }
385
386                         return CompareTo ((Guid)value);
387                 }
388
389                 public override bool Equals (object o)
390                 {
391                         if (o is Guid)
392                                 return CompareTo ((Guid)o) == 0;
393                         return false;
394                 }
395
396                 public int CompareTo (Guid value)
397                 {
398                         if (_a != value._a) {
399                                 return Compare (_a, value._a);
400                         }
401                         else if (_b != value._b) {
402                                 return Compare (_b, value._b);
403                         }
404                         else if (_c != value._c) {
405                                 return Compare (_c, value._c);
406                         }
407                         else if (_d != value._d) {
408                                 return Compare (_d, value._d);
409                         }
410                         else if (_e != value._e) {
411                                 return Compare (_e, value._e);
412                         }
413                         else if (_f != value._f) {
414                                 return Compare (_f, value._f);
415                         }
416                         else if (_g != value._g) {
417                                 return Compare (_g, value._g);
418                         }
419                         else if (_h != value._h) {
420                                 return Compare (_h, value._h);
421                         }
422                         else if (_i != value._i) {
423                                 return Compare (_i, value._i);
424                         }
425                         else if (_j != value._j) {
426                                 return Compare (_j, value._j);
427                         }
428                         else if (_k != value._k) {
429                                 return Compare (_k, value._k);
430                         }
431                         return 0;
432                 }
433
434                 public bool Equals (Guid g)
435                 {
436                         return CompareTo (g) == 0;
437                 }
438
439                 public override int GetHashCode ()
440                 {
441                         int res;
442         
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);
453
454                         return res;
455                 }
456
457                 private static char ToHex (int b)
458                 {
459                         return (char)((b<0xA)?('0' + b):('a' + b - 0xA));
460                 }
461
462                 private static object _rngAccess = new object ();
463                 private static RandomNumberGenerator _rng;
464                 private static RandomNumberGenerator _fastRng;
465
466                 // generated as per section 3.4 of the specification
467                 public static Guid NewGuid ()
468                 {
469                         byte[] b = new byte [16];
470
471                         // thread-safe access to the prng
472                         lock (_rngAccess) {
473                                 if (_rng == null)
474                                         _rng = RandomNumberGenerator.Create ();
475                                 _rng.GetBytes (b);
476                         }
477
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);
483
484                         return res;
485                 }
486
487                 // used in ModuleBuilder so mcs doesn't need to invoke 
488                 // CryptoConfig for simple assemblies.
489                 internal static byte[] FastNewGuidArray ()
490                 {
491                         byte[] guid = new byte [16];
492
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);
502                         }
503
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);
508
509                         return guid;
510                 }
511
512                 public byte[] ToByteArray ()
513                 {
514                         byte[] res = new byte[16];
515                         byte[] tmp;
516                         int d = 0;
517                         int s;
518
519                         tmp = Mono.Security.BitConverterLE.GetBytes(_a);
520                         for (s=0; s<4; ++s) {
521                                 res[d++] = tmp[s];
522                         }
523
524                         tmp = Mono.Security.BitConverterLE.GetBytes(_b);
525                         for (s=0; s<2; ++s) {
526                                 res[d++] = tmp[s];
527                         }
528
529                         tmp = Mono.Security.BitConverterLE.GetBytes(_c);
530                         for (s=0; s<2; ++s) {
531                                 res[d++] = tmp[s];
532                         }
533
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;
542
543                         return res;
544                 }
545
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));
555                 }
556
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));
562                 }
563
564                 static void AppendByte (StringBuilder builder, byte value) {
565                         builder.Append (ToHex ((value >> 4) & 0xf));
566                         builder.Append (ToHex (value & 0xf));
567                 }
568
569                 private string BaseToString (bool h, bool p, bool b)
570                 {
571                         StringBuilder res = new StringBuilder (40);
572                         
573                         if (p) {
574                                 res.Append ('(');
575                         } else if (b) {
576                                 res.Append ('{');
577                         }
578                 
579                         AppendInt (res, _a);
580                         if (h) {
581                                 res.Append ('-');
582                         }
583                         AppendShort (res, _b);
584                         if (h) {
585                                 res.Append ('-');
586                         }
587                         AppendShort (res, _c);
588                         if (h) {
589                                 res.Append ('-');
590                         }
591         
592                         AppendByte (res, _d);
593                         AppendByte (res, _e);
594
595                         if (h) {
596                                 res.Append ('-');
597                         }
598
599                         AppendByte (res, _f);
600                         AppendByte (res, _g);
601                         AppendByte (res, _h);
602                         AppendByte (res, _i);
603                         AppendByte (res, _j);
604                         AppendByte (res, _k);
605
606         
607                         if (p) {
608                                 res.Append (')');
609                         } else if (b) {
610                                 res.Append ('}');
611                         }
612                 
613                         return res.ToString ();
614                 }
615         
616                 public override string ToString ()
617                 {
618                         return BaseToString (true, false, false);
619                 }
620         
621                 public string ToString (string format)
622                 {
623                         bool h = true;
624                         bool p = false;
625                         bool b = false;
626         
627                         if (format != null) {
628                                 string f = format.ToLowerInvariant();
629         
630                                 if (f == "b") {
631                                         b = true;
632                                 }
633                                 else if (f == "p") {
634                                         p = true;
635                                 }
636                                 else if (f == "n") {
637                                         h = false;
638                                 }
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\""));
642                                 }
643                         }
644
645                         return BaseToString (h, p, b);
646                 }
647
648                 public string ToString (string format, IFormatProvider provider)
649                 {
650                         return ToString (format);
651                 }
652
653                 public static bool operator == (Guid a, Guid b)
654                 {
655                         return a.Equals(b);
656                 }
657
658                 public static bool operator != (Guid a, Guid b)
659                 {
660                         return !( a.Equals (b) );
661                 }
662
663 #if NET_4_0
664                 public static Guid Parse (string input)
665                 {
666                         Guid guid;
667                         if (!TryParse (input, out guid))
668                                 throw CreateFormatException (input);
669
670                         return guid;
671                 }
672
673                 public static Guid ParseExact (string input, string format)
674                 {
675                         Guid guid;
676                         if (!TryParseExact (input, format, out guid))
677                                 throw CreateFormatException (input);
678
679                         return guid;
680                 }
681
682                 public static bool TryParse (string input, out Guid result)
683                 {
684                         if (input == null)
685                                 throw new ArgumentNullException ("input");
686
687                         var parser = new GuidParser (input);
688                         return parser.Parse (out result);
689                 }
690
691                 public static bool TryParseExact (string input, string format, out Guid result)
692                 {
693                         if (input == null)
694                                 throw new ArgumentNullException ("input");
695                         if (format == null)
696                                 throw new ArgumentNullException ("format");
697
698                         var parser = new GuidParser (input);
699                         return parser.Parse (ParseFormat (format), out result);
700                 }
701
702                 static Format ParseFormat (string format)
703                 {
704                         if (format.Length != 1)
705                                 throw new ArgumentException ("Wrong format");
706
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;
718                         }
719
720                         throw new ArgumentException ("Wrong format");
721                 }
722 #endif
723         }
724 }