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