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)
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)
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
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:
26 // The above copyright notice and this permission notice shall be
27 // included in all copies or substantial portions of the Software.
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.
38 using System.Runtime.InteropServices;
39 using System.Security.Cryptography;
42 using Crimson.CommonCrypto;
48 [StructLayout (LayoutKind.Sequential)]
50 public struct Guid : IFormattable, IComparable, IComparable<Guid>, IEquatable<Guid> {
53 if (MonoTouchAOTHelper.FalseFlag) {
54 var comparer = new System.Collections.Generic.GenericComparer <Guid> ();
55 var eqcomparer = new System.Collections.Generic.GenericEqualityComparer <Guid> ();
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;
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}}
85 public GuidParser (string src)
94 _length = _src.Length;
98 get { return _cur >= _length; }
101 public static bool HasHyphen (Format format)
113 bool TryParseNDBP (Format format, out Guid guid)
118 if (format == Format.B && !ParseChar ('{'))
121 if (format == Format.P && !ParseChar ('('))
124 if (!ParseHex (8, true, out a))
127 var has_hyphen = HasHyphen (format);
129 if (has_hyphen && !ParseChar ('-'))
132 if (!ParseHex (4, true, out b))
135 if (has_hyphen && !ParseChar ('-'))
138 if (!ParseHex (4, true, out c))
141 if (has_hyphen && !ParseChar ('-'))
144 var d = new byte [8];
145 for (int i = 0; i < d.Length; i++) {
147 if (!ParseHex (2, true, out dd))
150 if (i == 1 && has_hyphen && !ParseChar ('-'))
156 if (format == Format.B && !ParseChar ('}'))
159 if (format == Format.P && !ParseChar (')'))
165 guid = new Guid ((int) a, (short) b, (short) c, d);
169 bool TryParseX (out Guid guid)
174 if (!(ParseChar ('{')
176 && ParseHex (8, false, out a)
179 && ParseHex (4, false, out b)
182 && ParseHex (4, false, out c)
184 && ParseChar ('{'))) {
189 var d = new byte [8];
190 for (int i = 0; i < d.Length; ++i) {
193 if (!(ParseHexPrefix () && ParseHex (2, false, out dd)))
198 if (i != 7 && !ParseChar (','))
202 if (!(ParseChar ('}') && ParseChar ('}')))
208 guid = new Guid ((int) a, (short) b, (short) c, d);
212 bool ParseHexPrefix ()
214 if (!ParseChar ('0'))
217 return ParseChar ('x');
220 bool ParseChar (char c)
222 if (!Eof && _src [_cur] == c) {
230 bool ParseHex (int length, bool strict, out ulong res)
234 for (int i = 0; i < length; i++) {
236 return !(strict && (i + 1 != length));
238 char c = Char.ToLowerInvariant (_src[_cur]);
239 if (Char.IsDigit (c)) {
240 res = res * 16 + c - '0';
242 } else if (c >= 'a' && c <= 'f') {
243 res = res * 16 + c - 'a' + 10;
249 return !(strict && (i + 1 != length));
256 public bool Parse (Format format, out Guid guid)
258 if (format == Format.X)
259 return TryParseX (out guid);
261 return TryParseNDBP (format, out guid);
264 public bool Parse (out Guid guid)
266 if (TryParseNDBP (Format.N, out guid))
270 if (TryParseNDBP (Format.D, out guid))
274 if (TryParseNDBP (Format.B, out guid))
278 if (TryParseNDBP (Format.P, out guid))
282 return TryParseX (out guid);
286 private static void CheckNull (object o)
289 throw new ArgumentNullException (Locale.GetText ("Value cannot be null."));
293 private static void CheckLength (byte[] o, int l)
295 if (o . Length != l) {
296 throw new ArgumentException (String.Format (Locale.GetText ("Array should be exactly {0} bytes long."), l));
300 private static void CheckArray (byte[] o, int l)
306 public Guid (byte[] b)
309 _a = Mono.Security.BitConverterLE.ToInt32 (b, 0);
310 _b = Mono.Security.BitConverterLE.ToInt16 (b, 4);
311 _c = Mono.Security.BitConverterLE.ToInt16 (b, 6);
322 public Guid (string g)
326 var parser = new GuidParser (g);
328 if (!parser.Parse (out guid))
329 throw CreateFormatException (g);
334 static Exception CreateFormatException (string s)
336 return new FormatException (string.Format ("Invalid Guid format: {0}", s));
339 public Guid (int a, short b, short c, byte[] d)
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)
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)
376 public static readonly Guid Empty = new Guid (0,0,0,0,0,0,0,0,0,0,0);
378 private static int Compare (int x, int y)
380 return ((uint)x < (uint)y) ? -1 : 1;
383 public int CompareTo (object value)
388 if (!(value is Guid)) {
389 throw new ArgumentException ("value", Locale.GetText (
390 "Argument of System.Guid.CompareTo should be a Guid."));
393 return CompareTo ((Guid)value);
396 public override bool Equals (object o)
399 return CompareTo ((Guid)o) == 0;
403 public int CompareTo (Guid value)
405 if (_a != value._a) {
406 return Compare (_a, value._a);
408 if (_b != value._b) {
409 return Compare (_b, value._b);
411 if (_c != value._c) {
412 return Compare (_c, value._c);
414 if (_d != value._d) {
415 return Compare (_d, value._d);
417 if (_e != value._e) {
418 return Compare (_e, value._e);
420 if (_f != value._f) {
421 return Compare (_f, value._f);
423 if (_g != value._g) {
424 return Compare (_g, value._g);
426 if (_h != value._h) {
427 return Compare (_h, value._h);
429 if (_i != value._i) {
430 return Compare (_i, value._i);
432 if (_j != value._j) {
433 return Compare (_j, value._j);
435 if (_k != value._k) {
436 return Compare (_k, value._k);
441 public bool Equals (Guid g)
443 return CompareTo (g) == 0;
446 public override int GetHashCode ()
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);
464 private static char ToHex (int b)
466 return (char)((b<0xA)?('0' + b):('a' + b - 0xA));
469 #if !FULL_AOT_RUNTIME
470 private static object _rngAccess = new object ();
471 private static RandomNumberGenerator _rng;
472 private static RandomNumberGenerator _fastRng;
475 // generated as per section 3.4 of the specification
476 public static Guid NewGuid ()
478 byte[] b = new byte [16];
479 #if !FULL_AOT_RUNTIME
480 // thread-safe access to the prng
483 _rng = RandomNumberGenerator.Create ();
487 Cryptor.GetRandom (b);
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);
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 ()
504 byte[] guid = new byte [16];
506 // thread-safe access to the prng
508 // if known, use preferred RNG
511 // else use hardcoded default RNG (bypassing CryptoConfig)
512 if (_fastRng == null)
513 _fastRng = new RNGCryptoServiceProvider ();
514 _fastRng.GetBytes (guid);
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);
525 public byte[] ToByteArray ()
527 byte[] res = new byte[16];
532 tmp = Mono.Security.BitConverterLE.GetBytes(_a);
533 for (s=0; s<4; ++s) {
537 tmp = Mono.Security.BitConverterLE.GetBytes(_b);
538 for (s=0; s<2; ++s) {
542 tmp = Mono.Security.BitConverterLE.GetBytes(_c);
543 for (s=0; s<2; ++s) {
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));
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));
577 static void AppendByte (StringBuilder builder, byte value) {
578 builder.Append (ToHex ((value >> 4) & 0xf));
579 builder.Append (ToHex (value & 0xf));
582 string ToString (Format format)
600 throw new NotImplementedException (format.ToString ());
603 StringBuilder res = new StringBuilder (length);
604 bool has_hyphen = GuidParser.HasHyphen (format);
606 if (format == Format.P) {
608 } else if (format == Format.B) {
610 } else if (format == Format.X) {
611 res.Append ('{').Append ('0').Append ('x');
617 } else if (format == Format.X) {
618 res.Append (',').Append ('0').Append ('x');
621 AppendShort (res, _b);
624 } else if (format == Format.X) {
625 res.Append (',').Append ('0').Append ('x');
628 AppendShort (res, _c);
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 ('}');;
652 AppendByte (res, _d);
653 AppendByte (res, _e);
659 AppendByte (res, _f);
660 AppendByte (res, _g);
661 AppendByte (res, _h);
662 AppendByte (res, _i);
663 AppendByte (res, _j);
664 AppendByte (res, _k);
666 if (format == Format.P) {
668 } else if (format == Format.B) {
673 return res.ToString ();
676 public override string ToString ()
678 return ToString (Format.D);
681 public string ToString (string format)
683 return ToString (ParseFormat (format));
686 // provider value is never used
687 public string ToString (string format, IFormatProvider provider)
689 return ToString (format);
692 public static bool operator == (Guid a, Guid b)
697 public static bool operator != (Guid a, Guid b)
699 return !( a.Equals (b) );
703 public static Guid Parse (string input)
706 throw new ArgumentNullException ("input");
709 if (!TryParse (input, out guid))
710 throw CreateFormatException (input);
715 public static Guid ParseExact (string input, string format)
718 throw new ArgumentNullException ("input");
720 throw new ArgumentNullException ("format");
723 if (!TryParseExact (input, format, out guid))
724 throw CreateFormatException (input);
729 public static bool TryParse (string input, out Guid result)
736 var parser = new GuidParser (input);
737 return parser.Parse (out result);
740 public static bool TryParseExact (string input, string format, out Guid result)
742 if (input == null || format == null) {
747 var parser = new GuidParser (input);
748 return parser.Parse (ParseFormat (format), out result);
752 static Format ParseFormat (string format)
754 if (string.IsNullOrEmpty (format))
757 switch (format [0]) {
777 throw new FormatException (
779 "Format String can be only one of \"D\", \"d\", \"N\", \"n\", \"P\", \"p\", \"B\", \"b\", \"X\" or \"x\""
781 "Format String can be only one of \"D\", \"d\", \"N\", \"n\", \"P\", \"p\", \"B\" or \"b\""