Merge pull request #495 from nicolas-raoul/fix-for-issue2907-with-no-formatting-changes
[mono.git] / mcs / class / corlib / System / Guid.cs
index 8ec1024d0e266a517c80b10ec76b8796b8e585db..e8dd2247d519a77d125f8c604331daba6b6a593d 100644 (file)
@@ -4,9 +4,12 @@
 // 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-2005 Novell, Inc (http://www.novell.com)
+// Copyright (C) 2004-2010 Novell, Inc (http://www.novell.com)
+// Copyright 2012 Xamarin, Inc (http://www.xamarin.com)
 //
 // References
 // 1.  UUIDs and GUIDs (DRAFT), Section 3.4
@@ -40,11 +43,15 @@ namespace System {
 
        [Serializable]
        [StructLayout (LayoutKind.Sequential)]
-#if NET_2_0
        [ComVisible (true)]
        public struct Guid : IFormattable, IComparable, IComparable<Guid>, IEquatable<Guid> {
-#else
-       public struct Guid : IFormattable, IComparable {
+#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;
@@ -58,8 +65,16 @@ namespace System {
                private byte _j; //_node4;
                private byte _k; //_node5;
 
-               internal class GuidParser
-               {
+               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}}
+               }
+
+               class GuidParser {
+
                        private string _src;
                        private int _length;
                        private int _cur;
@@ -70,175 +85,198 @@ namespace System {
                                Reset ();
                        }
 
-                       private void Reset ()
+                       void Reset ()
                        {
                                _cur = 0;
                                _length = _src.Length;
                        }
 
-                       private bool AtEnd ()
-                       {
-                               return _cur >= _length;
+                       bool Eof {
+                               get { return _cur >= _length; }
                        }
 
-                       private void ThrowFormatException ()
+                       public static bool HasHyphen (Format format)
                        {
-                               throw new FormatException (Locale.GetText ("Invalid format for Guid.Guid(string)."));
+                               switch (format) {
+                               case Format.D:
+                               case Format.B:
+                               case Format.P:
+                                       return true;
+                               default:
+                                       return false;
+                               }
                        }
 
-                       private ulong ParseHex(int length, bool strictLength)
+                       bool TryParseNDBP (Format format, out Guid guid)
                        {
-                               ulong res = 0;
-                               int i;
-                               bool end = false;
-
-                               for (i=0; (!end) && i<length; ++i) {
-                                       if (AtEnd ()) {
-                                               if (strictLength || i==0) {
-                                                       ThrowFormatException ();
-                                               }
-                                               else {
-                                                       end = true;
-                                               }
-                                       }
-                                       else {
-                                               char c = Char.ToLowerInvariant (_src[_cur]);
-                                               if (Char.IsDigit (c)) {
-                                                       res = res * 16 + c - '0';
-                                                       _cur++;
-                                               }
-                                               else if (c >= 'a' && c <= 'f') {
-                                                       res = res * 16 + c - 'a' + 10;
-                                                       _cur++;
-                                               }
-                                               else {
-                                                       if (strictLength || i==0) {
-                                                               ThrowFormatException ();
-                                                       }
-                                                       else {
-                                                               end = true;
-                                                       }
-                                               }
-                                       }
+                               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;
                                }
-                               return res;
+
+                               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;
                        }
 
-                       private bool ParseOptChar (char c)
+                       bool TryParseX (out Guid guid)
                        {
-                               if (!AtEnd() && _src[_cur] == c) {
-                                       _cur++;
-                                       return true;
-                               }
-                               else {
+                               ulong a, b, c;
+                               guid = new Guid ();
+
+                               if (!(ParseChar ('{')
+                                       && ParseHexPrefix ()
+                                       && ParseHex (8, false, out a)
+                                       && ParseChar (',')
+                                       && ParseHexPrefix ()
+                                       && ParseHex (4, false, out b)
+                                       && ParseChar (',')
+                                       && ParseHexPrefix ()
+                                       && ParseHex (4, false, out c)
+                                       && ParseChar (',')
+                                       && ParseChar ('{'))) {
+
                                        return false;
                                }
-                       }
 
-                       private void ParseChar (char c)
-                       {
-                               bool b = ParseOptChar (c);
-                               if (!b) {
-                                       ThrowFormatException ();
+                               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 && !ParseChar (','))
+                                               return false;
                                }
+
+                               if (!(ParseChar ('}') && ParseChar ('}')))
+                                       return false;
+
+                               if (!Eof)
+                                       return false;
+
+                               guid = new Guid ((int) a, (short) b, (short) c, d);
+                               return true;
                        }
 
-                       private Guid ParseGuid1 ()
+                       bool ParseHexPrefix ()
                        {
-                               bool openBrace; 
-                               bool groups = true;
-                               char endChar = '}';
-                               int a;
-                               short b;
-                               short c;
-                               byte[] d = new byte[8];
-                               int i;
-
-                               openBrace = ParseOptChar ('{');
-                               if (!openBrace) {
-                                       openBrace = ParseOptChar ('(');
-                                       if (openBrace) endChar = ')';
-                               }
-                               
-                               a = (int) ParseHex(8, true);
-                               
-                               if (openBrace) ParseChar('-');
-                               else groups = ParseOptChar('-');
-                               
-                               b = (short) ParseHex(4, true);
-                               if (groups) ParseChar('-');
-                               
-                               c = (short) ParseHex(4, true);
-                               if (groups) ParseChar('-');
-                               
-                               for (i=0; i<8; ++i) {
-                                       d[i] = (byte) ParseHex(2, true);
-                                       if (i == 1 && groups) {
-                                               ParseChar('-');
-                                       }       
-                               }
-       
-                               if (openBrace && !ParseOptChar(endChar)) {
-                                       ThrowFormatException ();
-                               }
-               
-                               return new Guid(a, b, c, d);
+                               if (!ParseChar ('0'))
+                                       return false;
+
+                               return ParseChar ('x');
                        }
 
-                       private void ParseHexPrefix ()
+                       bool ParseChar (char c)
                        {
-                               ParseChar ('0');
-                               ParseChar ('x');
+                               if (!Eof && _src [_cur] == c) {
+                                       _cur++;
+                                       return true;
+                               }
+
+                               return false;
                        }
 
-                       private Guid ParseGuid2 ()
+                       bool ParseHex (int length, bool strict, out ulong res)
                        {
-                               int a;
-                               short b;
-                               short c;
-                               byte[] d = new byte [8];
-                               int i;
-
-                               ParseChar ('{');
-                               ParseHexPrefix ();
-                               a = (int) ParseHex (8, false);
-                               ParseChar (',');
-                               ParseHexPrefix ();
-                               b = (short) ParseHex (4, false);
-                               ParseChar (',');
-                               ParseHexPrefix ();
-                               c = (short) ParseHex (4, false);
-                               ParseChar (',');
-                               ParseChar ('{');
-                               for (i=0; i<8; ++i) {
-                                       ParseHexPrefix ();
-                                       d[i] = (byte) ParseHex (2, false);
-                                       if (i != 7) {
-                                               ParseChar (',');
+                               res = 0;
+
+                               for (int i = 0; i < length; i++) {
+                                       if (Eof)
+                                               return !(strict && (i + 1 != length));
+
+                                       char c = Char.ToLowerInvariant (_src[_cur]);
+                                       if (Char.IsDigit (c)) {
+                                               res = res * 16 + c - '0';
+                                               _cur++;
+                                       } else if (c >= 'a' && c <= 'f') {
+                                               res = res * 16 + c - 'a' + 10;
+                                               _cur++;
+                                       } else {
+                                               if (!strict)
+                                                       return true;
+
+                                               return !(strict && (i + 1 != length));
                                        }
                                }
-                               ParseChar ('}');
-                               ParseChar ('}');
 
-                               return new Guid (a,b,c,d);
-                               
+                               return true;
                        }
 
-                       public Guid Parse ()
+                       public bool Parse (Format format, out Guid guid)
                        {
-                               Guid g;
+                               if (format == Format.X)
+                                       return TryParseX (out guid);
 
-                               try {
-                                       g  = ParseGuid1 ();
-                               }
-                               catch (FormatException) {
-                                       Reset ();
-                                       g = ParseGuid2 ();
-                               }
-                               if (!AtEnd () ) {
-                                       ThrowFormatException ();
-                               }
-                               return g;
+                               return TryParseNDBP (format, out guid);
+                       }
+
+                       public bool Parse (out Guid guid)
+                       {
+                               if (TryParseNDBP (Format.N, out guid))
+                                       return true;
+
+                               Reset ();
+                               if (TryParseNDBP (Format.D, out guid))
+                                       return true;
+
+                               Reset ();
+                               if (TryParseNDBP (Format.B, out guid))
+                                       return true;
+
+                               Reset ();
+                               if (TryParseNDBP (Format.P, out guid))
+                                       return true;
+
+                               Reset ();
+                               return TryParseX (out guid);
                        }
                }
 
@@ -281,13 +319,20 @@ namespace System {
                public Guid (string g)
                {
                        CheckNull (g);
-       
-                       GuidParser p = new GuidParser (g);
-                       Guid guid = p.Parse();
-       
+                       g = g.Trim();
+                       var parser = new GuidParser (g);
+                       Guid guid;
+                       if (!parser.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);
@@ -329,12 +374,7 @@ namespace System {
 
                private static int Compare (int x, int y)
                {
-                       if (x < y) {
-                               return -1;
-                       }
-                       else {
-                               return 1;
-                       }
+                       return ((uint)x < (uint)y) ? -1 : 1;
                }
 
                public int CompareTo (object value)
@@ -357,54 +397,48 @@ namespace System {
                        return false;
                }
 
-#if NET_2_0
                public int CompareTo (Guid value)
-#else
-               internal int CompareTo (Guid value)
-#endif
                {
                        if (_a != value._a) {
                                return Compare (_a, value._a);
                        }
-                       else if (_b != value._b) {
+                       if (_b != value._b) {
                                return Compare (_b, value._b);
                        }
-                       else if (_c != value._c) {
+                       if (_c != value._c) {
                                return Compare (_c, value._c);
                        }
-                       else if (_d != value._d) {
+                       if (_d != value._d) {
                                return Compare (_d, value._d);
                        }
-                       else if (_e != value._e) {
+                       if (_e != value._e) {
                                return Compare (_e, value._e);
                        }
-                       else if (_f != value._f) {
+                       if (_f != value._f) {
                                return Compare (_f, value._f);
                        }
-                       else if (_g != value._g) {
+                       if (_g != value._g) {
                                return Compare (_g, value._g);
                        }
-                       else if (_h != value._h) {
+                       if (_h != value._h) {
                                return Compare (_h, value._h);
                        }
-                       else if (_i != value._i) {
+                       if (_i != value._i) {
                                return Compare (_i, value._i);
                        }
-                       else if (_j != value._j) {
+                       if (_j != value._j) {
                                return Compare (_j, value._j);
                        }
-                       else if (_k != value._k) {
+                       if (_k != value._k) {
                                return Compare (_k, value._k);
                        }
                        return 0;
                }
 
-#if NET_2_0
                public bool Equals (Guid g)
                {
                        return CompareTo (g) == 0;
                }
-#endif
 
                public override int GetHashCode ()
                {
@@ -430,12 +464,17 @@ namespace System {
                }
 
                private static object _rngAccess = new object ();
+#if !FULL_AOT_RUNTIME
                private static RandomNumberGenerator _rng;
                private static RandomNumberGenerator _fastRng;
+#else
+               private static object _fastRng;
+#endif
 
                // generated as per section 3.4 of the specification
                public static Guid NewGuid ()
                {
+#if !FULL_AOT_RUNTIME
                        byte[] b = new byte [16];
 
                        // thread-safe access to the prng
@@ -444,6 +483,10 @@ namespace System {
                                        _rng = RandomNumberGenerator.Create ();
                                _rng.GetBytes (b);
                        }
+#else
+                       byte[] b = FastNewGuidArray ();
+#endif
+
 
                        Guid res = new Guid (b);
                        // Mask in Variant 1-0 in Bit[7..6]
@@ -463,12 +506,18 @@ namespace System {
                        // thread-safe access to the prng
                        lock (_rngAccess) {
                                // if known, use preferred RNG
+#if FULL_AOT_RUNTIME
+                               if (_fastRng == null)
+                                       _fastRng = new RNGCryptoServiceProvider ();
+                               (_fastRng as RNGCryptoServiceProvider).GetBytes (guid);
+#else
                                if (_rng != null)
                                        _fastRng = _rng;
                                // else use hardcoded default RNG (bypassing CryptoConfig)
                                if (_fastRng == null)
                                        _fastRng = new RNGCryptoServiceProvider ();
                                _fastRng.GetBytes (guid);
+#endif
                        }
 
                        // Mask in Variant 1-0 in Bit[7..6]
@@ -513,63 +562,118 @@ namespace System {
                        return res;
                }
 
-               private string BaseToString (bool h, bool p, bool b)
+               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)
                {
-                       StringBuilder res = new StringBuilder (40);
+                       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 (p) {
+                       if (format == Format.P) {
                                res.Append ('(');
-                       } else if (b) {
+                       } else if (format == Format.B) {
                                res.Append ('{');
+                       } else if (format == Format.X) {
+                               res.Append ('{').Append ('0').Append ('x');
                        }
                
-                       res.Append (_a.ToString ("x8"));
-                       if (h) {
+                       AppendInt (res, _a);
+                       if (has_hyphen) {
                                res.Append ('-');
+                       } else if (format == Format.X) {
+                               res.Append (',').Append ('0').Append ('x');
                        }
-                       res.Append (_b.ToString ("x4"));
-                       if (h) {
-                               res.Append ('-');
-                       }
-                       res.Append (_c.ToString ("x4"));
-                       if (h) {
+                       
+                       AppendShort (res, _b);
+                       if (has_hyphen) {
                                res.Append ('-');
+                       } else if (format == Format.X) {
+                               res.Append (',').Append ('0').Append ('x');
                        }
-       
-                       char[] vals1 = {
-                               ToHex((_d >> 4) & 0xf),
-                               ToHex(_d & 0xf),
-                               ToHex((_e >> 4) & 0xf),
-                               ToHex(_e & 0xf)
-                       };
-
-                       res.Append (vals1);
 
-                       if (h) {
+                       AppendShort (res, _c);
+                       if (has_hyphen) {
                                res.Append ('-');
                        }
-               
-                       char[] vals2 = {
-                               ToHex((_f >> 4) & 0xf),
-                               ToHex(_f & 0xf),
-                               ToHex((_g >> 4) & 0xf),
-                               ToHex(_g & 0xf),
-                               ToHex((_h >> 4) & 0xf),
-                               ToHex(_h & 0xf),
-                               ToHex((_i >> 4) & 0xf),
-                               ToHex(_i & 0xf),
-                               ToHex((_j >> 4) & 0xf),
-                               ToHex(_j & 0xf),
-                               ToHex((_k >> 4) & 0xf),
-                               ToHex(_k & 0xf)
-                       };
        
-                       res.Append (vals2);
+                       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 ('-');
+                               }
        
-                       if (p) {
-                               res.Append (')');
-                       } else if (b) {
-                               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 ();
@@ -577,36 +681,15 @@ namespace System {
        
                public override string ToString ()
                {
-                       return BaseToString (true, false, false);
+                       return ToString (Format.D);
                }
        
                public string ToString (string format)
                {
-                       bool h = true;
-                       bool p = false;
-                       bool b = false;
-       
-                       if (format != null) {
-                               string f = format.ToLowerInvariant();
-       
-                               if (f == "b") {
-                                       b = true;
-                               }
-                               else if (f == "p") {
-                                       p = true;
-                               }
-                               else if (f == "n") {
-                                       h = false;
-                               }
-                               else if (f != "d" && f != String.Empty) {
-                                       throw new FormatException (Locale.GetText (
-                                               "Argument to Guid.ToString(string format) should be \"b\", \"B\", \"d\", \"D\", \"n\", \"N\", \"p\" or \"P\""));
-                               }
-                       }
-
-                       return BaseToString (h, p, b);
+                       return ToString (ParseFormat (format));
                }
 
+               // provider value is never used
                public string ToString (string format, IFormatProvider provider)
                {
                        return ToString (format);
@@ -621,5 +704,89 @@ namespace System {
                {
                        return !( a.Equals (b) );
                }
+
+#if NET_4_0 || MOONLIGHT || MOBILE
+               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 || MOONLIGHT || MOBILE
+                       case 'X':
+                       case 'x':
+                               return Format.X;
+#endif
+                       }
+
+                       throw new FormatException (
+#if NET_4_0 || MOONLIGHT || MOBILE
+                               "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
+                               );
+               }
        }
 }