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");