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