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