Thu Jul 17 15:23:17 CEST 2003 Paolo Molaro <lupus@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 using System.Text;
13
14 namespace System {
15
16 [Serializable]
17 public struct Guid  : IFormattable, IComparable  {
18
19         private uint _timeLow;
20         private ushort _timeMid;
21         private ushort _timeHighAndVersion;
22         private byte _clockSeqHiAndReserved;
23         private byte _clockSeqLow;
24         private byte _node0;
25         private byte _node1;
26         private byte _node2;
27         private byte _node3;
28         private byte _node4;
29         private byte _node5;
30
31         internal class GuidState {
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                         
180                         return res;
181                 }
182         
183                 private bool ParseOptChar (char c)
184                 {
185                         if (!AtEnd() && _src[_cur] == c) {
186                                 _cur++;
187                                 return true;
188                         }
189                         else {
190                                 return false;
191                         }
192                 }
193         
194                 private void ParseChar (char c)
195                 {
196                         bool b = ParseOptChar (c);
197                         if (!b) {
198                                 ThrowFormatException ();
199                         }
200                 }
201         
202                 private Guid ParseGuid1 ()
203                 {
204                         bool openBrace; 
205                         int a;
206                         short b;
207                         short c;
208                         byte[] d = new byte[8];
209                         int i;
210         
211                         openBrace = ParseOptChar ('{');
212                         a = (int) ParseHex(8, true);
213                         ParseChar('-');
214                         b = (short) ParseHex(4, true);
215                         ParseChar('-');
216                         c = (short) ParseHex(4, true);
217                         ParseChar('-');
218                         for (i=0; i<8; ++i) {
219                                 d[i] = (byte) ParseHex(2, true);
220                                 if (i == 1) {
221                                         ParseChar('-');
222                                 }       
223                         }
224
225                         if (openBrace && !ParseOptChar('}')) {
226                                 ThrowFormatException ();
227                         }
228         
229                         return new Guid(a, b, c, d);
230                 }
231         
232                 private void ParseHexPrefix ()
233                 {
234                         ParseChar ('0');
235                         ParseChar ('x');
236                 }
237         
238                 private Guid ParseGuid2 ()
239                 {
240                         int a;
241                         short b;
242                         short c;
243                         byte[] d = new byte [8];
244                         int i;
245         
246                         ParseChar ('{');
247                         ParseHexPrefix ();
248                         a = (int) ParseHex (8, false);
249                         ParseChar (',');
250                         ParseHexPrefix ();
251                         b = (short) ParseHex (4, false);
252                         ParseChar (',');
253                         ParseHexPrefix ();
254                         c = (short) ParseHex (4, false);
255                         ParseChar (',');
256                         ParseChar ('{');
257                         for (i=0; i<8; ++i) {
258                                 ParseHexPrefix ();
259                                 d[i] = (byte) ParseHex (2, false);
260                                 if (i != 7) {
261                                         ParseChar (',');
262                                 }
263
264                         }       
265                         ParseChar ('}');
266                         ParseChar ('}');
267         
268                         return new Guid (a,b,c,d);                      
269                         
270                 }
271         
272                 public Guid Parse ()
273                 {
274                         Guid g;
275         
276                         try {
277                                 g  = ParseGuid1 ();
278                         }
279                         catch (FormatException) {
280                                 Reset ();
281                                 g = ParseGuid2 (); 
282                         }
283                         if (!AtEnd () ) {
284                                 ThrowFormatException ();
285                         }
286                         return g;
287                 }
288
289         }
290
291         private static GuidState _guidState = new GuidState ( true /* use pseudo RNG? */ ); 
292
293         private static void CheckNull (object o)
294         {
295                 if (o == null) {
296                         throw new ArgumentNullException (Locale.GetText ("Value cannot be null."));
297                 }
298         }
299
300         private static void CheckLength (byte[] o, int l)
301         {
302                 if (o . Length != l) {
303                         throw new ArgumentException (String.Format (Locale.GetText ("Array should be exactly {0} bytes long."), l));
304                 }
305         }
306
307         private static void CheckArray (byte[] o, int l)
308         {
309                 CheckNull (o);
310                 CheckLength (o, l);
311         }
312
313         public Guid (byte[] b)
314         {
315                 CheckArray (b, 16);
316                 _timeLow = System.BitConverter.ToUInt32 (b, 0);
317                 _timeMid = System.BitConverter.ToUInt16 (b, 4);
318                 _timeHighAndVersion = System.BitConverter.ToUInt16 (b, 6);
319                 _clockSeqHiAndReserved = b[8];
320                 _clockSeqLow = b[9];
321                 _node0 = b[10];
322                 _node1 = b[11];
323                 _node2 = b[12];
324                 _node3 = b[13];
325                 _node4 = b[14];
326                 _node5 = b[15];
327         }
328
329         public Guid (string g)
330         {
331                 CheckNull (g);
332
333                 GuidParser p = new GuidParser (g);
334                 Guid guid = p.Parse();
335
336                 this = guid;
337         }
338
339         public Guid (int a, short b, short c, byte[] d) 
340         {
341                 CheckArray(d, 8);
342                 _timeLow = (uint) a;
343                 _timeMid = (ushort) b;
344                 _timeHighAndVersion = (ushort) c;
345                 _clockSeqHiAndReserved = d[0];
346                 _clockSeqLow = d[1];
347                 _node0 = d[2];
348                 _node1 = d[3];
349                 _node2 = d[4];
350                 _node3 = d[5];
351                 _node4 = d[6];
352                 _node5 = d[7];
353         }
354
355         public Guid (
356                 int a,
357                 short b,
358                 short c,
359                 byte d,
360                 byte e,
361                 byte f,
362                 byte g,
363                 byte h,
364                 byte i,
365                 byte j,
366                 byte k)
367                 : this((uint) a, (ushort) b, (ushort) c, d, e, f, g, h, i, j, k) {}
368
369         [CLSCompliant(false)]
370         public Guid (
371                 uint a,
372                 ushort b,
373                 ushort c,
374                 byte d,
375                 byte e,
376                 byte f,
377                 byte g,
378                 byte h,
379                 byte i,
380                 byte j,
381                 byte k)
382         {
383                 _timeLow = a;
384                 _timeMid = b;
385                 _timeHighAndVersion = c;
386                 _clockSeqHiAndReserved = d;
387                 _clockSeqLow = e;
388                 _node0 = f;
389                 _node1 = g;
390                 _node2 = h;
391                 _node3 = i;
392                 _node4 = j;
393                 _node5 = k;
394         }
395
396         public static readonly Guid Empty = new Guid(0,0,0,0,0,0,0,0,0,0,0);
397
398         private static int Compare (uint x, uint y)
399         {
400                 if (x < y) {
401                         return -1;
402                 }
403                 else {
404                         return 1;
405                 }
406         }
407
408         public int CompareTo (object value )
409         {
410                 if (value == null )
411                         return 1;
412
413                 if (!(value is Guid)) {
414                         throw new ArgumentException (Locale.GetText (
415                                 "Argument of System.Guid.CompareTo should be a Guid"));
416                 }
417
418                 Guid v = (Guid) value;
419
420                 if (_timeLow != v._timeLow ) {
421                         return Compare(_timeLow, v._timeLow);
422                 }
423                 else if (_timeMid != v._timeMid) {
424                         return Compare(_timeMid, v._timeMid);
425                 }
426                 else if (_timeHighAndVersion != v._timeHighAndVersion) {
427                         return Compare(_timeHighAndVersion, v._timeHighAndVersion);
428                 }
429                 else if (_clockSeqHiAndReserved != v._clockSeqHiAndReserved) {
430                         return Compare(_clockSeqHiAndReserved, v._clockSeqHiAndReserved);
431                 }
432                 else if (_clockSeqLow != v._clockSeqLow) {
433                         return Compare(_clockSeqLow, v._clockSeqLow);
434                 }
435                 else if (_node0 != v._node0) {
436                         return Compare(_node0, v._node0);
437                 }
438                 else if (_node1 != v._node1) {
439                         return Compare(_node1, v._node1);
440                 }
441                 else if (_node2 != v._node2) {
442                         return Compare(_node2, v._node2);
443                 }
444                 else if (_node3 != v._node3) {
445                         return Compare(_node3, v._node3);
446                 }
447                 else if (_node4 != v._node4) {
448                         return Compare(_node4, v._node4);
449                 }
450                 else if (_node5 != v._node5) {
451                         return Compare(_node5, v._node5);
452                 }
453
454                 return 0;
455         }
456
457         public override bool Equals ( object o )
458         {
459                 try {
460                         return CompareTo (o) == 0;      
461                 }
462                 catch ( ArgumentException ) {
463                         return false;
464                 }
465         }
466
467         public override int GetHashCode ()
468         {
469                 int res;
470
471                 res = (int) _timeLow; 
472                 res = res ^ ((int) _timeMid << 16 | _timeHighAndVersion);
473                 res = res ^ ((int) _clockSeqHiAndReserved << 24);
474                 res = res ^ ((int) _clockSeqLow << 16);
475                 res = res ^ ((int) _node0 << 8);
476                 res = res ^ ((int) _node1);
477                 res = res ^ ((int) _node2 << 24);
478                 res = res ^ ((int) _node3 << 16);
479                 res = res ^ ((int) _node4 << 8);
480                 res = res ^ ((int) _node5);
481
482                 return res;
483         }
484
485         private static Guid NewTimeGuid()
486         {
487                 ulong timestamp = _guidState.NewTimestamp ();
488
489                 // Bit [31..0] (32 bits) for timeLow
490                 uint timeLow = (uint) (timestamp & 0x00000000fffffffful);
491                 // Bit [47..32] (16 bits) for timeMid
492                 ushort timeMid = (ushort) ((timestamp & 0x0000ffff00000000ul) >> 32); 
493                 // Bit [59..48] (12 bits) for timeHi
494                 ushort timeHi = (ushort) ((timestamp & 0x0fff000000000000ul) >> 48);
495                 // Bit [7..0] (8 bits) for clockSeqLo
496                 byte clockSeqLow = (byte) (Guid._guidState.ClockSeq & 0x00ffu);
497                 // Bit [13..8] (6 bits) for clockSeqHi
498                 byte clockSeqHi = (byte) ((Guid._guidState.ClockSeq & 0x3f00u) >> 8);
499                 byte[] mac = _guidState.MAC;
500
501                 clockSeqHi = (byte) (clockSeqHi | 0x80u); // Bit[7] = 1, Bit[6] = 0 (Variant)
502                 timeHi = (ushort) (timeHi | 0x1000u); // Bit[15..13] = 1 (Guid is time-based)
503
504                 return new Guid(timeLow, timeMid, timeHi, clockSeqHi, clockSeqLow, mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
505         }
506
507         private static Guid NewRandomGuid ()
508         {
509                 byte[] b = new byte[16];
510
511                 _guidState.NextBytes (b);
512
513                 Guid res = new Guid(b);
514                 // Mask in Variant 1-0 in Bit[7..6]
515                 res._clockSeqHiAndReserved = (byte) ((res._clockSeqHiAndReserved & 0x3fu) | 0x80u);
516                 // Mask in Version 4 (random based Guid) in Bits[15..13]
517                 res._timeHighAndVersion = (ushort) ((res._timeHighAndVersion & 0x0fffu) | 0x4000u);
518                 return res;
519         }
520
521         [MonoTODO]
522         public static Guid NewGuid ()
523         {
524                 return NewRandomGuid();
525         }
526
527         public byte[] ToByteArray ()
528         {
529                 byte[] res = new byte[16];
530                 byte[] tmp;
531                 int d = 0;
532                 int s;
533
534                 tmp = BitConverter.GetBytes(_timeLow);
535                 for (s=0; s<4; ++s) {
536                         res[d++] = tmp[s];
537                 }
538
539                 tmp = BitConverter.GetBytes(_timeMid);
540                 for (s=0; s<2; ++s) {
541                         res[d++] = tmp[s];
542                 }
543
544                 tmp = BitConverter.GetBytes(_timeHighAndVersion);
545                 for (s=0; s<2; ++s) {
546                         res[d++] = tmp[s];
547                 }
548
549                 res[8] = _clockSeqHiAndReserved;
550                 res[9] = _clockSeqLow;
551                 res[10] = _node0;
552                 res[11] = _node1;
553                 res[12] = _node2;
554                 res[13] = _node3;
555                 res[14] = _node4;
556                 res[15] = _node5;
557
558                 return res;
559         }
560
561         private string BaseToString(bool h, bool p, bool b)
562         {
563                 StringBuilder res = new StringBuilder (40);
564                 
565                 if (p) {
566                         res.Append ('(');
567                 } else if (b) {
568                         res.Append ('{');
569                 }
570         
571                 res.Append (_timeLow.ToString ("x8"));
572                 if (h) {
573                         res.Append ('-');
574                 }
575                 res.Append (_timeMid.ToString ("x4"));
576                 if (h) {
577                         res.Append ('-');
578                 }
579                 res.Append (_timeHighAndVersion.ToString ("x4"));
580                 if (h) {
581                         res.Append ('-');
582                 }
583                 res.Append ((char)('0' + ((_clockSeqHiAndReserved >> 4) & 0xf)));
584                 res.Append ((char)('0' + (_clockSeqHiAndReserved & 0xf)));
585                 res.Append ((char)('0' + ((_clockSeqLow >> 4) & 0xf)));
586                 res.Append ((char)('0' + (_clockSeqLow & 0xf)));
587                 if (h) {
588                         res.Append ('-');
589                 }
590                 res.Append ((char)('0' + ((_node0 >> 4) & 0xf)));
591                 res.Append ((char)('0' + (_node0 & 0xf)));
592                 res.Append ((char)('0' + ((_node1 >> 4) & 0xf)));
593                 res.Append ((char)('0' + (_node1 & 0xf)));
594                 res.Append ((char)('0' + ((_node2 >> 4) & 0xf)));
595                 res.Append ((char)('0' + (_node2 & 0xf)));
596                 res.Append ((char)('0' + ((_node3 >> 4) & 0xf)));
597                 res.Append ((char)('0' + (_node3 & 0xf)));
598                 res.Append ((char)('0' + ((_node4 >> 4) & 0xf)));
599                 res.Append ((char)('0' + (_node4 & 0xf)));
600                 res.Append ((char)('0' + ((_node5 >> 4) & 0xf)));
601                 res.Append ((char)('0' + (_node5 & 0xf)));
602
603                 if (p) {
604                         res.Append (')');
605                 } else if (b) {
606                         res.Append ('}');
607                 }
608         
609                 return res.ToString ();
610         }
611
612         public override string ToString ()
613         {
614                 return BaseToString (true, false, false);
615         }
616
617         public string ToString (string format)
618         {
619                 string f;
620                 bool h = true;
621                 bool p = false;
622                 bool b = false;
623
624                 if (format != null) {
625                         f = format.ToLower();
626
627                         if (f == "b") {
628                                 b = true;
629                         }
630                         else if (f == "p") {
631                                 p = true;
632                         }
633                         else if (f == "n") {
634                                 h = false;
635                         }
636                         else if (f != "d" && f != "") {
637                                 throw new FormatException ( Locale.GetText ("Argument to Guid.ToString(string format) should be \"b\", \"B\", \"d\", \"D\", \"n\", \"N\", \"p\" or \"P\""));
638                         }
639                 }
640
641                 return BaseToString (h, p, b);
642         }
643
644         public string ToString (string format, IFormatProvider provider)
645         {
646                 return ToString (format);
647         }
648
649         public static bool operator == (Guid a, Guid b)
650         {
651                 return a.Equals(b);
652         }
653
654         public static bool operator != (Guid a, Guid b)
655         {
656                 return !( a.Equals (b) );
657         }
658
659 }
660
661 }