Jo Shields a575963da9 Imported Upstream version 3.6.0
Former-commit-id: da6be194a6b1221998fc28233f2503bd61dd9d14
2014-08-13 10:39:27 +01:00

838 lines
18 KiB
C#

//
// System.Guid.cs
//
// Authors:
// Duco Fijma (duco@lorentz.xs4all.nl)
// Sebastien Pouliot (sebastien@ximian.com)
// Jb Evain (jbevain@novell.com)
// Marek Safar (marek.safar@gmail.com)
//
// (C) 2002 Duco Fijma
// Copyright (C) 2004-2010 Novell, Inc (http://www.novell.com)
// Copyright 2012, 2014 Xamarin, Inc (http://www.xamarin.com)
//
// References
// 1. UUIDs and GUIDs (DRAFT), Section 3.4
// http://www.ics.uci.edu/~ejw/authoring/uuid-guid/draft-leach-uuids-guids-01.txt
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
using System.Runtime.InteropServices;
using System.Security.Cryptography;
using System.Text;
#if MONOTOUCH && FULL_AOT_RUNTIME
using Crimson.CommonCrypto;
#endif
namespace System {
[Serializable]
[StructLayout (LayoutKind.Sequential)]
[ComVisible (true)]
public struct Guid : IFormattable, IComparable, IComparable<Guid>, IEquatable<Guid> {
#if MONOTOUCH
static Guid () {
if (MonoTouchAOTHelper.FalseFlag) {
var comparer = new System.Collections.Generic.GenericComparer <Guid> ();
var eqcomparer = new System.Collections.Generic.GenericEqualityComparer <Guid> ();
}
}
#endif
private int _a; //_timeLow;
private short _b; //_timeMid;
private short _c; //_timeHighAndVersion;
private byte _d; //_clockSeqHiAndReserved;
private byte _e; //_clockSeqLow;
private byte _f; //_node0;
private byte _g; //_node1;
private byte _h; //_node2;
private byte _i; //_node3;
private byte _j; //_node4;
private byte _k; //_node5;
enum Format {
N, // 00000000000000000000000000000000
D, // 00000000-0000-0000-0000-000000000000
B, // {00000000-0000-0000-0000-000000000000}
P, // (00000000-0000-0000-0000-000000000000)
X, // {0x00000000,0x0000,0x0000,{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}}
}
struct GuidParser
{
private string _src;
private int _length;
private int _cur;
public GuidParser (string src)
{
_src = src.Trim ();
_cur = 0;
_length = _src.Length;
}
void Reset ()
{
_cur = 0;
_length = _src.Length;
}
bool Eof {
get { return _cur >= _length; }
}
public static bool HasHyphen (Format format)
{
switch (format) {
case Format.D:
case Format.B:
case Format.P:
return true;
default:
return false;
}
}
bool TryParseNDBP (Format format, out Guid guid)
{
ulong a, b, c;
guid = new Guid ();
if (format == Format.B && !ParseChar ('{'))
return false;
if (format == Format.P && !ParseChar ('('))
return false;
if (!ParseHex (8, true, out a))
return false;
var has_hyphen = HasHyphen (format);
if (has_hyphen && !ParseChar ('-'))
return false;
if (!ParseHex (4, true, out b))
return false;
if (has_hyphen && !ParseChar ('-'))
return false;
if (!ParseHex (4, true, out c))
return false;
if (has_hyphen && !ParseChar ('-'))
return false;
var d = new byte [8];
for (int i = 0; i < d.Length; i++) {
ulong dd;
if (!ParseHex (2, true, out dd))
return false;
if (i == 1 && has_hyphen && !ParseChar ('-'))
return false;
d [i] = (byte) dd;
}
if (format == Format.B && !ParseChar ('}'))
return false;
if (format == Format.P && !ParseChar (')'))
return false;
if (!Eof)
return false;
guid = new Guid ((int) a, (short) b, (short) c, d);
return true;
}
bool TryParseX (out Guid guid)
{
ulong a, b, c;
guid = new Guid ();
if (!(ParseChar ('{')
&& ParseHexPrefix ()
&& ParseHex (8, false, out a)
&& ParseCharWithWhiteSpaces (',')
&& ParseHexPrefix ()
&& ParseHex (4, false, out b)
&& ParseCharWithWhiteSpaces (',')
&& ParseHexPrefix ()
&& ParseHex (4, false, out c)
&& ParseCharWithWhiteSpaces (',')
&& ParseCharWithWhiteSpaces ('{'))) {
return false;
}
var d = new byte [8];
for (int i = 0; i < d.Length; ++i) {
ulong dd;
if (!(ParseHexPrefix () && ParseHex (2, false, out dd)))
return false;
d [i] = (byte) dd;
if (i != 7 && !ParseCharWithWhiteSpaces (','))
return false;
}
if (!(ParseCharWithWhiteSpaces ('}') && ParseCharWithWhiteSpaces ('}')))
return false;
if (!Eof)
return false;
guid = new Guid ((int) a, (short) b, (short) c, d);
return true;
}
bool ParseHexPrefix ()
{
// It can be prefixed with whitespaces
for (; _cur < _length - 1; ++_cur) {
var ch = _src [_cur];
if (ch == '0') {
++_cur;
return _src [_cur++] == 'x';
}
if (!char.IsWhiteSpace (ch))
break;
}
return false;
}
bool ParseCharWithWhiteSpaces (char c)
{
while (!Eof) {
var ch = _src [_cur++];
if (ch == c)
return true;
if (!char.IsWhiteSpace (ch))
break;
}
return false;
}
bool ParseChar (char c)
{
if (!Eof && _src [_cur] == c) {
_cur++;
return true;
}
return false;
}
bool ParseHex (int length, bool strict, out ulong res)
{
res = 0;
for (int i = 0; i < length; i++) {
if (Eof)
return !(strict && (i + 1 != length));
char c = _src [_cur];
if (Char.IsDigit (c)) {
res = res * 16 + c - '0';
_cur++;
continue;
}
if (c >= 'a' && c <= 'f') {
res = res * 16 + c - 'a' + 10;
_cur++;
continue;
}
if (c >= 'A' && c <= 'F') {
res = res * 16 + c - 'A' + 10;
_cur++;
continue;
}
if (!strict)
return true;
return false; //!(strict && (i + 1 != length));
}
return true;
}
public bool Parse (Format format, out Guid guid)
{
if (format == Format.X)
return TryParseX (out guid);
return TryParseNDBP (format, out guid);
}
public bool Parse (out Guid guid)
{
switch (_length) {
case 32:
if (TryParseNDBP (Format.N, out guid))
return true;
break;
case 36:
if (TryParseNDBP (Format.D, out guid))
return true;
break;
case 38:
switch (_src [0]) {
case '{':
if (TryParseNDBP (Format.B, out guid))
return true;
break;
case '(':
if (TryParseNDBP (Format.P, out guid))
return true;
break;
}
break;
}
Reset ();
return TryParseX (out guid);
}
}
private static void CheckNull (object o)
{
if (o == null) {
throw new ArgumentNullException (Locale.GetText ("Value cannot be null."));
}
}
private static void CheckLength (byte[] o, int l)
{
if (o . Length != l) {
throw new ArgumentException (String.Format (Locale.GetText ("Array should be exactly {0} bytes long."), l));
}
}
private static void CheckArray (byte[] o, int l)
{
CheckNull (o);
CheckLength (o, l);
}
public Guid (byte[] b)
{
CheckArray (b, 16);
_a = Mono.Security.BitConverterLE.ToInt32 (b, 0);
_b = Mono.Security.BitConverterLE.ToInt16 (b, 4);
_c = Mono.Security.BitConverterLE.ToInt16 (b, 6);
_d = b [8];
_e = b [9];
_f = b [10];
_g = b [11];
_h = b [12];
_i = b [13];
_j = b [14];
_k = b [15];
}
public Guid (string g)
{
CheckNull (g);
Guid guid;
if (!new GuidParser (g).Parse (out guid))
throw CreateFormatException (g);
this = guid;
}
static Exception CreateFormatException (string s)
{
return new FormatException (string.Format ("Invalid Guid format: {0}", s));
}
public Guid (int a, short b, short c, byte[] d)
{
CheckArray (d, 8);
_a = (int) a;
_b = (short) b;
_c = (short) c;
_d = d [0];
_e = d [1];
_f = d [2];
_g = d [3];
_h = d [4];
_i = d [5];
_j = d [6];
_k = d [7];
}
public Guid (int a, short b, short c, byte d, byte e, byte f, byte g, byte h, byte i, byte j, byte k)
{
_a = a;
_b = b;
_c = c;
_d = d;
_e = e;
_f = f;
_g = g;
_h = h;
_i = i;
_j = j;
_k = k;
}
[CLSCompliant (false)]
public Guid (uint a, ushort b, ushort c, byte d, byte e, byte f, byte g, byte h, byte i, byte j, byte k)
: this((int) a, (short) b, (short) c, d, e, f, g, h, i, j, k)
{
}
public static readonly Guid Empty = new Guid (0,0,0,0,0,0,0,0,0,0,0);
private static int Compare (int x, int y)
{
return ((uint)x < (uint)y) ? -1 : 1;
}
public int CompareTo (object value)
{
if (value == null)
return 1;
if (!(value is Guid)) {
throw new ArgumentException ("value", Locale.GetText (
"Argument of System.Guid.CompareTo should be a Guid."));
}
return CompareTo ((Guid)value);
}
public override bool Equals (object o)
{
if (o is Guid)
return CompareTo ((Guid)o) == 0;
return false;
}
public int CompareTo (Guid value)
{
if (_a != value._a) {
return Compare (_a, value._a);
}
if (_b != value._b) {
return Compare (_b, value._b);
}
if (_c != value._c) {
return Compare (_c, value._c);
}
if (_d != value._d) {
return Compare (_d, value._d);
}
if (_e != value._e) {
return Compare (_e, value._e);
}
if (_f != value._f) {
return Compare (_f, value._f);
}
if (_g != value._g) {
return Compare (_g, value._g);
}
if (_h != value._h) {
return Compare (_h, value._h);
}
if (_i != value._i) {
return Compare (_i, value._i);
}
if (_j != value._j) {
return Compare (_j, value._j);
}
if (_k != value._k) {
return Compare (_k, value._k);
}
return 0;
}
public bool Equals (Guid g)
{
return CompareTo (g) == 0;
}
public override int GetHashCode ()
{
int res;
res = (int) _a;
res = res ^ ((int) _b << 16 | _c);
res = res ^ ((int) _d << 24);
res = res ^ ((int) _e << 16);
res = res ^ ((int) _f << 8);
res = res ^ ((int) _g);
res = res ^ ((int) _h << 24);
res = res ^ ((int) _i << 16);
res = res ^ ((int) _j << 8);
res = res ^ ((int) _k);
return res;
}
private static char ToHex (int b)
{
return (char)((b<0xA)?('0' + b):('a' + b - 0xA));
}
#if !FULL_AOT_RUNTIME
private static object _rngAccess = new object ();
private static RandomNumberGenerator _rng;
private static RandomNumberGenerator _fastRng;
#endif
#if FULL_AOT_RUNTIME && !MONOTOUCH
// NSA approved random generator.
static void LameRandom (byte [] b)
{
var r = new Random ();
r.NextBytes (b);
}
#endif
// generated as per section 3.4 of the specification
public static Guid NewGuid ()
{
byte[] b = new byte [16];
#if !FULL_AOT_RUNTIME
// thread-safe access to the prng
lock (_rngAccess) {
if (_rng == null)
_rng = RandomNumberGenerator.Create ();
_rng.GetBytes (b);
}
#elif MONOTOUCH
Cryptor.GetRandom (b);
#else
LameRandom (b);
#endif
Guid res = new Guid (b);
// Mask in Variant 1-0 in Bit[7..6]
res._d = (byte) ((res._d & 0x3fu) | 0x80u);
// Mask in Version 4 (random based Guid) in Bits[15..13]
res._c = (short) ((res._c & 0x0fffu) | 0x4000u);
return res;
}
#if !FULL_AOT_RUNTIME
// used in ModuleBuilder so mcs doesn't need to invoke
// CryptoConfig for simple assemblies.
internal static byte[] FastNewGuidArray ()
{
byte[] guid = new byte [16];
// thread-safe access to the prng
lock (_rngAccess) {
// if known, use preferred RNG
if (_rng != null)
_fastRng = _rng;
// else use hardcoded default RNG (bypassing CryptoConfig)
if (_fastRng == null)
_fastRng = new RNGCryptoServiceProvider ();
_fastRng.GetBytes (guid);
}
// Mask in Variant 1-0 in Bit[7..6]
guid [8] = (byte) ((guid [8] & 0x3f) | 0x80);
// Mask in Version 4 (random based Guid) in Bits[15..13]
guid [7] = (byte) ((guid [7] & 0x0f) | 0x40);
return guid;
}
#endif
public byte[] ToByteArray ()
{
byte[] res = new byte[16];
byte[] tmp;
int d = 0;
int s;
tmp = Mono.Security.BitConverterLE.GetBytes(_a);
for (s=0; s<4; ++s) {
res[d++] = tmp[s];
}
tmp = Mono.Security.BitConverterLE.GetBytes(_b);
for (s=0; s<2; ++s) {
res[d++] = tmp[s];
}
tmp = Mono.Security.BitConverterLE.GetBytes(_c);
for (s=0; s<2; ++s) {
res[d++] = tmp[s];
}
res[8] = _d;
res[9] = _e;
res[10] = _f;
res[11] = _g;
res[12] = _h;
res[13] = _i;
res[14] = _j;
res[15] = _k;
return res;
}
static void AppendInt (StringBuilder builder, int value) {
builder.Append (ToHex ((value >> 28) & 0xf));
builder.Append (ToHex ((value >> 24) & 0xf));
builder.Append (ToHex ((value >> 20) & 0xf));
builder.Append (ToHex ((value >> 16) & 0xf));
builder.Append (ToHex ((value >> 12) & 0xf));
builder.Append (ToHex ((value >> 8) & 0xf));
builder.Append (ToHex ((value >> 4) & 0xf));
builder.Append (ToHex (value & 0xf));
}
static void AppendShort (StringBuilder builder, short value) {
builder.Append (ToHex ((value >> 12) & 0xf));
builder.Append (ToHex ((value >> 8) & 0xf));
builder.Append (ToHex ((value >> 4) & 0xf));
builder.Append (ToHex (value & 0xf));
}
static void AppendByte (StringBuilder builder, byte value) {
builder.Append (ToHex ((value >> 4) & 0xf));
builder.Append (ToHex (value & 0xf));
}
string ToString (Format format)
{
int length;
switch (format) {
case Format.B:
case Format.P:
length = 38;
break;
case Format.D:
length = 36;
break;
case Format.N:
length = 32;
break;
case Format.X:
length = 68;
break;
default:
throw new NotImplementedException (format.ToString ());
}
StringBuilder res = new StringBuilder (length);
bool has_hyphen = GuidParser.HasHyphen (format);
if (format == Format.P) {
res.Append ('(');
} else if (format == Format.B) {
res.Append ('{');
} else if (format == Format.X) {
res.Append ('{').Append ('0').Append ('x');
}
AppendInt (res, _a);
if (has_hyphen) {
res.Append ('-');
} else if (format == Format.X) {
res.Append (',').Append ('0').Append ('x');
}
AppendShort (res, _b);
if (has_hyphen) {
res.Append ('-');
} else if (format == Format.X) {
res.Append (',').Append ('0').Append ('x');
}
AppendShort (res, _c);
if (has_hyphen) {
res.Append ('-');
}
if (format == Format.X) {
res.Append (',').Append ('{').Append ('0').Append ('x');
AppendByte (res, _d);
res.Append (',').Append ('0').Append ('x');
AppendByte (res, _e);
res.Append (',').Append ('0').Append ('x');
AppendByte (res, _f);
res.Append (',').Append ('0').Append ('x');
AppendByte (res, _g);
res.Append (',').Append ('0').Append ('x');
AppendByte (res, _h);
res.Append (',').Append ('0').Append ('x');
AppendByte (res, _i);
res.Append (',').Append ('0').Append ('x');
AppendByte (res, _j);
res.Append (',').Append ('0').Append ('x');
AppendByte (res, _k);
res.Append ('}').Append ('}');;
} else {
AppendByte (res, _d);
AppendByte (res, _e);
if (has_hyphen) {
res.Append ('-');
}
AppendByte (res, _f);
AppendByte (res, _g);
AppendByte (res, _h);
AppendByte (res, _i);
AppendByte (res, _j);
AppendByte (res, _k);
if (format == Format.P) {
res.Append (')');
} else if (format == Format.B) {
res.Append ('}');
}
}
return res.ToString ();
}
public override string ToString ()
{
return ToString (Format.D);
}
public string ToString (string format)
{
return ToString (ParseFormat (format));
}
// provider value is never used
public string ToString (string format, IFormatProvider provider)
{
return ToString (format);
}
public static bool operator == (Guid a, Guid b)
{
return a.Equals(b);
}
public static bool operator != (Guid a, Guid b)
{
return !( a.Equals (b) );
}
#if NET_4_0
public static Guid Parse (string input)
{
if (input == null)
throw new ArgumentNullException ("input");
Guid guid;
if (!TryParse (input, out guid))
throw CreateFormatException (input);
return guid;
}
public static Guid ParseExact (string input, string format)
{
if (input == null)
throw new ArgumentNullException ("input");
if (format == null)
throw new ArgumentNullException ("format");
Guid guid;
if (!TryParseExact (input, format, out guid))
throw CreateFormatException (input);
return guid;
}
public static bool TryParse (string input, out Guid result)
{
if (input == null) {
result = Empty;
return false;
}
var parser = new GuidParser (input);
return parser.Parse (out result);
}
public static bool TryParseExact (string input, string format, out Guid result)
{
if (input == null || format == null) {
result = Empty;
return false;
}
var parser = new GuidParser (input);
return parser.Parse (ParseFormat (format), out result);
}
#endif
static Format ParseFormat (string format)
{
if (string.IsNullOrEmpty (format))
return Format.D;
switch (format [0]) {
case 'N':
case 'n':
return Format.N;
case 'D':
case 'd':
return Format.D;
case 'B':
case 'b':
return Format.B;
case 'P':
case 'p':
return Format.P;
#if NET_4_0
case 'X':
case 'x':
return Format.X;
#endif
}
throw new FormatException (
#if NET_4_0
"Format String can be only one of \"D\", \"d\", \"N\", \"n\", \"P\", \"p\", \"B\", \"b\", \"X\" or \"x\""
#else
"Format String can be only one of \"D\", \"d\", \"N\", \"n\", \"P\", \"p\", \"B\" or \"b\""
#endif
);
}
}
}