5 // Duco Fijma (duco@lorentz.xs4all.nl)
10 using System.Security.Cryptography;
16 public struct Guid : IFormattable, IComparable
18 private uint _timeLow;
19 private ushort _timeMid;
20 private ushort _timeHighAndVersion;
21 private byte _clockSeqHiAndReserved;
22 private byte _clockSeqLow;
30 internal class GuidState
32 protected Random _prnd; // Pseudo RNG
33 protected RandomNumberGenerator _rnd; // Strong RNG
34 protected bool _usePRnd; // 'true' for pseudo RNG
35 protected ushort _clockSeq;
36 protected ulong _lastTimestamp;
37 protected byte[] _mac;
39 public int NextInt(uint x)
42 return _prnd.Next ((int) x);
45 byte[] b = new byte[4];
48 uint res = BitConverter.ToUInt32 (b, 0);
54 public void NextBytes(byte[] b)
57 _prnd . NextBytes (b);
64 [MonoTODO("Get real MAC address")]
65 public GuidState (bool usePRnd)
69 _prnd = new Random (unchecked((int) DateTime.Now.Ticks));
72 _rnd = RandomNumberGenerator.Create ();
74 _clockSeq = (ushort) NextInt (0x4000); // 14 bits
81 public ulong NewTimestamp ()
86 timestamp = (ulong) (DateTime.UtcNow - new DateTime (1582, 10, 15, 0, 0, 0)).Ticks;
87 if (timestamp < _lastTimestamp) {
88 // clock moved backwards!
90 _clockSeq = (ushort) (_clockSeq & 0x3fff);
93 if (timestamp > _lastTimestamp) {
94 _lastTimestamp = timestamp;
101 public ushort ClockSeq {
116 internal class GuidParser
122 public GuidParser (string src)
128 private void Reset ()
131 _length = _src.Length;
134 private bool AtEnd ()
136 return _cur >= _length;
139 private void ThrowFormatException ()
141 throw new FormatException (Locale.GetText ("Invalid format for Guid.Guid(string)."));
144 private ulong ParseHex(int length, bool strictLength)
150 for (i=0; (!end) && i<length; ++i) {
152 if (strictLength || i==0) {
153 ThrowFormatException ();
160 char c = Char.ToLower (_src[_cur]);
161 if (Char.IsDigit (c)) {
162 res = res * 16 + c - '0';
165 else if (c >= 'a' && c <= 'f') {
166 res = res * 16 + c - 'a' + 10;
170 if (strictLength || i==0) {
171 ThrowFormatException ();
182 private bool ParseOptChar (char c)
184 if (!AtEnd() && _src[_cur] == c) {
193 private void ParseChar (char c)
195 bool b = ParseOptChar (c);
197 ThrowFormatException ();
201 private Guid ParseGuid1 ()
207 byte[] d = new byte[8];
210 openBrace = ParseOptChar ('{');
211 a = (int) ParseHex(8, true);
213 b = (short) ParseHex(4, true);
215 c = (short) ParseHex(4, true);
217 for (i=0; i<8; ++i) {
218 d[i] = (byte) ParseHex(2, true);
224 if (openBrace && !ParseOptChar('}')) {
225 ThrowFormatException ();
228 return new Guid(a, b, c, d);
231 private void ParseHexPrefix ()
237 private Guid ParseGuid2 ()
242 byte[] d = new byte [8];
247 a = (int) ParseHex (8, false);
250 b = (short) ParseHex (4, false);
253 c = (short) ParseHex (4, false);
256 for (i=0; i<8; ++i) {
258 d[i] = (byte) ParseHex (2, false);
266 return new Guid (a,b,c,d);
277 catch (FormatException) {
282 ThrowFormatException ();
288 private static GuidState _guidState = new GuidState ( true /* use pseudo RNG? */ );
290 private static void CheckNull (object o)
293 throw new ArgumentNullException (Locale.GetText ("Value cannot be null."));
297 private static void CheckLength (byte[] o, int l)
299 if (o . Length != l) {
300 throw new ArgumentException (String.Format (Locale.GetText ("Array should be exactly {0} bytes long."), l));
304 private static void CheckArray (byte[] o, int l)
310 public Guid (byte[] b)
313 _timeLow = System.BitConverter.ToUInt32 (b, 0);
314 _timeMid = System.BitConverter.ToUInt16 (b, 4);
315 _timeHighAndVersion = System.BitConverter.ToUInt16 (b, 6);
316 _clockSeqHiAndReserved = b[8];
326 public Guid (string g)
330 GuidParser p = new GuidParser (g);
331 Guid guid = p.Parse();
336 public Guid (int a, short b, short c, byte[] d)
340 _timeMid = (ushort) b;
341 _timeHighAndVersion = (ushort) c;
342 _clockSeqHiAndReserved = d[0];
352 public Guid (int a, short b, short c, byte d, byte e, byte f, byte g, byte h, byte i, byte j, byte k)
353 : this((uint) a, (ushort) b, (ushort) c, d, e, f, g, h, i, j, k)
357 [CLSCompliant (false)]
358 public Guid (uint a, ushort b, ushort c, byte d, byte e, byte f, byte g, byte h, byte i, byte j, byte k)
362 _timeHighAndVersion = c;
363 _clockSeqHiAndReserved = d;
373 public static readonly Guid Empty = new Guid (0,0,0,0,0,0,0,0,0,0,0);
375 private static int Compare (uint x, uint y)
385 public int CompareTo (object value )
390 if (!(value is Guid)) {
391 throw new ArgumentException (Locale.GetText (
392 "Argument of System.Guid.CompareTo should be a Guid."));
395 Guid v = (Guid) value;
397 if (_timeLow != v._timeLow ) {
398 return Compare(_timeLow, v._timeLow);
400 else if (_timeMid != v._timeMid) {
401 return Compare(_timeMid, v._timeMid);
403 else if (_timeHighAndVersion != v._timeHighAndVersion) {
404 return Compare(_timeHighAndVersion, v._timeHighAndVersion);
406 else if (_clockSeqHiAndReserved != v._clockSeqHiAndReserved) {
407 return Compare(_clockSeqHiAndReserved, v._clockSeqHiAndReserved);
409 else if (_clockSeqLow != v._clockSeqLow) {
410 return Compare(_clockSeqLow, v._clockSeqLow);
412 else if (_node0 != v._node0) {
413 return Compare(_node0, v._node0);
415 else if (_node1 != v._node1) {
416 return Compare(_node1, v._node1);
418 else if (_node2 != v._node2) {
419 return Compare(_node2, v._node2);
421 else if (_node3 != v._node3) {
422 return Compare(_node3, v._node3);
424 else if (_node4 != v._node4) {
425 return Compare(_node4, v._node4);
427 else if (_node5 != v._node5) {
428 return Compare(_node5, v._node5);
433 public override bool Equals (object o)
436 return CompareTo (o) == 0;
438 catch (ArgumentException) {
443 public override int GetHashCode ()
447 res = (int) _timeLow;
448 res = res ^ ((int) _timeMid << 16 | _timeHighAndVersion);
449 res = res ^ ((int) _clockSeqHiAndReserved << 24);
450 res = res ^ ((int) _clockSeqLow << 16);
451 res = res ^ ((int) _node0 << 8);
452 res = res ^ ((int) _node1);
453 res = res ^ ((int) _node2 << 24);
454 res = res ^ ((int) _node3 << 16);
455 res = res ^ ((int) _node4 << 8);
456 res = res ^ ((int) _node5);
461 private static Guid NewTimeGuid ()
463 ulong timestamp = _guidState.NewTimestamp ();
465 // Bit [31..0] (32 bits) for timeLow
466 uint timeLow = (uint) (timestamp & 0x00000000fffffffful);
467 // Bit [47..32] (16 bits) for timeMid
468 ushort timeMid = (ushort) ((timestamp & 0x0000ffff00000000ul) >> 32);
469 // Bit [59..48] (12 bits) for timeHi
470 ushort timeHi = (ushort) ((timestamp & 0x0fff000000000000ul) >> 48);
471 // Bit [7..0] (8 bits) for clockSeqLo
472 byte clockSeqLow = (byte) (Guid._guidState.ClockSeq & 0x00ffu);
473 // Bit [13..8] (6 bits) for clockSeqHi
474 byte clockSeqHi = (byte) ((Guid._guidState.ClockSeq & 0x3f00u) >> 8);
475 byte[] mac = _guidState.MAC;
477 clockSeqHi = (byte) (clockSeqHi | 0x80u); // Bit[7] = 1, Bit[6] = 0 (Variant)
478 timeHi = (ushort) (timeHi | 0x1000u); // Bit[15..13] = 1 (Guid is time-based)
480 return new Guid (timeLow, timeMid, timeHi, clockSeqHi, clockSeqLow,
481 mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
484 private static Guid NewRandomGuid ()
486 byte[] b = new byte[16];
488 _guidState.NextBytes (b);
490 Guid res = new Guid(b);
491 // Mask in Variant 1-0 in Bit[7..6]
492 res._clockSeqHiAndReserved = (byte) ((res._clockSeqHiAndReserved & 0x3fu) | 0x80u);
493 // Mask in Version 4 (random based Guid) in Bits[15..13]
494 res._timeHighAndVersion = (ushort) ((res._timeHighAndVersion & 0x0fffu) | 0x4000u);
498 private static char ToHex (int b)
500 return (char)((b<0xA)?('0' + b):('a' + b - 0xA));
503 public static Guid NewGuid ()
505 return NewRandomGuid();
508 public byte[] ToByteArray ()
510 byte[] res = new byte[16];
515 tmp = BitConverter.GetBytes(_timeLow);
516 for (s=0; s<4; ++s) {
520 tmp = BitConverter.GetBytes(_timeMid);
521 for (s=0; s<2; ++s) {
525 tmp = BitConverter.GetBytes(_timeHighAndVersion);
526 for (s=0; s<2; ++s) {
530 res[8] = _clockSeqHiAndReserved;
531 res[9] = _clockSeqLow;
542 private string BaseToString (bool h, bool p, bool b)
544 StringBuilder res = new StringBuilder (40);
552 res.Append (_timeLow.ToString ("x8"));
556 res.Append (_timeMid.ToString ("x4"));
560 res.Append (_timeHighAndVersion.ToString ("x4"));
566 ToHex((_clockSeqHiAndReserved >> 4) & 0xf),
567 ToHex(_clockSeqHiAndReserved & 0xf),
568 ToHex((_clockSeqLow >> 4) & 0xf),
569 ToHex(_clockSeqLow & 0xf)
579 ToHex((_node0 >> 4) & 0xf),
581 ToHex((_node1 >> 4) & 0xf),
583 ToHex((_node2 >> 4) & 0xf),
585 ToHex((_node3 >> 4) & 0xf),
587 ToHex((_node4 >> 4) & 0xf),
589 ToHex((_node5 >> 4) & 0xf),
601 return res.ToString ();
604 public override string ToString ()
606 return BaseToString (true, false, false);
609 public string ToString (string format)
616 if (format != null) {
617 f = format.ToLower();
628 else if (f != "d" && f != "") {
629 throw new FormatException (Locale.GetText (
630 "Argument to Guid.ToString(string format) should be \"b\", \"B\", \"d\", \"D\", \"n\", \"N\", \"p\" or \"P\""));
634 return BaseToString (h, p, b);
637 public string ToString (string format, IFormatProvider provider)
639 return ToString (format);
642 public static bool operator == (Guid a, Guid b)
647 public static bool operator != (Guid a, Guid b)
649 return !( a.Equals (b) );