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