5 // Duco Fijma (duco@lorentz.xs4all.nl)
6 // Sebastien Pouliot (sebastien@ximian.com)
9 // Copyright (C) 2004 Novell (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
16 using System.Runtime.InteropServices;
17 using System.Security.Cryptography;
23 [StructLayout (LayoutKind.Sequential)]
24 public struct Guid : IFormattable, IComparable
26 private uint _timeLow;
27 private ushort _timeMid;
28 private ushort _timeHighAndVersion;
29 private byte _clockSeqHiAndReserved;
30 private byte _clockSeqLow;
38 internal class GuidParser
44 public GuidParser (string src)
53 _length = _src.Length;
58 return _cur >= _length;
61 private void ThrowFormatException ()
63 throw new FormatException (Locale.GetText ("Invalid format for Guid.Guid(string)."));
66 private ulong ParseHex(int length, bool strictLength)
72 for (i=0; (!end) && i<length; ++i) {
74 if (strictLength || i==0) {
75 ThrowFormatException ();
82 char c = Char.ToLowerInvariant (_src[_cur]);
83 if (Char.IsDigit (c)) {
84 res = res * 16 + c - '0';
87 else if (c >= 'a' && c <= 'f') {
88 res = res * 16 + c - 'a' + 10;
92 if (strictLength || i==0) {
93 ThrowFormatException ();
104 private bool ParseOptChar (char c)
106 if (!AtEnd() && _src[_cur] == c) {
115 private void ParseChar (char c)
117 bool b = ParseOptChar (c);
119 ThrowFormatException ();
123 private Guid ParseGuid1 ()
131 byte[] d = new byte[8];
134 openBrace = ParseOptChar ('{');
136 openBrace = ParseOptChar ('(');
137 if (openBrace) endChar = ')';
140 a = (int) ParseHex(8, true);
142 if (openBrace) ParseChar('-');
143 else groups = ParseOptChar('-');
145 b = (short) ParseHex(4, true);
146 if (groups) ParseChar('-');
148 c = (short) ParseHex(4, true);
149 if (groups) ParseChar('-');
151 for (i=0; i<8; ++i) {
152 d[i] = (byte) ParseHex(2, true);
153 if (i == 1 && groups) {
158 if (openBrace && !ParseOptChar(endChar)) {
159 ThrowFormatException ();
162 return new Guid(a, b, c, d);
165 private void ParseHexPrefix ()
171 private Guid ParseGuid2 ()
176 byte[] d = new byte [8];
181 a = (int) ParseHex (8, false);
184 b = (short) ParseHex (4, false);
187 c = (short) ParseHex (4, false);
190 for (i=0; i<8; ++i) {
192 d[i] = (byte) ParseHex (2, false);
200 return new Guid (a,b,c,d);
211 catch (FormatException) {
216 ThrowFormatException ();
222 private static void CheckNull (object o)
225 throw new ArgumentNullException (Locale.GetText ("Value cannot be null."));
229 private static void CheckLength (byte[] o, int l)
231 if (o . Length != l) {
232 throw new ArgumentException (String.Format (Locale.GetText ("Array should be exactly {0} bytes long."), l));
236 private static void CheckArray (byte[] o, int l)
242 public Guid (byte[] b)
245 _timeLow = System.BitConverter.ToUInt32 (b, 0);
246 _timeMid = System.BitConverter.ToUInt16 (b, 4);
247 _timeHighAndVersion = System.BitConverter.ToUInt16 (b, 6);
248 _clockSeqHiAndReserved = b[8];
258 public Guid (string g)
262 GuidParser p = new GuidParser (g);
263 Guid guid = p.Parse();
268 public Guid (int a, short b, short c, byte[] d)
272 _timeMid = (ushort) b;
273 _timeHighAndVersion = (ushort) c;
274 _clockSeqHiAndReserved = d[0];
284 public Guid (int a, short b, short c, byte d, byte e, byte f, byte g, byte h, byte i, byte j, byte k)
285 : this((uint) a, (ushort) b, (ushort) c, d, e, f, g, h, i, j, k)
289 [CLSCompliant (false)]
290 public Guid (uint a, ushort b, ushort c, byte d, byte e, byte f, byte g, byte h, byte i, byte j, byte k)
294 _timeHighAndVersion = c;
295 _clockSeqHiAndReserved = d;
305 public static readonly Guid Empty = new Guid (0,0,0,0,0,0,0,0,0,0,0);
307 private static int Compare (uint x, uint y)
317 public int CompareTo (object value )
322 if (!(value is Guid)) {
323 throw new ArgumentException (Locale.GetText (
324 "Argument of System.Guid.CompareTo should be a Guid."));
327 Guid v = (Guid) value;
329 if (_timeLow != v._timeLow ) {
330 return Compare(_timeLow, v._timeLow);
332 else if (_timeMid != v._timeMid) {
333 return Compare(_timeMid, v._timeMid);
335 else if (_timeHighAndVersion != v._timeHighAndVersion) {
336 return Compare(_timeHighAndVersion, v._timeHighAndVersion);
338 else if (_clockSeqHiAndReserved != v._clockSeqHiAndReserved) {
339 return Compare(_clockSeqHiAndReserved, v._clockSeqHiAndReserved);
341 else if (_clockSeqLow != v._clockSeqLow) {
342 return Compare(_clockSeqLow, v._clockSeqLow);
344 else if (_node0 != v._node0) {
345 return Compare(_node0, v._node0);
347 else if (_node1 != v._node1) {
348 return Compare(_node1, v._node1);
350 else if (_node2 != v._node2) {
351 return Compare(_node2, v._node2);
353 else if (_node3 != v._node3) {
354 return Compare(_node3, v._node3);
356 else if (_node4 != v._node4) {
357 return Compare(_node4, v._node4);
359 else if (_node5 != v._node5) {
360 return Compare(_node5, v._node5);
365 public override bool Equals (object o)
368 return CompareTo (o) == 0;
370 catch (ArgumentException) {
375 public override int GetHashCode ()
379 res = (int) _timeLow;
380 res = res ^ ((int) _timeMid << 16 | _timeHighAndVersion);
381 res = res ^ ((int) _clockSeqHiAndReserved << 24);
382 res = res ^ ((int) _clockSeqLow << 16);
383 res = res ^ ((int) _node0 << 8);
384 res = res ^ ((int) _node1);
385 res = res ^ ((int) _node2 << 24);
386 res = res ^ ((int) _node3 << 16);
387 res = res ^ ((int) _node4 << 8);
388 res = res ^ ((int) _node5);
393 private static char ToHex (int b)
395 return (char)((b<0xA)?('0' + b):('a' + b - 0xA));
398 private static object _rngAccess = new object ();
399 private static RandomNumberGenerator _rng;
400 private static RandomNumberGenerator _fastRng;
402 // generated as per section 3.4 of the specification
403 public static Guid NewGuid ()
405 byte[] b = new byte [16];
407 // thread-safe access to the prng
410 _rng = RandomNumberGenerator.Create ();
414 Guid res = new Guid (b);
415 // Mask in Variant 1-0 in Bit[7..6]
416 res._clockSeqHiAndReserved = (byte) ((res._clockSeqHiAndReserved & 0x3fu) | 0x80u);
417 // Mask in Version 4 (random based Guid) in Bits[15..13]
418 res._timeHighAndVersion = (ushort) ((res._timeHighAndVersion & 0x0fffu) | 0x4000u);
423 // used in ModuleBuilder so mcs doesn't need to invoke
424 // CryptoConfig for simple assemblies.
425 internal static byte[] FastNewGuidArray ()
427 byte[] guid = new byte [16];
429 // thread-safe access to the prng
431 // if known, use preferred RNG
434 // else use hardcoded default RNG (bypassing CryptoConfig)
435 if (_fastRng == null)
436 _fastRng = new RNGCryptoServiceProvider ();
437 _fastRng.GetBytes (guid);
440 // Mask in Variant 1-0 in Bit[7..6]
441 guid [8] = (byte) ((guid [8] & 0x3f) | 0x80);
442 // Mask in Version 4 (random based Guid) in Bits[15..13]
443 guid [7] = (byte) ((guid [7] & 0x0f) | 0x40);
448 public byte[] ToByteArray ()
450 byte[] res = new byte[16];
455 tmp = BitConverter.GetBytes(_timeLow);
456 for (s=0; s<4; ++s) {
460 tmp = BitConverter.GetBytes(_timeMid);
461 for (s=0; s<2; ++s) {
465 tmp = BitConverter.GetBytes(_timeHighAndVersion);
466 for (s=0; s<2; ++s) {
470 res[8] = _clockSeqHiAndReserved;
471 res[9] = _clockSeqLow;
482 private string BaseToString (bool h, bool p, bool b)
484 StringBuilder res = new StringBuilder (40);
492 res.Append (_timeLow.ToString ("x8"));
496 res.Append (_timeMid.ToString ("x4"));
500 res.Append (_timeHighAndVersion.ToString ("x4"));
506 ToHex((_clockSeqHiAndReserved >> 4) & 0xf),
507 ToHex(_clockSeqHiAndReserved & 0xf),
508 ToHex((_clockSeqLow >> 4) & 0xf),
509 ToHex(_clockSeqLow & 0xf)
519 ToHex((_node0 >> 4) & 0xf),
521 ToHex((_node1 >> 4) & 0xf),
523 ToHex((_node2 >> 4) & 0xf),
525 ToHex((_node3 >> 4) & 0xf),
527 ToHex((_node4 >> 4) & 0xf),
529 ToHex((_node5 >> 4) & 0xf),
541 return res.ToString ();
544 public override string ToString ()
546 return BaseToString (true, false, false);
549 public string ToString (string format)
555 if (format != null) {
556 string f = format.ToLowerInvariant();
567 else if (f != "d" && f != "") {
568 throw new FormatException (Locale.GetText (
569 "Argument to Guid.ToString(string format) should be \"b\", \"B\", \"d\", \"D\", \"n\", \"N\", \"p\" or \"P\""));
573 return BaseToString (h, p, b);
576 public string ToString (string format, IFormatProvider provider)
578 return ToString (format);
581 public static bool operator == (Guid a, Guid b)
586 public static bool operator != (Guid a, Guid b)
588 return !( a.Equals (b) );