Update PointConverter.cs
[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 //      Jb Evain (jbevain@novell.com)
8 //      Marek Safar (marek.safar@gmail.com)
9 //
10 // (C) 2002 Duco Fijma
11 // Copyright (C) 2004-2010 Novell, Inc (http://www.novell.com)
12 // Copyright 2012 Xamarin, Inc (http://www.xamarin.com)
13 //
14 // References
15 // 1.   UUIDs and GUIDs (DRAFT), Section 3.4
16 //      http://www.ics.uci.edu/~ejw/authoring/uuid-guid/draft-leach-uuids-guids-01.txt 
17 //
18 // Permission is hereby granted, free of charge, to any person obtaining
19 // a copy of this software and associated documentation files (the
20 // "Software"), to deal in the Software without restriction, including
21 // without limitation the rights to use, copy, modify, merge, publish,
22 // distribute, sublicense, and/or sell copies of the Software, and to
23 // permit persons to whom the Software is furnished to do so, subject to
24 // the following conditions:
25 // 
26 // The above copyright notice and this permission notice shall be
27 // included in all copies or substantial portions of the Software.
28 // 
29 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
30 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
31 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
32 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
33 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
34 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
35 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
36 //
37
38 using System.Runtime.InteropServices;
39 using System.Security.Cryptography;
40 using System.Text;
41 #if FULL_AOT_RUNTIME
42 using Crimson.CommonCrypto;
43 #endif
44
45 namespace System {
46
47         [Serializable]
48         [StructLayout (LayoutKind.Sequential)]
49         [ComVisible (true)]
50         public struct Guid : IFormattable, IComparable, IComparable<Guid>, IEquatable<Guid> {
51 #if FULL_AOT_RUNTIME
52                 static Guid () {
53                         if (MonoTouchAOTHelper.FalseFlag) {
54                                 var comparer = new System.Collections.Generic.GenericComparer <Guid> ();
55                                 var eqcomparer = new System.Collections.Generic.GenericEqualityComparer <Guid> ();
56                         }
57                 }
58 #endif
59                 private int _a; //_timeLow;
60                 private short _b; //_timeMid;
61                 private short _c; //_timeHighAndVersion;
62                 private byte _d; //_clockSeqHiAndReserved;
63                 private byte _e; //_clockSeqLow;
64                 private byte _f; //_node0;
65                 private byte _g; //_node1;
66                 private byte _h; //_node2;
67                 private byte _i; //_node3;
68                 private byte _j; //_node4;
69                 private byte _k; //_node5;
70
71                 enum Format {
72                         N, // 00000000000000000000000000000000
73                         D, // 00000000-0000-0000-0000-000000000000
74                         B, // {00000000-0000-0000-0000-000000000000}
75                         P, // (00000000-0000-0000-0000-000000000000)
76                         X, // {0x00000000,0x0000,0x0000,{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}}
77                 }
78
79                 class GuidParser {
80
81                         private string _src;
82                         private int _length;
83                         private int _cur;
84
85                         public GuidParser (string src)
86                         {
87                                 _src = src;
88                                 Reset ();
89                         }
90
91                         void Reset ()
92                         {
93                                 _cur = 0;
94                                 _length = _src.Length;
95                         }
96
97                         bool Eof {
98                                 get { return _cur >= _length; }
99                         }
100
101                         public static bool HasHyphen (Format format)
102                         {
103                                 switch (format) {
104                                 case Format.D:
105                                 case Format.B:
106                                 case Format.P:
107                                         return true;
108                                 default:
109                                         return false;
110                                 }
111                         }
112
113                         bool TryParseNDBP (Format format, out Guid guid)
114                         {
115                                 ulong a, b, c;
116                                 guid = new Guid ();
117
118                                 if (format == Format.B && !ParseChar ('{'))
119                                         return false;
120
121                                 if (format == Format.P && !ParseChar ('('))
122                                         return false;
123
124                                 if (!ParseHex (8, true, out a))
125                                         return false;
126
127                                 var has_hyphen = HasHyphen (format);
128
129                                 if (has_hyphen && !ParseChar ('-'))
130                                         return false;
131
132                                 if (!ParseHex (4, true, out b))
133                                         return false;
134
135                                 if (has_hyphen && !ParseChar ('-'))
136                                         return false;
137
138                                 if (!ParseHex (4, true, out c))
139                                         return false;
140
141                                 if (has_hyphen && !ParseChar ('-'))
142                                         return false;
143
144                                 var d = new byte [8];
145                                 for (int i = 0; i < d.Length; i++) {
146                                         ulong dd;
147                                         if (!ParseHex (2, true, out dd))
148                                                 return false;
149
150                                         if (i == 1 && has_hyphen && !ParseChar ('-'))
151                                                 return false;
152
153                                         d [i] = (byte) dd;
154                                 }
155
156                                 if (format == Format.B && !ParseChar ('}'))
157                                         return false;
158
159                                 if (format == Format.P && !ParseChar (')'))
160                                         return false;
161
162                                 if (!Eof)
163                                         return false;
164
165                                 guid = new Guid ((int) a, (short) b, (short) c, d);
166                                 return true;
167                         }
168
169                         bool TryParseX (out Guid guid)
170                         {
171                                 ulong a, b, c;
172                                 guid = new Guid ();
173
174                                 if (!(ParseChar ('{')
175                                         && ParseHexPrefix ()
176                                         && ParseHex (8, false, out a)
177                                         && ParseChar (',')
178                                         && ParseHexPrefix ()
179                                         && ParseHex (4, false, out b)
180                                         && ParseChar (',')
181                                         && ParseHexPrefix ()
182                                         && ParseHex (4, false, out c)
183                                         && ParseChar (',')
184                                         && ParseChar ('{'))) {
185
186                                         return false;
187                                 }
188
189                                 var d = new byte [8];
190                                 for (int i = 0; i < d.Length; ++i) {
191                                         ulong dd;
192
193                                         if (!(ParseHexPrefix () && ParseHex (2, false, out dd)))
194                                                 return false;
195
196                                         d [i] = (byte) dd;
197
198                                         if (i != 7 && !ParseChar (','))
199                                                 return false;
200                                 }
201
202                                 if (!(ParseChar ('}') && ParseChar ('}')))
203                                         return false;
204
205                                 if (!Eof)
206                                         return false;
207
208                                 guid = new Guid ((int) a, (short) b, (short) c, d);
209                                 return true;
210                         }
211
212                         bool ParseHexPrefix ()
213                         {
214                                 if (!ParseChar ('0'))
215                                         return false;
216
217                                 return ParseChar ('x');
218                         }
219
220                         bool ParseChar (char c)
221                         {
222                                 if (!Eof && _src [_cur] == c) {
223                                         _cur++;
224                                         return true;
225                                 }
226
227                                 return false;
228                         }
229
230                         bool ParseHex (int length, bool strict, out ulong res)
231                         {
232                                 res = 0;
233
234                                 for (int i = 0; i < length; i++) {
235                                         if (Eof)
236                                                 return !(strict && (i + 1 != length));
237
238                                         char c = Char.ToLowerInvariant (_src[_cur]);
239                                         if (Char.IsDigit (c)) {
240                                                 res = res * 16 + c - '0';
241                                                 _cur++;
242                                         } else if (c >= 'a' && c <= 'f') {
243                                                 res = res * 16 + c - 'a' + 10;
244                                                 _cur++;
245                                         } else {
246                                                 if (!strict)
247                                                         return true;
248
249                                                 return !(strict && (i + 1 != length));
250                                         }
251                                 }
252
253                                 return true;
254                         }
255
256                         public bool Parse (Format format, out Guid guid)
257                         {
258                                 if (format == Format.X)
259                                         return TryParseX (out guid);
260
261                                 return TryParseNDBP (format, out guid);
262                         }
263
264                         public bool Parse (out Guid guid)
265                         {
266                                 if (TryParseNDBP (Format.N, out guid))
267                                         return true;
268
269                                 Reset ();
270                                 if (TryParseNDBP (Format.D, out guid))
271                                         return true;
272
273                                 Reset ();
274                                 if (TryParseNDBP (Format.B, out guid))
275                                         return true;
276
277                                 Reset ();
278                                 if (TryParseNDBP (Format.P, out guid))
279                                         return true;
280
281                                 Reset ();
282                                 return TryParseX (out guid);
283                         }
284                 }
285
286                 private static void CheckNull (object o)
287                 {
288                         if (o == null) {
289                                 throw new ArgumentNullException (Locale.GetText ("Value cannot be null."));
290                         }
291                 }
292
293                 private static void CheckLength (byte[] o, int l)
294                 {
295                         if (o . Length != l) {
296                                 throw new ArgumentException (String.Format (Locale.GetText ("Array should be exactly {0} bytes long."), l));
297                         }
298                 }
299
300                 private static void CheckArray (byte[] o, int l)
301                 {
302                         CheckNull (o);
303                         CheckLength (o, l);
304                 }
305
306                 public Guid (byte[] b)
307                 {
308                         CheckArray (b, 16);
309                         _a = Mono.Security.BitConverterLE.ToInt32 (b, 0);
310                         _b = Mono.Security.BitConverterLE.ToInt16 (b, 4);
311                         _c = Mono.Security.BitConverterLE.ToInt16 (b, 6);
312                         _d = b [8];
313                         _e = b [9];
314                         _f = b [10];
315                         _g = b [11];
316                         _h = b [12];
317                         _i = b [13];
318                         _j = b [14];
319                         _k = b [15];
320                 }
321
322                 public Guid (string g)
323                 {
324                         CheckNull (g);
325                         g = g.Trim();
326                         var parser = new GuidParser (g);
327                         Guid guid;
328                         if (!parser.Parse (out guid))
329                                 throw CreateFormatException (g);
330
331                         this = guid;
332                 }
333
334                 static Exception CreateFormatException (string s)
335                 {
336                         return new FormatException (string.Format ("Invalid Guid format: {0}", s));
337                 }
338
339                 public Guid (int a, short b, short c, byte[] d)
340                 {
341                         CheckArray (d, 8);
342                         _a = (int) a;
343                         _b = (short) b;
344                         _c = (short) c;
345                         _d = d [0];
346                         _e = d [1];
347                         _f = d [2];
348                         _g = d [3];
349                         _h = d [4];
350                         _i = d [5];
351                         _j = d [6];
352                         _k = d [7];
353                 }
354
355                 public Guid (int a, short b, short c, byte d, byte e, byte f, byte g, byte h, byte i, byte j, byte k)
356                 {
357                         _a = a;
358                         _b = b;
359                         _c = c;
360                         _d = d;
361                         _e = e;
362                         _f = f;
363                         _g = g;
364                         _h = h;
365                         _i = i;
366                         _j = j;
367                         _k = k;
368                 }
369
370                 [CLSCompliant (false)]
371                 public Guid (uint a, ushort b, ushort c, byte d, byte e, byte f, byte g, byte h, byte i, byte j, byte k)
372                         : this((int) a, (short) b, (short) c, d, e, f, g, h, i, j, k)
373                 {
374                 }
375
376                 public static readonly Guid Empty = new Guid (0,0,0,0,0,0,0,0,0,0,0);
377
378                 private static int Compare (int x, int y)
379                 {
380                         return ((uint)x < (uint)y) ? -1 : 1;
381                 }
382
383                 public int CompareTo (object value)
384                 {
385                         if (value == null)
386                                 return 1;
387
388                         if (!(value is Guid)) {
389                                 throw new ArgumentException ("value", Locale.GetText (
390                                         "Argument of System.Guid.CompareTo should be a Guid."));
391                         }
392
393                         return CompareTo ((Guid)value);
394                 }
395
396                 public override bool Equals (object o)
397                 {
398                         if (o is Guid)
399                                 return CompareTo ((Guid)o) == 0;
400                         return false;
401                 }
402
403                 public int CompareTo (Guid value)
404                 {
405                         if (_a != value._a) {
406                                 return Compare (_a, value._a);
407                         }
408                         if (_b != value._b) {
409                                 return Compare (_b, value._b);
410                         }
411                         if (_c != value._c) {
412                                 return Compare (_c, value._c);
413                         }
414                         if (_d != value._d) {
415                                 return Compare (_d, value._d);
416                         }
417                         if (_e != value._e) {
418                                 return Compare (_e, value._e);
419                         }
420                         if (_f != value._f) {
421                                 return Compare (_f, value._f);
422                         }
423                         if (_g != value._g) {
424                                 return Compare (_g, value._g);
425                         }
426                         if (_h != value._h) {
427                                 return Compare (_h, value._h);
428                         }
429                         if (_i != value._i) {
430                                 return Compare (_i, value._i);
431                         }
432                         if (_j != value._j) {
433                                 return Compare (_j, value._j);
434                         }
435                         if (_k != value._k) {
436                                 return Compare (_k, value._k);
437                         }
438                         return 0;
439                 }
440
441                 public bool Equals (Guid g)
442                 {
443                         return CompareTo (g) == 0;
444                 }
445
446                 public override int GetHashCode ()
447                 {
448                         int res;
449         
450                         res = (int) _a; 
451                         res = res ^ ((int) _b << 16 | _c);
452                         res = res ^ ((int) _d << 24);
453                         res = res ^ ((int) _e << 16);
454                         res = res ^ ((int) _f << 8);
455                         res = res ^ ((int) _g);
456                         res = res ^ ((int) _h << 24);
457                         res = res ^ ((int) _i << 16);
458                         res = res ^ ((int) _j << 8);
459                         res = res ^ ((int) _k);
460
461                         return res;
462                 }
463
464                 private static char ToHex (int b)
465                 {
466                         return (char)((b<0xA)?('0' + b):('a' + b - 0xA));
467                 }
468
469 #if !FULL_AOT_RUNTIME
470                 private static object _rngAccess = new object ();
471                 private static RandomNumberGenerator _rng;
472                 private static RandomNumberGenerator _fastRng;
473 #endif
474
475                 // generated as per section 3.4 of the specification
476                 public static Guid NewGuid ()
477                 {
478                         byte[] b = new byte [16];
479 #if !FULL_AOT_RUNTIME
480                         // thread-safe access to the prng
481                         lock (_rngAccess) {
482                                 if (_rng == null)
483                                         _rng = RandomNumberGenerator.Create ();
484                                 _rng.GetBytes (b);
485                         }
486 #else
487                         Cryptor.GetRandom (b);
488 #endif
489
490                         Guid res = new Guid (b);
491                         // Mask in Variant 1-0 in Bit[7..6]
492                         res._d = (byte) ((res._d & 0x3fu) | 0x80u);
493                         // Mask in Version 4 (random based Guid) in Bits[15..13]
494                         res._c = (short) ((res._c & 0x0fffu) | 0x4000u);
495
496                         return res;
497                 }
498
499 #if !FULL_AOT_RUNTIME
500                 // used in ModuleBuilder so mcs doesn't need to invoke 
501                 // CryptoConfig for simple assemblies.
502                 internal static byte[] FastNewGuidArray ()
503                 {
504                         byte[] guid = new byte [16];
505
506                         // thread-safe access to the prng
507                         lock (_rngAccess) {
508                                 // if known, use preferred RNG
509                                 if (_rng != null)
510                                         _fastRng = _rng;
511                                 // else use hardcoded default RNG (bypassing CryptoConfig)
512                                 if (_fastRng == null)
513                                         _fastRng = new RNGCryptoServiceProvider ();
514                                 _fastRng.GetBytes (guid);
515                         }
516
517                         // Mask in Variant 1-0 in Bit[7..6]
518                         guid [8] = (byte) ((guid [8] & 0x3f) | 0x80);
519                         // Mask in Version 4 (random based Guid) in Bits[15..13]
520                         guid [7] = (byte) ((guid [7] & 0x0f) | 0x40);
521
522                         return guid;
523                 }
524 #endif
525                 public byte[] ToByteArray ()
526                 {
527                         byte[] res = new byte[16];
528                         byte[] tmp;
529                         int d = 0;
530                         int s;
531
532                         tmp = Mono.Security.BitConverterLE.GetBytes(_a);
533                         for (s=0; s<4; ++s) {
534                                 res[d++] = tmp[s];
535                         }
536
537                         tmp = Mono.Security.BitConverterLE.GetBytes(_b);
538                         for (s=0; s<2; ++s) {
539                                 res[d++] = tmp[s];
540                         }
541
542                         tmp = Mono.Security.BitConverterLE.GetBytes(_c);
543                         for (s=0; s<2; ++s) {
544                                 res[d++] = tmp[s];
545                         }
546
547                         res[8] = _d;
548                         res[9] = _e;
549                         res[10] = _f;
550                         res[11] = _g;
551                         res[12] = _h;
552                         res[13] = _i;
553                         res[14] = _j;
554                         res[15] = _k;
555
556                         return res;
557                 }
558
559                 static void AppendInt (StringBuilder builder, int value) {
560                         builder.Append (ToHex ((value >> 28) & 0xf));
561                         builder.Append (ToHex ((value >> 24) & 0xf));
562                         builder.Append (ToHex ((value >> 20) & 0xf));
563                         builder.Append (ToHex ((value >> 16) & 0xf));
564                         builder.Append (ToHex ((value >> 12) & 0xf));
565                         builder.Append (ToHex ((value >> 8) & 0xf));
566                         builder.Append (ToHex ((value >> 4) & 0xf));
567                         builder.Append (ToHex (value & 0xf));
568                 }
569
570                 static void AppendShort (StringBuilder builder, short value) {
571                         builder.Append (ToHex ((value >> 12) & 0xf));
572                         builder.Append (ToHex ((value >> 8) & 0xf));
573                         builder.Append (ToHex ((value >> 4) & 0xf));
574                         builder.Append (ToHex (value & 0xf));
575                 }
576
577                 static void AppendByte (StringBuilder builder, byte value) {
578                         builder.Append (ToHex ((value >> 4) & 0xf));
579                         builder.Append (ToHex (value & 0xf));
580                 }
581
582                 string ToString (Format format)
583                 {
584                         int length;
585                         switch (format) {
586                         case Format.B:
587                         case Format.P:
588                                 length = 38;
589                                 break;
590                         case Format.D:
591                                 length = 36;
592                                 break;
593                         case Format.N:
594                                 length = 32;
595                                 break;
596                         case Format.X:
597                                 length = 68;
598                                 break;          
599                         default:
600                                 throw new NotImplementedException (format.ToString ());
601                         }
602                         
603                         StringBuilder res = new StringBuilder (length);
604                         bool has_hyphen = GuidParser.HasHyphen (format);
605                         
606                         if (format == Format.P) {
607                                 res.Append ('(');
608                         } else if (format == Format.B) {
609                                 res.Append ('{');
610                         } else if (format == Format.X) {
611                                 res.Append ('{').Append ('0').Append ('x');
612                         }
613                 
614                         AppendInt (res, _a);
615                         if (has_hyphen) {
616                                 res.Append ('-');
617                         } else if (format == Format.X) {
618                                 res.Append (',').Append ('0').Append ('x');
619                         }
620                         
621                         AppendShort (res, _b);
622                         if (has_hyphen) {
623                                 res.Append ('-');
624                         } else if (format == Format.X) {
625                                 res.Append (',').Append ('0').Append ('x');
626                         }
627
628                         AppendShort (res, _c);
629                         if (has_hyphen) {
630                                 res.Append ('-');
631                         }
632         
633                         if (format == Format.X) {
634                                 res.Append (',').Append ('{').Append ('0').Append ('x');
635                                 AppendByte (res, _d);
636                                 res.Append (',').Append ('0').Append ('x');
637                                 AppendByte (res, _e);
638                                 res.Append (',').Append ('0').Append ('x');
639                                 AppendByte (res, _f);
640                                 res.Append (',').Append ('0').Append ('x');
641                                 AppendByte (res, _g);
642                                 res.Append (',').Append ('0').Append ('x');
643                                 AppendByte (res, _h);
644                                 res.Append (',').Append ('0').Append ('x');
645                                 AppendByte (res, _i);
646                                 res.Append (',').Append ('0').Append ('x');
647                                 AppendByte (res, _j);
648                                 res.Append (',').Append ('0').Append ('x');
649                                 AppendByte (res, _k);
650                                 res.Append ('}').Append ('}');;
651                         } else {
652                                 AppendByte (res, _d);
653                                 AppendByte (res, _e);
654         
655                                 if (has_hyphen) {
656                                         res.Append ('-');
657                                 }
658         
659                                 AppendByte (res, _f);
660                                 AppendByte (res, _g);
661                                 AppendByte (res, _h);
662                                 AppendByte (res, _i);
663                                 AppendByte (res, _j);
664                                 AppendByte (res, _k);
665         
666                                 if (format == Format.P) {
667                                         res.Append (')');
668                                 } else if (format == Format.B) {
669                                         res.Append ('}');
670                                 }
671                         }
672                 
673                         return res.ToString ();
674                 }
675         
676                 public override string ToString ()
677                 {
678                         return ToString (Format.D);
679                 }
680         
681                 public string ToString (string format)
682                 {
683                         return ToString (ParseFormat (format));
684                 }
685
686                 // provider value is never used
687                 public string ToString (string format, IFormatProvider provider)
688                 {
689                         return ToString (format);
690                 }
691
692                 public static bool operator == (Guid a, Guid b)
693                 {
694                         return a.Equals(b);
695                 }
696
697                 public static bool operator != (Guid a, Guid b)
698                 {
699                         return !( a.Equals (b) );
700                 }
701
702 #if NET_4_0
703                 public static Guid Parse (string input)
704                 {
705                         if (input == null)
706                                 throw new ArgumentNullException ("input");
707
708                         Guid guid;
709                         if (!TryParse (input, out guid))
710                                 throw CreateFormatException (input);
711
712                         return guid;
713                 }
714
715                 public static Guid ParseExact (string input, string format)
716                 {
717                         if (input == null)
718                                 throw new ArgumentNullException ("input");
719                         if (format == null)
720                                 throw new ArgumentNullException ("format");
721
722                         Guid guid;
723                         if (!TryParseExact (input, format, out guid))
724                                 throw CreateFormatException (input);
725
726                         return guid;
727                 }
728
729                 public static bool TryParse (string input, out Guid result)
730                 {
731                         if (input == null) {
732                                 result = Empty;
733                                 return false;
734                         }
735
736                         var parser = new GuidParser (input);
737                         return parser.Parse (out result);
738                 }
739
740                 public static bool TryParseExact (string input, string format, out Guid result)
741                 {
742                         if (input == null || format == null) {
743                                 result = Empty;
744                                 return false;
745                         }
746
747                         var parser = new GuidParser (input);
748                         return parser.Parse (ParseFormat (format), out result);
749                 }
750 #endif
751
752                 static Format ParseFormat (string format)
753                 {
754                         if (string.IsNullOrEmpty (format))
755                                 return Format.D;
756                         
757                         switch (format [0]) {
758                         case 'N':
759                         case 'n':
760                                 return Format.N;
761                         case 'D':
762                         case 'd':
763                                 return Format.D;
764                         case 'B':
765                         case 'b':
766                                 return Format.B;
767                         case 'P':
768                         case 'p':
769                                 return Format.P;
770 #if NET_4_0
771                         case 'X':
772                         case 'x':
773                                 return Format.X;
774 #endif
775                         }
776
777                         throw new FormatException (
778 #if NET_4_0
779                                 "Format String can be only one of \"D\", \"d\", \"N\", \"n\", \"P\", \"p\", \"B\", \"b\", \"X\" or \"x\""
780 #else
781                                 "Format String can be only one of \"D\", \"d\", \"N\", \"n\", \"P\", \"p\", \"B\" or \"b\""
782 #endif
783                                 );
784                 }
785         }
786 }