Make a copy of the old ZipLib
[mono.git] / mcs / class / corlib / System / String.cs
index 6d92bf96d80c38354604421e4c379c89efe85963..cf66fd4336c54bbf83609e49805bdd51e0a2b6bf 100644 (file)
@@ -5,20 +5,50 @@
 //   Patrik Torstensson
 //   Jeffrey Stedfast (fejj@ximian.com)
 //   Dan Lewis (dihlewis@yahoo.co.uk)
+//   Sebastien Pouliot  <sebastien@ximian.com>
 //
 // (C) 2001 Ximian, Inc.  http://www.ximian.com
+// Copyright (C) 2004-2005 Novell (http://www.novell.com)
+//
+// 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;
 using System.Text;
 using System.Collections;
 using System.Globalization;
 using System.Runtime.CompilerServices;
 
+#if NET_2_0
+using System.Runtime.ConstrainedExecution;
+using System.Runtime.InteropServices;
+#endif
+
 namespace System
 {
        [Serializable]
-       public sealed class String : IConvertible, IComparable, ICloneable, IEnumerable
+#if NET_2_0
+       [ComVisible (true)]
+       public sealed class String : IConvertible, ICloneable, IEnumerable, IComparable, IComparable<String>, IEquatable <String>
+#else
+       public sealed class String : IConvertible, ICloneable, IEnumerable, IComparable
+#endif
        {
                [NonSerialized] private int length;
                [NonSerialized] private char start_char;
@@ -77,11 +107,17 @@ namespace System
                        return !Equals (a, b);
                }
 
+#if NET_2_0
+               [ReliabilityContractAttribute (Consistency.WillNotCorruptState, Cer.MayFail)]
+#endif
                public override bool Equals (Object obj)
                {
                        return Equals (this, obj as String);
                }
 
+#if NET_2_0
+               [ReliabilityContractAttribute (Consistency.WillNotCorruptState, Cer.MayFail)]
+#endif
                public bool Equals (String value)
                {
                        return Equals (this, value);
@@ -112,11 +148,12 @@ namespace System
                        if (sourceIndex < 0 || destinationIndex < 0 || count < 0)
                                throw new ArgumentOutOfRangeException (); 
 
-                       if (sourceIndex + count > Length)
-                               throw new ArgumentOutOfRangeException ();
-
-                       if (destinationIndex + count > destination.Length)
-                               throw new ArgumentOutOfRangeException ();
+                       // re-ordered to avoid possible integer overflow
+                       if (sourceIndex > Length - count)
+                               throw new ArgumentOutOfRangeException ("sourceIndex + count > Length");
+                       // re-ordered to avoid possible integer overflow
+                       if (destinationIndex > destination.Length - count)
+                               throw new ArgumentOutOfRangeException ("destinationIndex + count > destination.Length");
 
                        InternalCopyTo (sourceIndex, destination, destinationIndex, count);
                }
@@ -128,10 +165,15 @@ namespace System
 
                public char[] ToCharArray (int startIndex, int length)
                {
-                       if (startIndex < 0 || length < 0 || startIndex + length > this.length)
-                               throw new ArgumentOutOfRangeException (); 
+                       if (startIndex < 0)
+                               throw new ArgumentOutOfRangeException ("startIndex", "< 0"); 
+                       if (length < 0)
+                               throw new ArgumentOutOfRangeException ("length", "< 0"); 
+                       // re-ordered to avoid possible integer overflow
+                       if (startIndex > this.length - length)
+                               throw new ArgumentOutOfRangeException ("startIndex + length > this.length"); 
 
-                       char [] tmp = new char[length];
+                       char[] tmp = new char [length];
 
                        InternalCopyTo (startIndex, tmp, 0, length);
 
@@ -149,7 +191,7 @@ namespace System
                                separator = WhiteChars;
 
                        if (count < 0)
-                               throw new ArgumentOutOfRangeException ();
+                               throw new ArgumentOutOfRangeException ("count");
 
                        if (count == 0) 
                                return new String[0];
@@ -160,36 +202,161 @@ namespace System
                        return InternalSplit (separator, count);
                }
 
-               public String Substring (int startIndex)
+#if NET_2_0
+               [ComVisible (false)]
+               [MonoTODO]
+               public String[] Split (char[] separator, int count, StringSplitOptions options)
+               {
+                       if (separator == null || separator.Length == 0)
+                               return Split (WhiteChars, count, options);
+
+                       if (count < 0)
+                               throw new ArgumentOutOfRangeException ("count", "Count cannot be less than zero.");
+                       if ((options != StringSplitOptions.None) && (options != StringSplitOptions.RemoveEmptyEntries))
+                               throw new ArgumentException ("options must be one of the values in the StringSplitOptions enumeration", "options");
+
+                       bool removeEmpty = (options & StringSplitOptions.RemoveEmptyEntries) == StringSplitOptions.RemoveEmptyEntries;
+
+                       if (!removeEmpty)
+                               return Split (separator, count);
+                       else
+                               throw new NotImplementedException ();
+               }
+
+               [ComVisible (false)]
+               public String[] Split (string[] separator, int count, StringSplitOptions options)
+               {
+                       if (separator == null || separator.Length == 0)
+                               return Split (WhiteChars, count, options);
+
+                       if (count < 0)
+                               throw new ArgumentOutOfRangeException ("count", "Count cannot be less than zero.");
+                       if ((options != StringSplitOptions.None) && (options != StringSplitOptions.RemoveEmptyEntries))
+                               throw new ArgumentException ("Illegal enum value: " + options + ".", "options");
+
+                       bool removeEmpty = (options & StringSplitOptions.RemoveEmptyEntries) == StringSplitOptions.RemoveEmptyEntries;
+
+                       if (count == 0 || (this == String.Empty && removeEmpty))
+                               return new String [0];
+
+                       ArrayList arr = new ArrayList ();
+
+                       int pos = 0;
+                       while (pos < this.Length) {
+                               int matchIndex = -1;
+                               int matchPos = Int32.MaxValue;
+
+                               // Find the first position where any of the separators matches
+                               for (int i = 0; i < separator.Length; ++i) {
+                                       string sep = separator [i];
+                                       if (sep == null || sep == String.Empty)
+                                               continue;
+
+                                       int match = IndexOf (sep, pos);
+                                       if (match > -1 && match < matchPos) {
+                                               matchIndex = i;
+                                               matchPos = match;
+                                       }
+                               }
+
+                               if (matchIndex == -1)
+                                       break;
+
+                               if (matchPos == pos && removeEmpty) {
+                                       pos = matchPos + separator [matchIndex].Length;
+                               }
+                               else {
+                                       arr.Add (this.Substring (pos, matchPos - pos));
+
+                                       pos = matchPos + separator [matchIndex].Length;
+
+                                       if (arr.Count == count - 1) {
+                                               break;
+                                       }
+                               }
+                       }
+
+                       if (arr.Count == 0)
+                               return new String [] { this };
+                       else {
+                               if (removeEmpty && pos == this.Length) {
+                                       String[] res = new String [arr.Count];
+                                       arr.CopyTo (0, res, 0, arr.Count);
+
+                                       return res;
+                               }
+                               else {
+                                       String[] res = new String [arr.Count + 1];
+                                       arr.CopyTo (0, res, 0, arr.Count);
+                                       res [arr.Count] = this.Substring (pos);
+
+                                       return res;
+                               }
+                       }
+               }
+
+               [ComVisible (false)]
+               public String[] Split (char[] separator, StringSplitOptions options)
+               {
+                       return Split (separator, Int32.MaxValue, options);
+               }
+
+               [ComVisible (false)]
+               public String[] Split (String[] separator, StringSplitOptions options)
+               {
+                       return Split (separator, Int32.MaxValue, options);
+               }
+#endif
+
+               public unsafe String Substring (int startIndex)
                {
                        if (startIndex < 0 || startIndex > this.length)
                                throw new ArgumentOutOfRangeException ("startIndex");
 
-                       string tmp = InternalAllocateStr (this.length - startIndex);
-                       InternalStrcpy (tmp, 0, this, startIndex, length - startIndex);
-
+                       int newlen = this.length - startIndex;
+                       string tmp = InternalAllocateStr (newlen);
+                       if (newlen != 0) {
+                               fixed (char *dest = tmp, src = this) {
+                                       memcpy ((byte*)dest, (byte*)(src + startIndex), newlen * 2);
+                               }
+                       }
                        return tmp;
                }
 
-               public String Substring (int startIndex, int length)
+               public unsafe String Substring (int startIndex, int length)
                {
-                       if (length < 0 || startIndex < 0 || startIndex + length > this.length)
-                               throw new ArgumentOutOfRangeException ();
+                       if (length < 0)
+                               throw new ArgumentOutOfRangeException ("length", "< 0");
+                       if (startIndex < 0)
+                               throw new ArgumentOutOfRangeException ("startIndex", "< 0");
+                       // re-ordered to avoid possible integer overflow
+                       if (startIndex > this.length - length)
+                               throw new ArgumentOutOfRangeException ("startIndex + length > this.length");
 
                        if (length == 0)
                                return String.Empty;
 
                        string tmp = InternalAllocateStr (length);
-                       InternalStrcpy (tmp, 0, this, startIndex, length);
+                       fixed (char *dest = tmp, src = this) {
+                               memcpy ((byte*)dest, (byte*)(src + startIndex), length * 2);
+                       }
 
                        return tmp;
                }       
 
                private static readonly char[] WhiteChars = { (char) 0x9, (char) 0xA, (char) 0xB, (char) 0xC, (char) 0xD,
+#if NET_2_0
+                       (char) 0x85, (char) 0x1680, (char) 0x2028, (char) 0x2029,
+#endif
                        (char) 0x20, (char) 0xA0, (char) 0x2000, (char) 0x2001, (char) 0x2002, (char) 0x2003, (char) 0x2004,
                        (char) 0x2005, (char) 0x2006, (char) 0x2007, (char) 0x2008, (char) 0x2009, (char) 0x200A, (char) 0x200B,
                        (char) 0x3000, (char) 0xFEFF };
 
+               public String Trim ()
+               {
+                       return InternalTrim (WhiteChars, 0);
+               }
+
                public String Trim (params char[] trimChars)
                {
                        if (trimChars == null || trimChars.Length == 0)
@@ -382,16 +549,19 @@ namespace System
 
                public bool EndsWith (String value)
                {
-                       if (value == null)
-                               throw new ArgumentNullException ("value");
-
-                       if (value == String.Empty)
-                               return true;
-
-                       if (value.length > this.length)
-                               return false;
+                       return EndsWith (value, false, CultureInfo.CurrentCulture);
+               }
 
-                       return (0 == Compare (this, length - value.length, value, 0, value.length));
+#if NET_2_0
+               public
+#else
+               internal
+#endif
+               bool EndsWith (String value, bool ignoreCase, CultureInfo culture)
+               {
+                       return (culture.CompareInfo.IsSuffix (this, value,
+                               ignoreCase ? CompareOptions.IgnoreCase :
+                               CompareOptions.None));
                }
 
                public int IndexOfAny (char [] anyOf)
@@ -406,8 +576,8 @@ namespace System
                {
                        if (anyOf == null)
                                throw new ArgumentNullException ("anyOf");
-                       if (startIndex < 0 || startIndex >= this.length)
-                               throw new ArgumentOutOfRangeException ("sourceIndex");
+                       if (startIndex < 0 || startIndex > this.length)
+                               throw new ArgumentOutOfRangeException ("startIndex");
 
                        return InternalIndexOfAny (anyOf, startIndex, this.length - startIndex);
                }
@@ -416,8 +586,13 @@ namespace System
                {
                        if (anyOf == null)
                                throw new ArgumentNullException ("anyOf");
-                       if (startIndex < 0 || count < 0 || startIndex + count > this.length)
-                               throw new ArgumentOutOfRangeException ();
+                       if (startIndex < 0)
+                               throw new ArgumentOutOfRangeException ("startIndex", "< 0");
+                       if (count < 0)
+                               throw new ArgumentOutOfRangeException ("count", "< 0");
+                       // re-ordered to avoid possible integer overflow
+                       if (startIndex > this.length - count)
+                               throw new ArgumentOutOfRangeException ("startIndex + count > this.length");
 
                        return InternalIndexOfAny (anyOf, startIndex, count);
                }
@@ -445,8 +620,13 @@ namespace System
                /* This method is culture-insensitive */
                public int IndexOf (char value, int startIndex, int count)
                {
-                       if (startIndex < 0 || count < 0 || startIndex + count > this.length)
-                               throw new ArgumentOutOfRangeException ();
+                       if (startIndex < 0)
+                               throw new ArgumentOutOfRangeException ("startIndex", "< 0");
+                       if (count < 0)
+                               throw new ArgumentOutOfRangeException ("count", "< 0");
+                       // re-ordered to avoid possible integer overflow
+                       if (startIndex > this.length - count)
+                               throw new ArgumentOutOfRangeException ("startIndex + count > this.length");
 
                        if ((startIndex == 0 && this.length == 0) || (startIndex == this.length) || (count == 0))
                                return -1;
@@ -463,9 +643,13 @@ namespace System
                {
                        if (value == null)
                                throw new ArgumentNullException ("value");
-
-                       if (startIndex < 0 || count < 0 || startIndex + count > this.length)
-                               throw new ArgumentOutOfRangeException ();
+                       if (startIndex < 0)
+                               throw new ArgumentOutOfRangeException ("startIndex", "< 0");
+                       if (count < 0)
+                               throw new ArgumentOutOfRangeException ("count", "< 0");
+                       // re-ordered to avoid possible integer overflow
+                       if (startIndex > this.length - count)
+                               throw new ArgumentOutOfRangeException ("startIndex + count > this.length");
 
                        if (value.length == 0)
                                return startIndex;
@@ -492,7 +676,7 @@ namespace System
                        if (anyOf == null) 
                                throw new ArgumentNullException ("anyOf");
 
-                       if (startIndex < 0 || startIndex > this.length)
+                       if (startIndex < 0 || startIndex >= this.length)
                                throw new ArgumentOutOfRangeException ();
 
                        if (this.length == 0)
@@ -506,8 +690,12 @@ namespace System
                        if (anyOf == null) 
                                throw new ArgumentNullException ("anyOf");
 
-                       if (startIndex < 0 || count < 0 || startIndex > this.length || startIndex - count < -1)
-                               throw new ArgumentOutOfRangeException ();
+                       if ((startIndex < 0) || (startIndex >= this.Length))
+                               throw new ArgumentOutOfRangeException ("startIndex", "< 0 || > this.Length");
+                       if ((count < 0) || (count > this.Length))
+                               throw new ArgumentOutOfRangeException ("count", "< 0 || > this.Length");
+                       if (startIndex - count + 1 < 0)
+                               throw new ArgumentOutOfRangeException ("startIndex - count + 1 < 0");
 
                        if (this.length == 0)
                                return -1;
@@ -539,7 +727,12 @@ namespace System
 
                public int LastIndexOf (String value, int startIndex)
                {
-                       return LastIndexOf (value, startIndex, startIndex + 1);
+                       if (value == null)
+                               throw new ArgumentNullException ("value");
+                       int max = startIndex;
+                       if (max < this.Length)
+                               max++;
+                       return LastIndexOf (value, startIndex, max);
                }
 
                /* This method is culture-insensitive */
@@ -548,11 +741,13 @@ namespace System
                        if (startIndex == 0 && this.length == 0)
                                return -1;
 
-                       if (startIndex < 0 || count < 0)
-                               throw new ArgumentOutOfRangeException ();
-
-                       if (startIndex >= this.length || startIndex - count + 1 < 0)
-                               throw new ArgumentOutOfRangeException ();
+                       // >= for char (> for string)
+                       if ((startIndex < 0) || (startIndex >= this.Length))
+                               throw new ArgumentOutOfRangeException ("startIndex", "< 0 || >= this.Length");
+                       if ((count < 0) || (count > this.Length))
+                               throw new ArgumentOutOfRangeException ("count", "< 0 || > this.Length");
+                       if (startIndex - count + 1 < 0)
+                               throw new ArgumentOutOfRangeException ("startIndex - count + 1 < 0");
 
                        for(int pos = startIndex; pos > startIndex - count; pos--) {
                                if (this [pos] == value)
@@ -566,8 +761,15 @@ namespace System
                {
                        if (value == null)
                                throw new ArgumentNullException ("value");
-
-                       if (value == String.Empty)
+                       // -1 > startIndex > for string (0 > startIndex >= for char)
+                       if ((startIndex < -1) || (startIndex > this.Length))
+                               throw new ArgumentOutOfRangeException ("startIndex", "< 0 || > this.Length");
+                       if ((count < 0) || (count > this.Length))
+                               throw new ArgumentOutOfRangeException ("count", "< 0 || > this.Length");
+                       if (startIndex - count + 1 < 0)
+                               throw new ArgumentOutOfRangeException ("startIndex - count + 1 < 0");
+
+                       if (value.Length == 0)
                                return 0;
 
                        if (startIndex == 0 && this.length == 0)
@@ -583,14 +785,32 @@ namespace System
                        if (count == 0)
                                return -1;
 
-                       if (startIndex < 0 || startIndex > this.length)
-                               throw new ArgumentOutOfRangeException ();
+                       if (startIndex == this.Length)
+                               startIndex--;
+                       return CultureInfo.CurrentCulture.CompareInfo.LastIndexOf (this, value, startIndex, count);
+               }
 
-                       if (count < 0 || startIndex - count + 1 < 0)
-                               throw new ArgumentOutOfRangeException ();
+#if NET_2_0
+               public bool Contains (String value)
+               {
+                       return IndexOf (value) != -1;
+               }
 
-                       return CultureInfo.CurrentCulture.CompareInfo.LastIndexOf (this, value, startIndex, count);
+               public static bool IsNullOrEmpty (String value)
+               {
+                       return (value == null) || (value.Length == 0);
+               }
+
+               public string Remove (int startIndex)
+               {
+                       if (startIndex < 0)
+                               throw new ArgumentOutOfRangeException ("startIndex", "StartIndex can not be less than zero");
+                       if (startIndex >= this.length)
+                               throw new ArgumentOutOfRangeException ("startIndex", "StartIndex must be less than the length of the string");
+
+                       return Remove (startIndex, this.length - startIndex);
                }
+#endif
 
                public String PadLeft (int totalWidth)
                {
@@ -600,7 +820,7 @@ namespace System
                public String PadLeft (int totalWidth, char paddingChar)
                {
                        if (totalWidth < 0)
-                               throw new ArgumentException ();
+                               throw new ArgumentOutOfRangeException ("totalWidth", "< 0");
 
                        if (totalWidth < this.length)
                                return String.Copy (this);
@@ -616,7 +836,7 @@ namespace System
                public String PadRight (int totalWidth, char paddingChar)
                {
                        if (totalWidth < 0)
-                               throw new ArgumentException ();
+                               throw new ArgumentOutOfRangeException ("totalWidth", "< 0");
 
                        if (totalWidth < this.length)
                                return String.Copy (this);
@@ -626,16 +846,19 @@ namespace System
 
                public bool StartsWith (String value)
                {
-                       if (value == null)
-                               throw new ArgumentNullException ("value");
-                       
-                       if (value == String.Empty)
-                               return true;
-
-                       if (this.length < value.length)
-                               return false;
+                       return StartsWith (value, false, CultureInfo.CurrentCulture);
+               }
 
-                       return (0 == Compare (this, 0, value, 0 , value.length));
+#if NET_2_0
+               public
+#else
+               internal
+#endif
+               bool StartsWith (String value, bool ignoreCase, CultureInfo culture)
+               {
+                       return (culture.CompareInfo.IsPrefix (this, value,
+                               ignoreCase ? CompareOptions.IgnoreCase :
+                               CompareOptions.None));
                }
 
                /* This method is culture insensitive */
@@ -650,15 +873,11 @@ namespace System
                        if (oldValue == null)
                                throw new ArgumentNullException ("oldValue");
 
-                       if (oldValue == String.Empty)
+                       if (oldValue.Length == 0)
                                throw new ArgumentException ("oldValue is the empty string.");
 
-                       if (this == String.Empty)
+                       if (this.Length == 0)
                                return this;
-
-                       if (oldValue.Length == 0 || oldValue[0] == '\0') {
-                               return(this);
-                       }
                        
                        if (newValue == null)
                                newValue = String.Empty;
@@ -666,47 +885,110 @@ namespace System
                        return InternalReplace (oldValue, newValue, CultureInfo.CurrentCulture.CompareInfo);
                }
 
-               public String Remove (int startIndex, int count)
+               public unsafe String Remove (int startIndex, int count)
                {
-                       if (startIndex < 0 || count < 0 || startIndex + count > this.length)
-                               throw new ArgumentOutOfRangeException ();
-
-                       return InternalRemove (startIndex, count);
+                       if (startIndex < 0)
+                               throw new ArgumentOutOfRangeException ("startIndex", "< 0");
+                       if (count < 0)
+                               throw new ArgumentOutOfRangeException ("count", "< 0");
+                       // re-ordered to avoid possible integer overflow
+                       if (startIndex > this.length - count)
+                               throw new ArgumentOutOfRangeException ("startIndex + count > this.length");
+
+                       String tmp = InternalAllocateStr (this.length - count);
+
+                       fixed (char *dest = tmp, src = this) {
+                               char *dst = dest;
+                               memcpy ((byte*)dst, (byte*)src, startIndex * 2);
+                               int skip = startIndex + count;
+                               dst += startIndex;
+                               memcpy ((byte*)dst, (byte*)(src + skip), (length - skip) * 2);
+                       }
+                       return tmp;
                }
 
                public String ToLower ()
                {
-                       return InternalToLower (CultureInfo.CurrentCulture);
+                       return ToLower (CultureInfo.CurrentCulture);
                }
 
                public String ToLower (CultureInfo culture)
                {
-                       return InternalToLower (culture);
+                       if (culture == null)
+                               throw new ArgumentNullException ("culture");
+
+                       if (culture.LCID == 0x007F) { // Invariant
+                               return ToLowerInvariant ();
+                       }
+                       return culture.TextInfo.ToLower (this);
+               }
+
+#if NET_2_0
+               public unsafe String ToLowerInvariant ()
+#else
+               internal unsafe String ToLowerInvariant ()
+#endif
+               {
+                       string tmp = InternalAllocateStr (length);
+                       fixed (char* source = &start_char, dest = tmp) {
+
+                               char* destPtr = (char*)dest;
+                               char* sourcePtr = (char*)source;
+
+                               for (int n = 0; n < length; n++) {
+                                       *destPtr = Char.ToLowerInvariant (*sourcePtr);
+                                       sourcePtr++;
+                                       destPtr++;
+                               }
+                       }
+                       return tmp;
                }
 
                public String ToUpper ()
                {
-                       return InternalToUpper (CultureInfo.CurrentCulture);
+                       return ToUpper (CultureInfo.CurrentCulture);
                }
 
                public String ToUpper (CultureInfo culture)
                {
-                       return InternalToUpper (culture);
+                       if (culture == null)
+                               throw new ArgumentNullException ("culture");
+
+                       if (culture.LCID == 0x007F) { // Invariant
+                               return ToUpperInvariant ();
+                       }
+                       return culture.TextInfo.ToUpper (this);
                }
 
-               public override String ToString ()
+#if NET_2_0
+               public unsafe String ToUpperInvariant ()
+#else
+               internal unsafe String ToUpperInvariant ()
+#endif
                {
-                       return this;
+                       string tmp = InternalAllocateStr (length);
+                       fixed (char* source = &start_char, dest = tmp) {
+
+                               char* destPtr = (char*)dest;
+                               char* sourcePtr = (char*)source;
+
+                               for (int n = 0; n < length; n++) {
+                                       *destPtr = Char.ToUpperInvariant (*sourcePtr);
+                                       sourcePtr++;
+                                       destPtr++;
+                               }
+                       }
+                       return tmp;
                }
 
-               public String ToString (IFormatProvider provider)
+               public override String ToString ()
                {
                        return this;
                }
 
-               public String Trim ()
+               public String ToString (IFormatProvider provider)
                {
-                       return Trim (null);
+                       return this;
                }
 
                public static String Format (String format, Object arg0)
@@ -781,14 +1063,15 @@ namespace System
                                        // pad formatted string and append to result
 
                                        if (width > str.length) {
-                                               string pad = new String (' ', width - str.length);
+                                               const char padchar = ' ';
+                                               int padlen = width - str.length;
 
                                                if (left_align) {
                                                        result.Append (str);
-                                                       result.Append (pad);
+                                                       result.Append (padchar, padlen);
                                                }
                                                else {
-                                                       result.Append (pad);
+                                                       result.Append (padchar, padlen);
                                                        result.Append (str);
                                                }
                                        }
@@ -807,10 +1090,10 @@ namespace System
                        }
 
                        if (start < format.length)
-                               result.Append (format.Substring (start));
+                               result.Append (format, start, format.Length - start);
                }
 
-               public static String Copy (String str)
+               public unsafe static String Copy (String str)
                {
                        if (str == null)
                                throw new ArgumentNullException ("str");
@@ -818,7 +1101,11 @@ namespace System
                        int length = str.length;
 
                        String tmp = InternalAllocateStr (length);
-                       InternalStrcpy (tmp, 0, str);
+                       if (length != 0) {
+                               fixed (char *dest = tmp, src = str) {
+                                       memcpy ((byte*)dest, (byte*)src, length * 2);
+                               }
+                       }
                        return tmp;
                }
 
@@ -830,7 +1117,7 @@ namespace System
                        return obj.ToString ();
                }
 
-               public static String Concat (Object obj1, Object obj2)
+               public unsafe static String Concat (Object obj1, Object obj2)
                {
                        string s1, s2;
 
@@ -846,8 +1133,16 @@ namespace System
                                return s1;
 
                        String tmp = InternalAllocateStr (s1.Length + s2.Length);
-                       InternalStrcpy (tmp, 0, s1);
-                       InternalStrcpy (tmp, s1.length, s2);
+                       if (s1.Length != 0) {
+                               fixed (char *dest = tmp, src = s1) {
+                                       memcpy ((byte*)dest, (byte*)src, s1.length * 2);
+                               }
+                       }
+                       if (s2.Length != 0) {
+                               fixed (char *dest = tmp, src = s2) {
+                                       memcpy ((byte*)(dest + s1.Length), (byte*)src, s2.length * 2);
+                               }
+                       }
 
                        return tmp;
                }
@@ -873,7 +1168,10 @@ namespace System
                        return Concat (s1, s2, s3);
                }
 
-               public static String Concat (Object obj1, Object obj2, Object obj3, Object obj4)
+#if ! BOOTSTRAP_WITH_OLDLIB
+               [CLSCompliant(false)]
+               public static String Concat (Object obj1, Object obj2, Object obj3,
+                                            Object obj4, __arglist)
                {
                        string s1, s2, s3, s4;
 
@@ -892,16 +1190,25 @@ namespace System
                        else
                                s3 = obj3.ToString ();
 
-                       if (obj4 == null)
-                               s4 = String.Empty;
-                       else
-                               s4 = obj4.ToString ();
+                       ArgIterator iter = new ArgIterator (__arglist);
+                       int argCount = iter.GetRemainingCount();
 
-                       return Concat (s1, s2, s3, s4);
-                       
+                       StringBuilder sb = new StringBuilder ();
+                       if (obj4 != null)
+                               sb.Append (obj4.ToString ());
+
+                       for (int i = 0; i < argCount; i++) {
+                               TypedReference typedRef = iter.GetNextArg ();
+                               sb.Append (TypedReference.ToObject (typedRef));
+                       }
+
+                       s4 = sb.ToString ();
+
+                       return Concat (s1, s2, s3, s4);                 
                }
+#endif
 
-               public static String Concat (String s1, String s2)
+               public unsafe static String Concat (String s1, String s2)
                {
                        if (s1 == null) {
                                if (s2 == null)
@@ -914,13 +1221,21 @@ namespace System
 
                        String tmp = InternalAllocateStr (s1.length + s2.length);
 
-                       InternalStrcpy (tmp, 0, s1);
-                       InternalStrcpy (tmp, s1.length, s2);
+                       if (s1.Length != 0) {
+                               fixed (char *dest = tmp, src = s1) {
+                                       memcpy ((byte*)dest, (byte*)src, s1.length * 2);
+                               }
+                       }
+                       if (s2.Length != 0) {
+                               fixed (char *dest = tmp, src = s2) {
+                                       memcpy ((byte*)(dest + s1.Length), (byte*)src, s2.length * 2);
+                               }
+                       }
 
                        return tmp;
                }
 
-               public static String Concat (String s1, String s2, String s3)
+               public unsafe static String Concat (String s1, String s2, String s3)
                {
                        if (s1 == null){
                                if (s2 == null){
@@ -947,14 +1262,26 @@ namespace System
                        //return InternalConcat (s1, s2, s3);
                        String tmp = InternalAllocateStr (s1.length + s2.length + s3.length);
 
-                       InternalStrcpy (tmp, 0, s1);
-                       InternalStrcpy (tmp, s1.length, s2);
-                       InternalStrcpy (tmp, s1.length + s2.length, s3);
+                       if (s1.Length != 0) {
+                               fixed (char *dest = tmp, src = s1) {
+                                       memcpy ((byte*)dest, (byte*)src, s1.length * 2);
+                               }
+                       }
+                       if (s2.Length != 0) {
+                               fixed (char *dest = tmp, src = s2) {
+                                       memcpy ((byte*)(dest + s1.Length), (byte*)src, s2.length * 2);
+                               }
+                       }
+                       if (s3.Length != 0) {
+                               fixed (char *dest = tmp, src = s3) {
+                                       memcpy ((byte*)(dest + s1.Length + s2.Length), (byte*)src, s3.length * 2);
+                               }
+                       }
 
                        return tmp;
                }
 
-               public static String Concat (String s1, String s2, String s3, String s4)
+               public unsafe static String Concat (String s1, String s2, String s3, String s4)
                {
                        if (s1 == null && s2 == null && s3 == null && s4 == null)
                                return String.Empty;
@@ -970,78 +1297,67 @@ namespace System
 
                        String tmp = InternalAllocateStr (s1.length + s2.length + s3.length + s4.length);
 
-                       InternalStrcpy (tmp, 0, s1);
-                       InternalStrcpy (tmp, s1.length, s2);
-                       InternalStrcpy (tmp, s1.length + s2.length, s3);
-                       InternalStrcpy (tmp, s1.length + s2.length + s3.length, s4);
+                       if (s1.Length != 0) {
+                               fixed (char *dest = tmp, src = s1) {
+                                       memcpy ((byte*)dest, (byte*)src, s1.length * 2);
+                               }
+                       }
+                       if (s2.Length != 0) {
+                               fixed (char *dest = tmp, src = s2) {
+                                       memcpy ((byte*)(dest + s1.Length), (byte*)src, s2.length * 2);
+                               }
+                       }
+                       if (s3.Length != 0) {
+                               fixed (char *dest = tmp, src = s3) {
+                                       memcpy ((byte*)(dest + s1.Length + s2.Length), (byte*)src, s3.length * 2);
+                               }
+                       }
+                       if (s4.Length != 0) {
+                               fixed (char *dest = tmp, src = s4) {
+                                       memcpy ((byte*)(dest + s1.Length + s2.Length + s3.Length), (byte*)src, s4.length * 2);
+                               }
+                       }
 
                        return tmp;
                }
 
                public static String Concat (params Object[] args)
                {
-                       string [] strings;
-                       int len, i, currentpos;
-
                        if (args == null)
                                throw new ArgumentNullException ("args");
 
-                       strings = new string [args.Length];
-                       len = 0;
+                       int i = args.Length;
+                       if (i == 0)
+                               return String.Empty;
+
+                       string [] strings = new string [i];
                        i = 0;
+                       int len = 0;
                        foreach (object arg in args) {
-                               /* use Empty for each null argument */
-                               if (arg == null)
+                               if (arg == null) {
                                        strings[i] = String.Empty;
-                               else
+                               } else {
                                        strings[i] = arg.ToString ();
-                               len += strings[i].length;
+                                       len += strings[i].length;
+                               }
                                i++;
                        }
 
                        if (len == 0)
                                return String.Empty;
 
-                       currentpos = 0;
-
-                       String tmp = InternalAllocateStr (len);
-                       for (i = 0; i < strings.Length; i++) {
-                               InternalStrcpy (tmp, currentpos, strings[i]);
-                               currentpos += strings[i].length;
-                       }
-
-                       return tmp;
+                       return InternalJoin (String.Empty, strings, 0, strings.Length);
                }
 
                public static String Concat (params String[] values)
                {
-                       int len, i, currentpos;
-
                        if (values == null)
                                throw new ArgumentNullException ("values");
 
-                       len = 0;
-                       foreach (string value in values)
-                               len += value != null ? value.length : 0;
-
-                       if (len == 0)
-                               return String.Empty;
-
-                       currentpos = 0;
-
-                       String tmp = InternalAllocateStr (len);
-                       for (i = 0; i < values.Length; i++) {
-                               if (values[i] == null)
-                                       continue;
-
-                               InternalStrcpy (tmp, currentpos, values[i]);
-                               currentpos += values[i].length;
-                       }       
-
-                       return tmp;
+                       return InternalJoin (String.Empty, values, 0, values.Length);
                }
 
-               public String Insert (int startIndex, String value)
+               public unsafe String Insert (int startIndex, String value)
                {
                        if (value == null)
                                throw new ArgumentNullException ("value");
@@ -1049,7 +1365,21 @@ namespace System
                        if (startIndex < 0 || startIndex > this.length)
                                throw new ArgumentOutOfRangeException ();
 
-                       return InternalInsert (startIndex, value);
+                       if (value.Length == 0)
+                               return this;
+                       if (this.Length == 0)
+                               return value;
+                       String tmp = InternalAllocateStr (this.length + value.length);
+
+                       fixed (char *dest = tmp, src = this, val = value) {
+                               char *dst = dest;
+                               memcpy ((byte*)dst, (byte*)src, startIndex * 2);
+                               dst += startIndex;
+                               memcpy ((byte*)dst, (byte*)val, value.length * 2);
+                               dst += value.length;
+                               memcpy ((byte*)dst, (byte*)(src + startIndex), (length - startIndex) * 2);
+                       }
+                       return tmp;
                }
 
 
@@ -1081,12 +1411,18 @@ namespace System
                {
                        if (value == null)
                                throw new ArgumentNullException ("value");
-
-                       if (startIndex + count > value.Length)
-                               throw new ArgumentOutOfRangeException ();
+                       if (startIndex < 0)
+                               throw new ArgumentOutOfRangeException ("startIndex", "< 0");
+                       if (count < 0)
+                               throw new ArgumentOutOfRangeException ("count", "< 0");
+                       // re-ordered to avoid possible integer overflow
+                       if (startIndex > value.Length - count)
+                               throw new ArgumentOutOfRangeException ("startIndex + count > value.length");
 
                        if (startIndex == value.Length)
                                return String.Empty;
+                       if (separator == null)
+                               separator = String.Empty;
 
                        return InternalJoin (separator, value, startIndex, count);
                }
@@ -1136,7 +1472,6 @@ namespace System
                        return Convert.ToInt64 (this, provider);
                }
        
-               [CLSCompliant (false)]
                sbyte IConvertible.ToSByte (IFormatProvider provider)
                {
                        return Convert.ToSByte (this, provider);
@@ -1157,19 +1492,16 @@ namespace System
                        return Convert.ToType (this, conversionType,  provider);
                }
 
-               [CLSCompliant (false)]
                ushort IConvertible.ToUInt16 (IFormatProvider provider)
                {
                        return Convert.ToUInt16 (this, provider);
                }
 
-               [CLSCompliant (false)]
                uint IConvertible.ToUInt32 (IFormatProvider provider)
                {
                        return Convert.ToUInt32 (this, provider);
                }
 
-               [CLSCompliant (false)]
                ulong IConvertible.ToUInt64 (IFormatProvider provider)
                {
                        return Convert.ToUInt64 (this, provider);
@@ -1215,9 +1547,10 @@ namespace System
 
                                if (str[ptr] == ',') {
                                        // White space between ',' and number or sign.
-                                       int start = ++ptr;
+                                       ++ptr;
                                        while (Char.IsWhiteSpace (str [ptr]))
                                                ++ptr;
+                                       int start = ptr;
 
                                        format = str.Substring (start, ptr - start);
 
@@ -1299,6 +1632,181 @@ namespace System
                        }
                }
 
+#if NET_2_0
+               [ReliabilityContractAttribute (Consistency.WillNotCorruptState, Cer.MayFail)]
+#endif
+               public unsafe override int GetHashCode ()
+               {
+                       fixed (char * c = this) {
+                               char * cc = c;
+                               char * end = cc + length - 1;
+                               int h = 0;
+                               for (;cc < end; cc += 2) {
+                                       h = (h << 5) - h + *cc;
+                                       h = (h << 5) - h + cc [1];
+                               }
+                               ++end;
+                               if (cc < end)
+                                       h = (h << 5) - h + *cc;
+                               return h;
+                       }
+               }
+
+               /* helpers used by the runtime as well as above or eslewhere in corlib */
+               internal static unsafe void memset (byte *dest, int val, int len)
+               {
+                       if (len < 8) {
+                               while (len != 0) {
+                                       *dest = (byte)val;
+                                       ++dest;
+                                       --len;
+                               }
+                               return;
+                       }
+                       if (val != 0) {
+                               val = val | (val << 8);
+                               val = val | (val << 16);
+                       }
+                       // align to 4
+                       int rest = (int)dest & 3;
+                       if (rest != 0) {
+                               rest = 4 - rest;
+                               len -= rest;
+                               do {
+                                       *dest = (byte)val;
+                                       ++dest;
+                                       --rest;
+                               } while (rest != 0);
+                       }
+                       while (len >= 16) {
+                               ((int*)dest) [0] = val;
+                               ((int*)dest) [1] = val;
+                               ((int*)dest) [2] = val;
+                               ((int*)dest) [3] = val;
+                               dest += 16;
+                               len -= 16;
+                       }
+                       while (len >= 4) {
+                               ((int*)dest) [0] = val;
+                               dest += 4;
+                               len -= 4;
+                       }
+                       // tail bytes
+                       while (len > 0) {
+                               *dest = (byte)val;
+                               dest++;
+                               len--;
+                       }
+               }
+
+               internal static unsafe void memcpy4 (byte *dest, byte *src, int size) {
+                       /*while (size >= 32) {
+                               // using long is better than int and slower than double
+                               // FIXME: enable this only on correct alignment or on platforms
+                               // that can tolerate unaligned reads/writes of doubles
+                               ((double*)dest) [0] = ((double*)src) [0];
+                               ((double*)dest) [1] = ((double*)src) [1];
+                               ((double*)dest) [2] = ((double*)src) [2];
+                               ((double*)dest) [3] = ((double*)src) [3];
+                               dest += 32;
+                               src += 32;
+                               size -= 32;
+                       }*/
+                       while (size >= 16) {
+                               ((int*)dest) [0] = ((int*)src) [0];
+                               ((int*)dest) [1] = ((int*)src) [1];
+                               ((int*)dest) [2] = ((int*)src) [2];
+                               ((int*)dest) [3] = ((int*)src) [3];
+                               dest += 16;
+                               src += 16;
+                               size -= 16;
+                       }
+                       while (size >= 4) {
+                               ((int*)dest) [0] = ((int*)src) [0];
+                               dest += 4;
+                               src += 4;
+                               size -= 4;
+                       }
+                       while (size > 0) {
+                               ((byte*)dest) [0] = ((byte*)src) [0];
+                               dest += 1;
+                               src += 1;
+                               --size;
+                       }
+               }
+               static unsafe void memcpy2 (byte *dest, byte *src, int size) {
+                       while (size >= 8) {
+                               ((short*)dest) [0] = ((short*)src) [0];
+                               ((short*)dest) [1] = ((short*)src) [1];
+                               ((short*)dest) [2] = ((short*)src) [2];
+                               ((short*)dest) [3] = ((short*)src) [3];
+                               dest += 8;
+                               src += 8;
+                               size -= 8;
+                       }
+                       while (size >= 2) {
+                               ((short*)dest) [0] = ((short*)src) [0];
+                               dest += 2;
+                               src += 2;
+                               size -= 2;
+                       }
+                       if (size > 0)
+                               ((byte*)dest) [0] = ((byte*)src) [0];
+               }
+               static unsafe void memcpy1 (byte *dest, byte *src, int size) {
+                       while (size >= 8) {
+                               ((byte*)dest) [0] = ((byte*)src) [0];
+                               ((byte*)dest) [1] = ((byte*)src) [1];
+                               ((byte*)dest) [2] = ((byte*)src) [2];
+                               ((byte*)dest) [3] = ((byte*)src) [3];
+                               ((byte*)dest) [4] = ((byte*)src) [4];
+                               ((byte*)dest) [5] = ((byte*)src) [5];
+                               ((byte*)dest) [6] = ((byte*)src) [6];
+                               ((byte*)dest) [7] = ((byte*)src) [7];
+                               dest += 8;
+                               src += 8;
+                               size -= 8;
+                       }
+                       while (size >= 2) {
+                               ((byte*)dest) [0] = ((byte*)src) [0];
+                               ((byte*)dest) [1] = ((byte*)src) [1];
+                               dest += 2;
+                               src += 2;
+                               size -= 2;
+                       }
+                       if (size > 0)
+                               ((byte*)dest) [0] = ((byte*)src) [0];
+               }
+               static unsafe void memcpy (byte *dest, byte *src, int size) {
+                       // FIXME: if pointers are not aligned, try to align them
+                       // so a faster routine can be used. Handle the case where
+                       // the pointers can't be reduced to have the same alignment
+                       // (just ignore the issue on x86?)
+                       if ((((int)dest | (int)src) & 3) != 0) {
+                               if (((int)dest & 1) != 0 && ((int)src & 1) != 0 && size >= 1) {
+                                       dest [0] = src [0];
+                                       ++dest;
+                                       ++src;
+                                       --size;
+                               }
+                               if (((int)dest & 2) != 0 && ((int)src & 2) != 0 && size >= 2) {
+                                       ((short*)dest) [0] = ((short*)src) [0];
+                                       dest += 2;
+                                       src += 2;
+                                       size -= 2;
+                               }
+                               if ((((int)dest | (int)src) & 1) != 0) {
+                                       memcpy1 (dest, src, size);
+                                       return;
+                               }
+                               if ((((int)dest | (int)src) & 2) != 0) {
+                                       memcpy2 (dest, src, size);
+                                       return;
+                               }
+                       }
+                       memcpy4 (dest, src, size);
+               }
+
                [CLSCompliant (false), MethodImplAttribute (MethodImplOptions.InternalCall)]
                unsafe public extern String (char *value);
 
@@ -1323,24 +1831,15 @@ namespace System
                [MethodImplAttribute (MethodImplOptions.InternalCall)]
                public extern String (char c, int count);
 
-               [MethodImplAttribute (MethodImplOptions.InternalCall)]
-               public extern override int GetHashCode ();
-
                [MethodImplAttribute (MethodImplOptions.InternalCall)]
                private extern static string InternalJoin (string separator, string[] value, int sIndex, int count);
 
-               [MethodImplAttribute (MethodImplOptions.InternalCall)]
-               private extern String InternalInsert (int sourceIndex, String value);
-
                [MethodImplAttribute (MethodImplOptions.InternalCall)]
                private extern String InternalReplace (char oldChar, char newChar);
 
                [MethodImplAttribute (MethodImplOptions.InternalCall)]
                private extern String InternalReplace (String oldValue, string newValue, CompareInfo comp);
 
-               [MethodImplAttribute (MethodImplOptions.InternalCall)]
-               private extern String InternalRemove (int sIndex, int count);
-
                [MethodImplAttribute (MethodImplOptions.InternalCall)]
                private extern void InternalCopyTo (int sIndex, char[] dest, int destIndex, int count);
 
@@ -1360,19 +1859,19 @@ namespace System
                private extern String InternalPad (int width, char chr, bool right);
 
                [MethodImplAttribute (MethodImplOptions.InternalCall)]
-               private extern String InternalToLower (CultureInfo culture);
+               internal extern static String InternalAllocateStr (int length);
 
                [MethodImplAttribute (MethodImplOptions.InternalCall)]
-               private extern String InternalToUpper (CultureInfo culture);
+               internal extern static void InternalStrcpy (String dest, int destPos, String src);
 
                [MethodImplAttribute (MethodImplOptions.InternalCall)]
-               internal extern static String InternalAllocateStr (int length);
+               internal extern static void InternalStrcpy (String dest, int destPos, char[] chars);
 
                [MethodImplAttribute (MethodImplOptions.InternalCall)]
-               internal extern static void InternalStrcpy (String dest, int destPos, String src);
+               internal extern static void InternalStrcpy (String dest, int destPos, String src, int sPos, int count);
 
                [MethodImplAttribute (MethodImplOptions.InternalCall)]
-               internal extern static void InternalStrcpy (String dest, int destPos, String src, int sPos, int count);
+               internal extern static void InternalStrcpy (String dest, int destPos, char[] chars, int sPos, int count);
 
                [MethodImplAttribute (MethodImplOptions.InternalCall)]
                private extern static string InternalIntern (string str);