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