5 // Duco Fijma (duco@lorentz.xs4all.nl)
6 // Sebastien Pouliot (sebastien@ximian.com)
9 // Copyright (C) 2004-2005 Novell, Inc (http://www.novell.com)
12 // 1. UUIDs and GUIDs (DRAFT), Section 3.4
13 // http://www.ics.uci.edu/~ejw/authoring/uuid-guid/draft-leach-uuids-guids-01.txt
15 // Permission is hereby granted, free of charge, to any person obtaining
16 // a copy of this software and associated documentation files (the
17 // "Software"), to deal in the Software without restriction, including
18 // without limitation the rights to use, copy, modify, merge, publish,
19 // distribute, sublicense, and/or sell copies of the Software, and to
20 // permit persons to whom the Software is furnished to do so, subject to
21 // the following conditions:
23 // The above copyright notice and this permission notice shall be
24 // included in all copies or substantial portions of the Software.
26 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
27 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
28 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
29 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
30 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
31 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
32 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
35 using System.Runtime.InteropServices;
36 using System.Security.Cryptography;
42 [StructLayout (LayoutKind.Sequential)]
44 public struct Guid : IFormattable, IComparable, IComparable<Guid>, IEquatable<Guid> {
47 if (MonoTouchAOTHelper.FalseFlag) {
48 var comparer = new System.Collections.Generic.GenericComparer <Guid> ();
49 var eqcomparer = new System.Collections.Generic.GenericEqualityComparer <Guid> ();
53 private int _a; //_timeLow;
54 private short _b; //_timeMid;
55 private short _c; //_timeHighAndVersion;
56 private byte _d; //_clockSeqHiAndReserved;
57 private byte _e; //_clockSeqLow;
58 private byte _f; //_node0;
59 private byte _g; //_node1;
60 private byte _h; //_node2;
61 private byte _i; //_node3;
62 private byte _j; //_node4;
63 private byte _k; //_node5;
66 N, // 00000000000000000000000000000000
67 D, // 00000000-0000-0000-0000-000000000000
68 B, // {00000000-0000-0000-0000-000000000000}
69 P, // (00000000-0000-0000-0000-000000000000)
70 X, // {0x00000000,0x0000,0x0000,{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}}
79 public GuidParser (string src)
88 _length = _src.Length;
92 get { return _cur >= _length; }
95 static bool HasHyphen (Format format)
107 bool TryParseNDBP (Format format, out Guid guid)
112 if (format == Format.B && !ParseChar ('{'))
115 if (format == Format.P && !ParseChar ('('))
118 if (!ParseHex (8, true, out a))
121 var has_hyphen = HasHyphen (format);
123 if (has_hyphen && !ParseChar ('-'))
126 if (!ParseHex (4, true, out b))
129 if (has_hyphen && !ParseChar ('-'))
132 if (!ParseHex (4, true, out c))
135 if (has_hyphen && !ParseChar ('-'))
138 var d = new byte [8];
139 for (int i = 0; i < d.Length; i++) {
141 if (!ParseHex (2, true, out dd))
144 if (i == 1 && has_hyphen && !ParseChar ('-'))
150 if (format == Format.B && !ParseChar ('}'))
153 if (format == Format.P && !ParseChar (')'))
156 guid = new Guid ((int) a, (short) b, (short) c, d);
160 bool TryParseX (out Guid guid)
165 if (!(ParseChar ('{')
167 && ParseHex (8, false, out a)
170 && ParseHex (4, false, out b)
173 && ParseHex (4, false, out c)
175 && ParseChar ('{'))) {
180 var d = new byte [8];
181 for (int i = 0; i < d.Length; ++i) {
184 if (!(ParseHexPrefix () && ParseHex (2, false, out dd)))
189 if (i != 7 && !ParseChar (','))
193 if (!(ParseChar ('}') && ParseChar ('}')))
196 guid = new Guid ((int) a, (short) b, (short) c, d);
200 bool ParseHexPrefix ()
202 if (!ParseChar ('0'))
205 return ParseChar ('x');
208 bool ParseChar (char c)
210 if (!Eof && _src [_cur] == c) {
218 bool ParseHex (int length, bool strict, out ulong res)
222 for (int i = 0; i < length; i++) {
224 return !(strict && (i + 1 != length));
226 char c = Char.ToLowerInvariant (_src[_cur]);
227 if (Char.IsDigit (c)) {
228 res = res * 16 + c - '0';
230 } else if (c >= 'a' && c <= 'f') {
231 res = res * 16 + c - 'a' + 10;
237 return !(strict && (i + 1 != length));
244 public bool Parse (Format format, out Guid guid)
246 if (format == Format.X)
247 return TryParseX (out guid);
249 return TryParseNDBP (format, out guid);
252 public bool Parse (out Guid guid)
254 if (TryParseNDBP (Format.N, out guid))
258 if (TryParseNDBP (Format.D, out guid))
262 if (TryParseNDBP (Format.B, out guid))
266 if (TryParseNDBP (Format.P, out guid))
270 return TryParseX (out guid);
274 private static void CheckNull (object o)
277 throw new ArgumentNullException (Locale.GetText ("Value cannot be null."));
281 private static void CheckLength (byte[] o, int l)
283 if (o . Length != l) {
284 throw new ArgumentException (String.Format (Locale.GetText ("Array should be exactly {0} bytes long."), l));
288 private static void CheckArray (byte[] o, int l)
294 public Guid (byte[] b)
297 _a = Mono.Security.BitConverterLE.ToInt32 (b, 0);
298 _b = Mono.Security.BitConverterLE.ToInt16 (b, 4);
299 _c = Mono.Security.BitConverterLE.ToInt16 (b, 6);
310 public Guid (string g)
314 var parser = new GuidParser (g);
316 if (!parser.Parse (out guid))
317 throw CreateFormatException (g);
322 static Exception CreateFormatException (string s)
324 return new FormatException (string.Format ("Invalid Guid format: {0}", s));
327 public Guid (int a, short b, short c, byte[] d)
343 public Guid (int a, short b, short c, byte d, byte e, byte f, byte g, byte h, byte i, byte j, byte k)
358 [CLSCompliant (false)]
359 public Guid (uint a, ushort b, ushort c, byte d, byte e, byte f, byte g, byte h, byte i, byte j, byte k)
360 : this((int) a, (short) b, (short) c, d, e, f, g, h, i, j, k)
364 public static readonly Guid Empty = new Guid (0,0,0,0,0,0,0,0,0,0,0);
366 private static int Compare (int x, int y)
376 public int CompareTo (object value)
381 if (!(value is Guid)) {
382 throw new ArgumentException ("value", Locale.GetText (
383 "Argument of System.Guid.CompareTo should be a Guid."));
386 return CompareTo ((Guid)value);
389 public override bool Equals (object o)
392 return CompareTo ((Guid)o) == 0;
396 public int CompareTo (Guid value)
398 if (_a != value._a) {
399 return Compare (_a, value._a);
401 else if (_b != value._b) {
402 return Compare (_b, value._b);
404 else if (_c != value._c) {
405 return Compare (_c, value._c);
407 else if (_d != value._d) {
408 return Compare (_d, value._d);
410 else if (_e != value._e) {
411 return Compare (_e, value._e);
413 else if (_f != value._f) {
414 return Compare (_f, value._f);
416 else if (_g != value._g) {
417 return Compare (_g, value._g);
419 else if (_h != value._h) {
420 return Compare (_h, value._h);
422 else if (_i != value._i) {
423 return Compare (_i, value._i);
425 else if (_j != value._j) {
426 return Compare (_j, value._j);
428 else if (_k != value._k) {
429 return Compare (_k, value._k);
434 public bool Equals (Guid g)
436 return CompareTo (g) == 0;
439 public override int GetHashCode ()
444 res = res ^ ((int) _b << 16 | _c);
445 res = res ^ ((int) _d << 24);
446 res = res ^ ((int) _e << 16);
447 res = res ^ ((int) _f << 8);
448 res = res ^ ((int) _g);
449 res = res ^ ((int) _h << 24);
450 res = res ^ ((int) _i << 16);
451 res = res ^ ((int) _j << 8);
452 res = res ^ ((int) _k);
457 private static char ToHex (int b)
459 return (char)((b<0xA)?('0' + b):('a' + b - 0xA));
462 private static object _rngAccess = new object ();
463 private static RandomNumberGenerator _rng;
464 private static RandomNumberGenerator _fastRng;
466 // generated as per section 3.4 of the specification
467 public static Guid NewGuid ()
469 byte[] b = new byte [16];
471 // thread-safe access to the prng
474 _rng = RandomNumberGenerator.Create ();
478 Guid res = new Guid (b);
479 // Mask in Variant 1-0 in Bit[7..6]
480 res._d = (byte) ((res._d & 0x3fu) | 0x80u);
481 // Mask in Version 4 (random based Guid) in Bits[15..13]
482 res._c = (short) ((res._c & 0x0fffu) | 0x4000u);
487 // used in ModuleBuilder so mcs doesn't need to invoke
488 // CryptoConfig for simple assemblies.
489 internal static byte[] FastNewGuidArray ()
491 byte[] guid = new byte [16];
493 // thread-safe access to the prng
495 // if known, use preferred RNG
498 // else use hardcoded default RNG (bypassing CryptoConfig)
499 if (_fastRng == null)
500 _fastRng = new RNGCryptoServiceProvider ();
501 _fastRng.GetBytes (guid);
504 // Mask in Variant 1-0 in Bit[7..6]
505 guid [8] = (byte) ((guid [8] & 0x3f) | 0x80);
506 // Mask in Version 4 (random based Guid) in Bits[15..13]
507 guid [7] = (byte) ((guid [7] & 0x0f) | 0x40);
512 public byte[] ToByteArray ()
514 byte[] res = new byte[16];
519 tmp = Mono.Security.BitConverterLE.GetBytes(_a);
520 for (s=0; s<4; ++s) {
524 tmp = Mono.Security.BitConverterLE.GetBytes(_b);
525 for (s=0; s<2; ++s) {
529 tmp = Mono.Security.BitConverterLE.GetBytes(_c);
530 for (s=0; s<2; ++s) {
546 static void AppendInt (StringBuilder builder, int value) {
547 builder.Append (ToHex ((value >> 28) & 0xf));
548 builder.Append (ToHex ((value >> 24) & 0xf));
549 builder.Append (ToHex ((value >> 20) & 0xf));
550 builder.Append (ToHex ((value >> 16) & 0xf));
551 builder.Append (ToHex ((value >> 12) & 0xf));
552 builder.Append (ToHex ((value >> 8) & 0xf));
553 builder.Append (ToHex ((value >> 4) & 0xf));
554 builder.Append (ToHex (value & 0xf));
557 static void AppendShort (StringBuilder builder, short value) {
558 builder.Append (ToHex ((value >> 12) & 0xf));
559 builder.Append (ToHex ((value >> 8) & 0xf));
560 builder.Append (ToHex ((value >> 4) & 0xf));
561 builder.Append (ToHex (value & 0xf));
564 static void AppendByte (StringBuilder builder, byte value) {
565 builder.Append (ToHex ((value >> 4) & 0xf));
566 builder.Append (ToHex (value & 0xf));
569 private string BaseToString (bool h, bool p, bool b)
571 StringBuilder res = new StringBuilder (40);
583 AppendShort (res, _b);
587 AppendShort (res, _c);
592 AppendByte (res, _d);
593 AppendByte (res, _e);
599 AppendByte (res, _f);
600 AppendByte (res, _g);
601 AppendByte (res, _h);
602 AppendByte (res, _i);
603 AppendByte (res, _j);
604 AppendByte (res, _k);
613 return res.ToString ();
616 public override string ToString ()
618 return BaseToString (true, false, false);
621 public string ToString (string format)
627 if (format != null) {
628 string f = format.ToLowerInvariant();
639 else if (f != "d" && f != String.Empty) {
640 throw new FormatException (Locale.GetText (
641 "Argument to Guid.ToString(string format) should be \"b\", \"B\", \"d\", \"D\", \"n\", \"N\", \"p\" or \"P\""));
645 return BaseToString (h, p, b);
648 public string ToString (string format, IFormatProvider provider)
650 return ToString (format);
653 public static bool operator == (Guid a, Guid b)
658 public static bool operator != (Guid a, Guid b)
660 return !( a.Equals (b) );
664 public static Guid Parse (string input)
667 if (!TryParse (input, out guid))
668 throw CreateFormatException (input);
673 public static Guid ParseExact (string input, string format)
676 if (!TryParseExact (input, format, out guid))
677 throw CreateFormatException (input);
682 public static bool TryParse (string input, out Guid result)
685 throw new ArgumentNullException ("input");
687 var parser = new GuidParser (input);
688 return parser.Parse (out result);
691 public static bool TryParseExact (string input, string format, out Guid result)
694 throw new ArgumentNullException ("input");
696 throw new ArgumentNullException ("format");
698 var parser = new GuidParser (input);
699 return parser.Parse (ParseFormat (format), out result);
702 static Format ParseFormat (string format)
704 if (format.Length != 1)
705 throw new ArgumentException ("Wrong format");
707 switch (format [0]) {
720 throw new ArgumentException ("Wrong format");