2010-03-12 Jb Evain <jbevain@novell.com>
[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                 internal class GuidParser
66                 {
67                         private string _src;
68                         private int _length;
69                         private int _cur;
70
71                         public GuidParser (string src)
72                         {
73                                 _src = src;
74                                 Reset ();
75                         }
76
77                         private void Reset ()
78                         {
79                                 _cur = 0;
80                                 _length = _src.Length;
81                         }
82
83                         private bool AtEnd ()
84                         {
85                                 return _cur >= _length;
86                         }
87
88                         private void ThrowFormatException ()
89                         {
90                                 throw new FormatException (Locale.GetText ("Invalid format for Guid.Guid(string)."));
91                         }
92
93                         private ulong ParseHex(int length, bool strictLength)
94                         {
95                                 ulong res = 0;
96                                 int i;
97                                 bool end = false;
98
99                                 for (i=0; (!end) && i<length; ++i) {
100                                         if (AtEnd ()) {
101                                                 if (strictLength || i==0) {
102                                                         ThrowFormatException ();
103                                                 }
104                                                 else {
105                                                         end = true;
106                                                 }
107                                         }
108                                         else {
109                                                 char c = Char.ToLowerInvariant (_src[_cur]);
110                                                 if (Char.IsDigit (c)) {
111                                                         res = res * 16 + c - '0';
112                                                         _cur++;
113                                                 }
114                                                 else if (c >= 'a' && c <= 'f') {
115                                                         res = res * 16 + c - 'a' + 10;
116                                                         _cur++;
117                                                 }
118                                                 else {
119                                                         if (strictLength || i==0) {
120                                                                 ThrowFormatException ();
121                                                         }
122                                                         else {
123                                                                 end = true;
124                                                         }
125                                                 }
126                                         }
127                                 }
128                                 return res;
129                         }
130
131                         private bool ParseOptChar (char c)
132                         {
133                                 if (!AtEnd() && _src[_cur] == c) {
134                                         _cur++;
135                                         return true;
136                                 }
137                                 else {
138                                         return false;
139                                 }
140                         }
141
142                         private void ParseChar (char c)
143                         {
144                                 bool b = ParseOptChar (c);
145                                 if (!b) {
146                                         ThrowFormatException ();
147                                 }
148                         }
149
150                         private Guid ParseGuid1 ()
151                         {
152                                 bool openBrace; 
153                                 bool groups = true;
154                                 char endChar = '}';
155                                 int a;
156                                 short b;
157                                 short c;
158                                 byte[] d = new byte[8];
159                                 int i;
160
161                                 openBrace = ParseOptChar ('{');
162                                 if (!openBrace) {
163                                         openBrace = ParseOptChar ('(');
164                                         if (openBrace) endChar = ')';
165                                 }
166                                 
167                                 a = (int) ParseHex(8, true);
168                                 
169                                 if (openBrace) ParseChar('-');
170                                 else groups = ParseOptChar('-');
171                                 
172                                 b = (short) ParseHex(4, true);
173                                 if (groups) ParseChar('-');
174                                 
175                                 c = (short) ParseHex(4, true);
176                                 if (groups) ParseChar('-');
177                                 
178                                 for (i=0; i<8; ++i) {
179                                         d[i] = (byte) ParseHex(2, true);
180                                         if (i == 1 && groups) {
181                                                 ParseChar('-');
182                                         }       
183                                 }
184         
185                                 if (openBrace && !ParseOptChar(endChar)) {
186                                         ThrowFormatException ();
187                                 }
188                 
189                                 return new Guid(a, b, c, d);
190                         }
191
192                         private void ParseHexPrefix ()
193                         {
194                                 ParseChar ('0');
195                                 ParseChar ('x');
196                         }
197
198                         private Guid ParseGuid2 ()
199                         {
200                                 int a;
201                                 short b;
202                                 short c;
203                                 byte[] d = new byte [8];
204                                 int i;
205
206                                 ParseChar ('{');
207                                 ParseHexPrefix ();
208                                 a = (int) ParseHex (8, false);
209                                 ParseChar (',');
210                                 ParseHexPrefix ();
211                                 b = (short) ParseHex (4, false);
212                                 ParseChar (',');
213                                 ParseHexPrefix ();
214                                 c = (short) ParseHex (4, false);
215                                 ParseChar (',');
216                                 ParseChar ('{');
217                                 for (i=0; i<8; ++i) {
218                                         ParseHexPrefix ();
219                                         d[i] = (byte) ParseHex (2, false);
220                                         if (i != 7) {
221                                                 ParseChar (',');
222                                         }
223                                 }
224                                 ParseChar ('}');
225                                 ParseChar ('}');
226
227                                 return new Guid (a,b,c,d);
228                                 
229                         }
230
231                         public Guid Parse ()
232                         {
233                                 Guid g;
234
235                                 try {
236                                         g  = ParseGuid1 ();
237                                 }
238                                 catch (FormatException) {
239                                         Reset ();
240                                         g = ParseGuid2 ();
241                                 }
242                                 if (!AtEnd () ) {
243                                         ThrowFormatException ();
244                                 }
245                                 return g;
246                         }
247                 }
248
249                 private static void CheckNull (object o)
250                 {
251                         if (o == null) {
252                                 throw new ArgumentNullException (Locale.GetText ("Value cannot be null."));
253                         }
254                 }
255
256                 private static void CheckLength (byte[] o, int l)
257                 {
258                         if (o . Length != l) {
259                                 throw new ArgumentException (String.Format (Locale.GetText ("Array should be exactly {0} bytes long."), l));
260                         }
261                 }
262
263                 private static void CheckArray (byte[] o, int l)
264                 {
265                         CheckNull (o);
266                         CheckLength (o, l);
267                 }
268
269                 public Guid (byte[] b)
270                 {
271                         CheckArray (b, 16);
272                         _a = Mono.Security.BitConverterLE.ToInt32 (b, 0);
273                         _b = Mono.Security.BitConverterLE.ToInt16 (b, 4);
274                         _c = Mono.Security.BitConverterLE.ToInt16 (b, 6);
275                         _d = b [8];
276                         _e = b [9];
277                         _f = b [10];
278                         _g = b [11];
279                         _h = b [12];
280                         _i = b [13];
281                         _j = b [14];
282                         _k = b [15];
283                 }
284
285                 public Guid (string g)
286                 {
287                         CheckNull (g);
288                         g = g.Trim();
289                         GuidParser p = new GuidParser (g);
290                         Guid guid = p.Parse();
291         
292                         this = guid;
293                 }
294
295                 public Guid (int a, short b, short c, byte[] d)
296                 {
297                         CheckArray (d, 8);
298                         _a = (int) a;
299                         _b = (short) b;
300                         _c = (short) c;
301                         _d = d [0];
302                         _e = d [1];
303                         _f = d [2];
304                         _g = d [3];
305                         _h = d [4];
306                         _i = d [5];
307                         _j = d [6];
308                         _k = d [7];
309                 }
310
311                 public Guid (int a, short b, short c, byte d, byte e, byte f, byte g, byte h, byte i, byte j, byte k)
312                 {
313                         _a = a;
314                         _b = b;
315                         _c = c;
316                         _d = d;
317                         _e = e;
318                         _f = f;
319                         _g = g;
320                         _h = h;
321                         _i = i;
322                         _j = j;
323                         _k = k;
324                 }
325
326                 [CLSCompliant (false)]
327                 public Guid (uint a, ushort b, ushort c, byte d, byte e, byte f, byte g, byte h, byte i, byte j, byte k)
328                         : this((int) a, (short) b, (short) c, d, e, f, g, h, i, j, k)
329                 {
330                 }
331
332                 public static readonly Guid Empty = new Guid (0,0,0,0,0,0,0,0,0,0,0);
333
334                 private static int Compare (int x, int y)
335                 {
336                         if (x < y) {
337                                 return -1;
338                         }
339                         else {
340                                 return 1;
341                         }
342                 }
343
344                 public int CompareTo (object value)
345                 {
346                         if (value == null)
347                                 return 1;
348
349                         if (!(value is Guid)) {
350                                 throw new ArgumentException ("value", Locale.GetText (
351                                         "Argument of System.Guid.CompareTo should be a Guid."));
352                         }
353
354                         return CompareTo ((Guid)value);
355                 }
356
357                 public override bool Equals (object o)
358                 {
359                         if (o is Guid)
360                                 return CompareTo ((Guid)o) == 0;
361                         return false;
362                 }
363
364                 public int CompareTo (Guid value)
365                 {
366                         if (_a != value._a) {
367                                 return Compare (_a, value._a);
368                         }
369                         else if (_b != value._b) {
370                                 return Compare (_b, value._b);
371                         }
372                         else if (_c != value._c) {
373                                 return Compare (_c, value._c);
374                         }
375                         else if (_d != value._d) {
376                                 return Compare (_d, value._d);
377                         }
378                         else if (_e != value._e) {
379                                 return Compare (_e, value._e);
380                         }
381                         else if (_f != value._f) {
382                                 return Compare (_f, value._f);
383                         }
384                         else if (_g != value._g) {
385                                 return Compare (_g, value._g);
386                         }
387                         else if (_h != value._h) {
388                                 return Compare (_h, value._h);
389                         }
390                         else if (_i != value._i) {
391                                 return Compare (_i, value._i);
392                         }
393                         else if (_j != value._j) {
394                                 return Compare (_j, value._j);
395                         }
396                         else if (_k != value._k) {
397                                 return Compare (_k, value._k);
398                         }
399                         return 0;
400                 }
401
402                 public bool Equals (Guid g)
403                 {
404                         return CompareTo (g) == 0;
405                 }
406
407                 public override int GetHashCode ()
408                 {
409                         int res;
410         
411                         res = (int) _a; 
412                         res = res ^ ((int) _b << 16 | _c);
413                         res = res ^ ((int) _d << 24);
414                         res = res ^ ((int) _e << 16);
415                         res = res ^ ((int) _f << 8);
416                         res = res ^ ((int) _g);
417                         res = res ^ ((int) _h << 24);
418                         res = res ^ ((int) _i << 16);
419                         res = res ^ ((int) _j << 8);
420                         res = res ^ ((int) _k);
421
422                         return res;
423                 }
424
425                 private static char ToHex (int b)
426                 {
427                         return (char)((b<0xA)?('0' + b):('a' + b - 0xA));
428                 }
429
430                 private static object _rngAccess = new object ();
431                 private static RandomNumberGenerator _rng;
432                 private static RandomNumberGenerator _fastRng;
433
434                 // generated as per section 3.4 of the specification
435                 public static Guid NewGuid ()
436                 {
437                         byte[] b = new byte [16];
438
439                         // thread-safe access to the prng
440                         lock (_rngAccess) {
441                                 if (_rng == null)
442                                         _rng = RandomNumberGenerator.Create ();
443                                 _rng.GetBytes (b);
444                         }
445
446                         Guid res = new Guid (b);
447                         // Mask in Variant 1-0 in Bit[7..6]
448                         res._d = (byte) ((res._d & 0x3fu) | 0x80u);
449                         // Mask in Version 4 (random based Guid) in Bits[15..13]
450                         res._c = (short) ((res._c & 0x0fffu) | 0x4000u);
451
452                         return res;
453                 }
454
455                 // used in ModuleBuilder so mcs doesn't need to invoke 
456                 // CryptoConfig for simple assemblies.
457                 internal static byte[] FastNewGuidArray ()
458                 {
459                         byte[] guid = new byte [16];
460
461                         // thread-safe access to the prng
462                         lock (_rngAccess) {
463                                 // if known, use preferred RNG
464                                 if (_rng != null)
465                                         _fastRng = _rng;
466                                 // else use hardcoded default RNG (bypassing CryptoConfig)
467                                 if (_fastRng == null)
468                                         _fastRng = new RNGCryptoServiceProvider ();
469                                 _fastRng.GetBytes (guid);
470                         }
471
472                         // Mask in Variant 1-0 in Bit[7..6]
473                         guid [8] = (byte) ((guid [8] & 0x3f) | 0x80);
474                         // Mask in Version 4 (random based Guid) in Bits[15..13]
475                         guid [7] = (byte) ((guid [7] & 0x0f) | 0x40);
476
477                         return guid;
478                 }
479
480                 public byte[] ToByteArray ()
481                 {
482                         byte[] res = new byte[16];
483                         byte[] tmp;
484                         int d = 0;
485                         int s;
486
487                         tmp = Mono.Security.BitConverterLE.GetBytes(_a);
488                         for (s=0; s<4; ++s) {
489                                 res[d++] = tmp[s];
490                         }
491
492                         tmp = Mono.Security.BitConverterLE.GetBytes(_b);
493                         for (s=0; s<2; ++s) {
494                                 res[d++] = tmp[s];
495                         }
496
497                         tmp = Mono.Security.BitConverterLE.GetBytes(_c);
498                         for (s=0; s<2; ++s) {
499                                 res[d++] = tmp[s];
500                         }
501
502                         res[8] = _d;
503                         res[9] = _e;
504                         res[10] = _f;
505                         res[11] = _g;
506                         res[12] = _h;
507                         res[13] = _i;
508                         res[14] = _j;
509                         res[15] = _k;
510
511                         return res;
512                 }
513
514                 static void AppendInt (StringBuilder builder, int value) {
515                         builder.Append (ToHex ((value >> 28) & 0xf));
516                         builder.Append (ToHex ((value >> 24) & 0xf));
517                         builder.Append (ToHex ((value >> 20) & 0xf));
518                         builder.Append (ToHex ((value >> 16) & 0xf));
519                         builder.Append (ToHex ((value >> 12) & 0xf));
520                         builder.Append (ToHex ((value >> 8) & 0xf));
521                         builder.Append (ToHex ((value >> 4) & 0xf));
522                         builder.Append (ToHex (value & 0xf));
523                 }
524
525                 static void AppendShort (StringBuilder builder, short value) {
526                         builder.Append (ToHex ((value >> 12) & 0xf));
527                         builder.Append (ToHex ((value >> 8) & 0xf));
528                         builder.Append (ToHex ((value >> 4) & 0xf));
529                         builder.Append (ToHex (value & 0xf));
530                 }
531
532                 static void AppendByte (StringBuilder builder, byte value) {
533                         builder.Append (ToHex ((value >> 4) & 0xf));
534                         builder.Append (ToHex (value & 0xf));
535                 }
536
537                 private string BaseToString (bool h, bool p, bool b)
538                 {
539                         StringBuilder res = new StringBuilder (40);
540                         
541                         if (p) {
542                                 res.Append ('(');
543                         } else if (b) {
544                                 res.Append ('{');
545                         }
546                 
547                         AppendInt (res, _a);
548                         if (h) {
549                                 res.Append ('-');
550                         }
551                         AppendShort (res, _b);
552                         if (h) {
553                                 res.Append ('-');
554                         }
555                         AppendShort (res, _c);
556                         if (h) {
557                                 res.Append ('-');
558                         }
559         
560                         AppendByte (res, _d);
561                         AppendByte (res, _e);
562
563                         if (h) {
564                                 res.Append ('-');
565                         }
566
567                         AppendByte (res, _f);
568                         AppendByte (res, _g);
569                         AppendByte (res, _h);
570                         AppendByte (res, _i);
571                         AppendByte (res, _j);
572                         AppendByte (res, _k);
573
574         
575                         if (p) {
576                                 res.Append (')');
577                         } else if (b) {
578                                 res.Append ('}');
579                         }
580                 
581                         return res.ToString ();
582                 }
583         
584                 public override string ToString ()
585                 {
586                         return BaseToString (true, false, false);
587                 }
588         
589                 public string ToString (string format)
590                 {
591                         bool h = true;
592                         bool p = false;
593                         bool b = false;
594         
595                         if (format != null) {
596                                 string f = format.ToLowerInvariant();
597         
598                                 if (f == "b") {
599                                         b = true;
600                                 }
601                                 else if (f == "p") {
602                                         p = true;
603                                 }
604                                 else if (f == "n") {
605                                         h = false;
606                                 }
607                                 else if (f != "d" && f != String.Empty) {
608                                         throw new FormatException (Locale.GetText (
609                                                 "Argument to Guid.ToString(string format) should be \"b\", \"B\", \"d\", \"D\", \"n\", \"N\", \"p\" or \"P\""));
610                                 }
611                         }
612
613                         return BaseToString (h, p, b);
614                 }
615
616                 public string ToString (string format, IFormatProvider provider)
617                 {
618                         return ToString (format);
619                 }
620
621                 public static bool operator == (Guid a, Guid b)
622                 {
623                         return a.Equals(b);
624                 }
625
626                 public static bool operator != (Guid a, Guid b)
627                 {
628                         return !( a.Equals (b) );
629                 }
630         }
631 }