// ==++==
//
//   Copyright (c) Microsoft Corporation.  All rights reserved.
//
// ==--==
//============================================================
//
// Class:  String
//
//===========================================================  
namespace System
{
    using System.Text;
    using System;
    using System.Diagnostics;
    using System.Globalization;
    using System.Threading;
    using System.Collections;
    using System.Runtime.CompilerServices;
    using System.Runtime.InteropServices;
    using Microsoft.Bartok.Runtime;

    //
    // The String class represents a static string of characters.  Many of
    // the String methods perform some type of transformation on the current
    // instance and return the result as a new String. All comparison methods are
    // implemented as a part of String.  As with arrays, character positions
    // (indices) are zero-based.
    //
    //| <include path='docs/doc[@for="String"]/*' />
    public sealed class String : IComparable, ICloneable, IEnumerable {

        [RequiredByBartok]
        private int m_arrayLength;
        [RequiredByBartok]
        private int m_stringLength;
        [RequiredByBartok]
        internal char[] m_chars;

        //private static readonly char FmtMsgMarkerChar='%';
        //private static readonly char FmtMsgFmtCodeChar='!';
        //These are defined in Com99/src/vm/COMStringCommon.h and must be kept in sync.
        private const int TrimHead = 0;
        private const int TrimTail = 1;
        private const int TrimBoth = 2;

        // The Empty constant holds the empty string value.
        //We need to call the String constructor so that the compiler doesn't mark this as a literal.
        //Marking this as a literal would mean that it doesn't show up as a field which we can access
        //from native.
        //| <include path='docs/doc[@for="String.Empty"]/*' />
        public static readonly String Empty = "";

        //
        // Constructors
        //

        // Creates a new string from the characters in a subarray.  The new string will
        // be created from the characters in value between startIndex and
        // startIndex + length - 1.
        //
        //| <include path='docs/doc[@for="String.String7"]/*' />
        public String(char [] value, int startIndex, int length): this(length)
        {
            Buffer.MoveMemory(m_chars, value, 0, startIndex, length);
        }

        // Creates a new string from the characters in a subarray.  The new string will be
        // created from the characters in value.
        //
        //| <include path='docs/doc[@for="String.String5"]/*' />
        public String(char [] value): this(value, 0, value.Length) {}

        //| <include path='docs/doc[@for="String.String6"]/*' />
        public String(char c, int count): this(count)
        {
            for (int i = 0; i < count; i++) {
                m_chars[i] = c;
            }
        }

        private String(int length)
        {
            m_chars = new char[length + 1];
            m_arrayLength = length + 1;
            m_stringLength = length;
        }

        /// This is an EE implemented function so that the JIT can recognise it and
        /// treat it specially by eliminating checks on character fetches.
        [NoHeapAllocation]
        private int InternalLength() {
            // See also Lightning\Src\VM\COMString.cpp::Length
            // See also Lightning\Src\VM\object.h::GetStringLength
            return this.m_stringLength;
        }

        // Joins an array of strings together as one string with a separator between each original string.
        //
        //| <include path='docs/doc[@for="String.Join"]/*' />
        public static String Join(String separator, String[] value) {
            if (value == null) {
                throw new ArgumentNullException("value");
            }
            return Join(separator, value, 0, value.Length);
        }

        ///
        // Joins an array of strings together as one string with a separator
        // between each original string.
        //
        // @param separator the string used as a separator.
        // @param value the array of strings to be joined.
        // @param startIndex the position within the array to start using
        //  strings.
        // @param count the number of strings to take from the array.
        //
        // @return a string consisting of the strings contained in <i>value</i>
        //  from <EM>startIndex</EM> to <EM>startIndex</EM> + <EM>count</EM>
        //  joined together to form a single string, with each of the original
        //  strings separated by <i>separator</i>.
        // 
        //| <include path='docs/doc[@for="String.Join1"]/*' />
        public static String Join(String separator, String[] value,
                                  int startIndex, int count)
        {
            // See also Lightning\Src\VM\COMString.cpp::JoinArray
            if (separator == null) {
                separator = String.Empty;
            }
            if (value == null) {
                throw new ArgumentNullException("value array is null");
            }
            if (startIndex < 0) {
                throw new ArgumentOutOfRangeException("startIndex is negative");
            }
            if (count < 0) {
                throw new ArgumentOutOfRangeException("count is negative");
            }
            if (startIndex + count > value.Length) {
                throw new ArgumentOutOfRangeException("startIndex+count>value.Length");
            }
            // Special case the empty list of strings
            if (count == 0) {
                return String.Empty;
            }
            // Compute the length of the new string
            int newLength = 0;
            int limit = startIndex + count;
            for (int i = startIndex; i < limit; i++) {
                String s = value[i];
                if (s != null) {
                    newLength += s.m_stringLength;
                }
            }
            newLength += (count - 1) * separator.m_stringLength;
            // Create a new String
            String result = FastAllocateString(newLength);
            if (newLength == 0) {
                return result;
            }
            // Fill in the string
            int dstIndex = 0;
            String firstString = value[startIndex];
            if (firstString != null) {
                FillString(result, 0, firstString);
                dstIndex = firstString.m_stringLength;
            }
            for (int i = startIndex + 1; i < limit; i++) {
                FillString(result, dstIndex, separator);
                dstIndex += separator.m_stringLength;
                String elementString = value[i];
                if (elementString != null) {
                    FillString(result, dstIndex, elementString);
                    dstIndex += elementString.m_stringLength;
                }
            }
            return result;
        }

        private static bool CaseInsensitiveCompHelper(
            char[] strAChars,
            char[] strBChars,
            int aStart,
            int bStart,
            int aLength,
            int bLength,
            out int result) {

            char charA;
            char charB;

            result = 0;

            // setup the pointers so that we can always increment them.
            // We never access these pointers at the negative offset.
            int a = aStart - 1;
            int b = bStart - 1;

            do {
                a++; b++;

                charA = strAChars[a];
                charB = strBChars[b];

                //Case-insensitive comparison on chars greater than 0x80 requires
                //a locale-aware casing operation and we're not going there.
                if (charA >= 0x80 || charB >= 0x80) {
                    // TODO: We should be able to fix this.
                    return false;
                }

                // Do the right thing if they differ in case only.
                // We depend on the fact that the uppercase and lowercase letters
                // in the range which we care about (A-Z,a-z) differ only by the
                // 0x20 bit.
                // The check below takes the xor of the two characters and
                // determines if this bit is only set on one of them.
                // If they're different cases, we know that we need to execute
                // only one of the conditions within block.
                if (((charA^charB) & 0x20) != 0) {
                    if (charA >='A' && charA <='Z') {
                        charA |= (char)0x20;
                    }
                    else if (charB >='A' && charB <='Z') {
                        charB |= (char)0x20;
                    }
                }
            } while (charA == charB && charA != 0);

            // Return the (case-insensitive) difference between them.
            if (charA != charB) {
                result = (int)(charA-charB);
                return true;
            }

            // The length of b was unknown because it was just a pointer to a
            // null-terminated string.
            // If we get here, we know that both A and B are pointing at a null.
            // However, A can have an embedded null.  Check the number of
            // characters that we've walked in A against the expected length.
            if (bLength == -1) {
                if (a != aLength) {
                    result = 1;
                    return true;
                }
                result=0;
                return true;
            }

            result = (aLength - bLength);
            return true;
        }

        internal static int nativeCompareOrdinal(String strA, String strB,
                                                 bool bIgnoreCase)
        {
            // See also Lightning\Src\VM\COMString.cpp::FCCompareOrdinal
            int strALength = strA.Length;
            int strBLength = strB.Length;
            char[] strAChars = strA.m_chars;
            char[] strBChars = strB.m_chars;

                    // Handle the comparison where we wish to ignore case.
                    if (bIgnoreCase) {
                        int result;
                        if (CaseInsensitiveCompHelper(strAChars, strBChars,
                                                      0, 0,
                                                      strALength, strBLength,
                                                      out result)) {
                            return result;
                        }
                        else {
                            // This will happen if we have characters greater
                            // than 0x7F.
                            throw new ArgumentException();
                        }
                    }

                    // If the strings are the same length, compare exactly the
                    // right # of chars.  If they are different, compare the
                    // shortest # + 1 (the '\0').
                    int count = strALength;
                    if (count > strBLength)
                        count = strBLength;

                    int i = 0;
                    while ((count -= 1) >= 0) {
                        int diff = (int)strAChars[i] - (int)strBChars[i];
                        if (diff != 0) {
                            return diff;
                        }
                        i++;
                    }

                    return strALength - strBLength;
        }

        internal static int nativeCompareOrdinalEx(String strA, int indexA,
                                                   String strB, int indexB,
                                                   int count)
        {
            throw new Exception("System.String.nativeCompareOrdinalEx not implemented in Bartok!");
        }

        //
        // Search/Query methods
        //

        // Determines whether two strings match.
        //| <include path='docs/doc[@for="String.Equals"]/*' />
        public override bool Equals(Object obj) {
            if (obj is String) {
                return this.Equals((String) obj);
            }
            else {
                return false;
            }
        }

        // Determines whether two strings match.
        //| <include path='docs/doc[@for="String.Equals1"]/*' />
        public bool Equals(String value) {
            if (value == null) {
                return false;
            }
            if (this.m_stringLength != value.m_stringLength) {
                return false;
            }
            char[] thisCharPtr = this.m_chars;
                char[] valueCharPtr = value.m_chars;
                    int cursor = 0;
                    for (int i = this.m_stringLength; i > 0; i--) {
                        if (thisCharPtr[cursor] != valueCharPtr[cursor]) {
                            return false;
                        }
                        cursor++;
                    }
            return true;
        }

        // Determines whether two Strings match.
        //| <include path='docs/doc[@for="String.Equals2"]/*' />
        public static bool Equals(String a, String b) {
            if ((Object)a ==(Object)b) {
                return true;
            }

            if ((Object)a == null || (Object)b == null) {
                return false;
            }

            return a.Equals(b);
        }

        //| <include path='docs/doc[@for="String.operatorEQ"]/*' />
        public static bool operator == (String a, String b)
        {
            return String.Equals(a, b);
        }

        //| <include path='docs/doc[@for="String.operatorNE"]/*' />
        public static bool operator != (String a, String b)
        {
            return !String.Equals(a, b);
        }

        internal char InternalGetChar(int index)
        {
            if ((uint) index >= (uint) this.m_stringLength) {
                throw new IndexOutOfRangeException();
            }
            return this.m_chars[index];
        }

        // Gets the character at a specified position.
        //
        //| <include path='docs/doc[@for="String.this"]/*' />
        public char this[int index] {
            get { return InternalGetChar(index); }
        }

        // Converts a substring of this string to an array of characters.  Copies the
        // characters of this string beginning at position startIndex and ending at
        // startIndex + length - 1 to the character array buffer, beginning
        // at bufferStartIndex.
        //
        //| <include path='docs/doc[@for="String.CopyTo"]/*' />
        public void CopyTo(int sourceIndex, char[] destination, int destinationIndex, int count)
        {
            if (destination == null)
                throw new ArgumentNullException("destination");
            if (count < 0)
                throw new ArgumentOutOfRangeException("count", "ArgumentOutOfRange_NegativeCount");
            if (sourceIndex < 0)
                throw new ArgumentOutOfRangeException("sourceIndex", "ArgumentOutOfRange_Index");
            if (count > Length - sourceIndex)
                throw new ArgumentOutOfRangeException("sourceIndex", "ArgumentOutOfRange_IndexCount");
            if (destinationIndex > destination.Length - count || destinationIndex < 0)
                throw new ArgumentOutOfRangeException("destinationIndex", "ArgumentOutOfRange_IndexCount");
            InternalCopyTo(sourceIndex, destination, destinationIndex, count);
        }

        internal        void InternalCopyTo(int sourceIndex, char[] destination,
                                            int destinationIndex, int count) {
            if (sourceIndex + count > this.m_stringLength) {
                throw new ArgumentOutOfRangeException();
            }
            Buffer.MoveMemory(destination, this.m_chars, destinationIndex, sourceIndex,
                              count);
        }

        internal void CopyToByteArray(int sourceIndex, byte[] destination,
                                      int destinationIndex, int charCount)
        {
            // Called by System.Text.UnicodeEncoding.GetBytes() for little-endian Unicode.

            if (destination == null)
                throw new ArgumentNullException("destination");
            if (charCount < 0)
                throw new ArgumentOutOfRangeException("charCount");
            if (destinationIndex < 0)
                throw new ArgumentOutOfRangeException("destinationIndex");
            if (sourceIndex < 0)
                throw new ArgumentOutOfRangeException("sourceIndex");
            if (sourceIndex + charCount > this.m_stringLength)
                throw new ArgumentOutOfRangeException("charCount");

            int byteCount = charCount * 2;
            if (destinationIndex + byteCount > destination.Length)
                throw new ArgumentOutOfRangeException("destinationLength");

            if (charCount > 0) {
                Buffer.InternalBlockCopy(this.m_chars, sourceIndex * 2,
                    destination, destinationIndex, byteCount);
            }
        }

        // Returns the entire string as an array of characters.
        //| <include path='docs/doc[@for="String.ToCharArray"]/*' />
        public char[] ToCharArray() {
            return ToCharArray(0,Length);
        }

        // Returns a substring of this string as an array of characters.
        //
        //| <include path='docs/doc[@for="String.ToCharArray1"]/*' />
        public char[] ToCharArray(int startIndex, int length)
        {
            // Range check everything.
            if (startIndex < 0 || startIndex > Length || startIndex > Length - length)
                throw new ArgumentOutOfRangeException("startIndex", "ArgumentOutOfRange_Index");
            if (length < 0)
                throw new ArgumentOutOfRangeException("length", "ArgumentOutOfRange_Index");

            char[] chars = new char[length];
            InternalCopyTo(startIndex, chars, 0, length);
            return chars;
        }

        // Gets a hash code for this string.  If strings A and B are such that A.Equals(B), then
        // they will return the same hash code.
        //| <include path='docs/doc[@for="String.GetHashCode"]/*' />
        public override int GetHashCode() {
            char[] charPtrFixed = this.m_chars;
                int charPtr = 0;
                if (charPtrFixed[this.m_stringLength] != 0) {
                    throw new Exception("Bartok string is not null terminated");
                }
                uint hash = 5381;
                char c = charPtrFixed[charPtr];
                while (c != 0) {
                    hash = ((hash << 5) + hash) ^ c;
                    charPtr++;
                    c = charPtrFixed[charPtr];
                }
                return (int) hash;
        }

        // Gets the length of this string
        //| <include path='docs/doc[@for="String.Length"]/*' />
        public int Length {
            [NoHeapAllocation]
            get { return InternalLength(); }
        }

        ///<internalonly/>
        internal int ArrayLength {
            [NoHeapAllocation]
            get { return (m_arrayLength); }
        }

        // Used by StringBuilder
        internal int Capacity {
            [NoHeapAllocation]
            get { return (m_arrayLength - 1); }
        }

        // Creates an array of strings by splitting this string at each
        // occurrence of a separator.  The separator is searched for, and if found,
        // the substring preceding the occurrence is stored as the first element in
        // the array of strings.  We then continue in this manner by searching
        // the substring that follows the occurrence.  On the other hand, if the separator
        // is not found, the array of strings will contain this instance as its only element.
        // If the separator is null
        // whitespace (i.e., Character.IsWhitespace) is used as the separator.
        //
        //| <include path='docs/doc[@for="String.Split"]/*' />
        public String [] Split(params char [] separator) {
            return Split(separator, Int32.MaxValue);
        }

        //==============================MakeSeparatorList========================
        //Args: baseString -- the string to parse for the given list of
        //                    separator chars.
        //      Separator  -- A string containing all of the split characters.
        //      list       -- a pointer to a caller-allocated array of ints for
        //                    split char indices.
        //      listLength -- the number of slots allocated in list.
        //Returns: A list of all of the places within baseString where instances
        //         of characters in Separator occur.
        //Exceptions: None.
        //N.B.:  This just returns silently if the caller hasn't allocated
        //       enough space for the int list.
        //=======================================================================  
        private static int MakeSeparatorList(String baseString, char[] Separator, int[] list, int listLength) {
            int i;
            int foundCount=0;
            char[] thisChars = baseString.m_chars;
                int thisLength = baseString.Length;

                if (Separator == null || Separator.Length == 0) {
                    //If they passed null or an empty string,
                    //look for whitespace.
                    for (i = 0; i < thisLength && foundCount < listLength; i++) {
                        if (Char.IsWhiteSpace(thisChars[i])) {
                            list[foundCount++]=i;
                        }
                    }
                }
                else {
                    int searchLength = Separator.Length;
                    //If they passed in a string of chars,
                    //actually look for those chars.
                    for (i = 0; i < thisLength && foundCount < listLength; i++) {
                        if (ArrayContains(thisChars[i],Separator) >= 0) {
                            list[foundCount++]=i;
                        }
                    }
                }
                return foundCount;
        }

        // Creates an array of strings by splitting this string at each
        // occurrence of a separator.  The separator is searched for, and if found,
        // the substring preceding the occurrence is stored as the first element in
        // the array of strings.  We then continue in this manner by searching
        // the substring that follows the occurrence.  On the other hand, if the separator
        // is not found, the array of strings will contain this instance as its only element.
        // If the separator is the empty string (i.e., String.Empty), then
        // whitespace (i.e., Character.IsWhitespace) is used as the separator.
        // If there are more than count different strings, the last n-(count-1)
        // elements are concatenated and added as the last String.
        //
        //| <include path='docs/doc[@for="String.Split1"]/*' />
        //| <include path='docs/doc[@for="String.Split1"]/*' />
        public String[] Split(char[] separator, int count) {
            int numReplaces;
            int numActualReplaces;
            int[] sepList;
            int currIndex=0;
            int arrIndex=0;
            //char *thisChars;
            int thisLength;
            int i;
            String[] splitStrings;
            String temp;

            if (count < 0) {
                throw new ArgumentOutOfRangeException
                    ("count", "ArgumentOutOfRange_Negative");
            }

            //Allocate space and fill an array of ints with a list of everyplace
            //within our String that a separator character occurs.
            sepList = new int[this.Length];
            numReplaces = MakeSeparatorList(this, separator, sepList, this.Length);
            //Handle the special case of no replaces.
            if (0 == numReplaces) {
                splitStrings = new String[1];
                splitStrings[0] = this;
                return splitStrings;
            }
            thisLength = Length;

            count--;
            numActualReplaces = (numReplaces<count) ? numReplaces : count;

            //Allocate space for the new array.
            //+1 for the string from the end of the last replace to the end
            //of the String.
            splitStrings = new String[numActualReplaces+1];

            for (i = 0; i < numActualReplaces && currIndex < thisLength; i++) {
                temp = this.Substring(currIndex, sepList[i]-currIndex);

                splitStrings[arrIndex++] = temp;
                currIndex=sepList[i]+1;
            }

            //Handle the last string at the end of the array if there is one.

            if (currIndex < thisLength) {
                temp = this.Substring(currIndex, thisLength-currIndex);
                splitStrings[arrIndex] = temp;
            }
            else if (arrIndex == numActualReplaces) {
                //We had a separator character at the end of a string.  Rather than just allowing
                //a null character, we'll replace the last element in the array with an empty string.
                temp = Empty;
                splitStrings[arrIndex] = temp;
            }

            return splitStrings;
        }

        // Returns a substring of this string.
        //
        //| <include path='docs/doc[@for="String.Substring"]/*' />
        public String Substring(int startIndex) {
            return this.Substring (startIndex, Length-startIndex);
        }

        // Returns a substring of this string.
        //
        //| <include path='docs/doc[@for="String.Substring1"]/*' />
        public String Substring(int startIndex, int length) {

            int thisLength = Length;

            //Bounds Checking.
            if (startIndex < 0) {
                throw new ArgumentOutOfRangeException("startIndex", "ArgumentOutOfRange_StartIndex");
            }

            if (length < 0) {
                throw new ArgumentOutOfRangeException("length", "ArgumentOutOfRange_NegativeLength");
            }

            if (startIndex > thisLength - length) {
                throw new ArgumentOutOfRangeException("length", "ArgumentOutOfRange_IndexLength");
            }

            String s = FastAllocateString(length);
            FillSubstring(s, 0, this, startIndex, length);

            return s;
        }

        internal String TrimHelper(char[] trimChars, int trimType)
        {
            // See also Lighting\Src\VM\COMString.cpp::TrimHelper
            int stringLength = this.m_stringLength;
            int iLeft = 0;
            int iRight = stringLength - 1;
            this.TrimHelper(trimChars, trimType, ref iLeft, ref iRight);
            int newLength = iRight - iLeft + 1;
            if (newLength == stringLength) {
                return this;
            }
            else if (newLength == 0) {
                return String.Empty;
            }
            else {
                String result = FastAllocateString(newLength);
                FillSubstring(result, 0, this, iLeft, newLength);
                return result;
            }
        }

        private        void TrimHelper(char[] trimChars, int trimType,
                                       ref int iLeft, ref int iRight)
        {
            char[] charPtr = this.m_chars;
                if (trimType == String.TrimHead ||
                    trimType == String.TrimBoth) {
                    while (iLeft <= iRight &&
                           ArrayContains(charPtr[iLeft], trimChars) >= 0) {
                        iLeft++;
                    }
                }
                if (trimType == String.TrimTail ||
                    trimType == String.TrimBoth) {
                    while (iRight >= iLeft &&
                           ArrayContains(charPtr[iRight], trimChars) >= 0) {
                        iRight--;
                    }
                }
        }

        private static int ArrayContains(char c, char[] charArray) {
            int limit = charArray.Length;
            for (int i = 0; i < limit; i++) {
                if (charArray[i] == c) {
                    return i;
                }
            }
            return -1;
        }

        ///
        // Creates a new string from the characters in a subarray.  The new
        // string will be created from the characters in <i>value</i> between
        // <i>startIndex</i> and <i>startIndex</i> + <i>length</i> - 1.
        //
        // @param value an array of characters.
        // @param startIndex the index at which the subarray begins.
        // @param length the length of the subarray.
        //
        // @exception ArgumentException if value is <b>null</b>.
        // @exception ArgumentException if <i>startIndex</i> or
        //  <i>startIndex</i>+<i>length</i>-1 are not valid indices of
        //  <i>value</i>.
        // 
        //| <include path='docs/doc[@for="String.String7"]/*' />

        public static String StringCTOR(char[] value, int startIndex,
                                        int length) {
            // See also Lightning\Src\VM\COMString.cpp::StringInitCharArray
            if (value == null) {
                throw new ArgumentNullException("array value is null");
            }
            if (startIndex < 0) {
                throw new ArgumentOutOfRangeException("startIndex is negative");
            }
            if (length < 0) {
                throw new ArgumentOutOfRangeException("length is negative");
            }
            if (length == 0) {
                return String.Empty;
            }
            else {
                String result = FastAllocateString(length);
                FillStringArray(result, 0, value, startIndex, length);
                return result;
            }
        }

        ///
        // Creates a new string from the characters in a subarray.  The new
        // string will be created from the characters in <i>value</i>.
        //
        // @param value an array of characters.
        //
        // @exception ArgumentException if value is <b>null</b>.
        // 
        //| <include path='docs/doc[@for="String.String5"]/*' />
        public static String StringCTOR(char[] value) {
            // See also Lightning\Src\VM\COMString.cpp::StringInitChars
            if (value == null) {
                return String.Empty;
            }
            int length = value.Length;
            if (length == 0) {
                return String.Empty;
            }
            String result = FastAllocateString(length);
            FillStringArray(result, 0, value, 0, length);
            return result;
        }

        internal static String NewString(String curr,int start,int copyLen,
                                         int capacity) {
            String result = FastAllocateString(capacity);
            FillSubstring(result, 0, curr, start, copyLen);
            return result;
        }

        //| <include path='docs/doc[@for="String.String6"]/*' />
        public static String StringCTOR(char c, int count) {
            // See also Lightning\Src\VM\COMString.cpp::StringInitCharCount
            if (count < 0) {
                throw new ArgumentOutOfRangeException("count is negative");
            }
            if (count == 0) {
                return String.Empty;
            }
            else {
                String result = FastAllocateString(count);
                FillStringChar(result, 0, c, count);
                return result;
            }
        }

        // Removes a string of characters from the ends of this string.
        //| <include path='docs/doc[@for="String.Trim"]/*' />
        public String Trim(params char[] trimChars) {
            if (null == trimChars || trimChars.Length == 0) {
                trimChars=CharacterInfo.WhitespaceChars;
            }
            return TrimHelper(trimChars,TrimBoth);
        }

        // Removes a string of characters from the beginning of this string.
        //| <include path='docs/doc[@for="String.TrimStart"]/*' />
        public String TrimStart(params char[] trimChars) {
            if (null == trimChars || trimChars.Length == 0) {
                trimChars=CharacterInfo.WhitespaceChars;
            }
            return TrimHelper(trimChars,TrimHead);
        }


        // Removes a string of characters from the end of this string.
        //| <include path='docs/doc[@for="String.TrimEnd"]/*' />
        public String TrimEnd(params char[] trimChars) {
            if (null == trimChars || trimChars.Length == 0) {
                trimChars=CharacterInfo.WhitespaceChars;
            }
            return TrimHelper(trimChars,TrimTail);
        }

        // For ASCIIEncoding::GetString()
        static internal String CreateStringFromASCII(byte[] bytes, int startIndex, int length) {
            Debug.Assert(bytes != null, "need a byte[].");
            Debug.Assert(startIndex >= 0 && (startIndex < bytes.Length || bytes.Length == 0), "startIndex >= 0 && startIndex < bytes.Length");
            Debug.Assert(length >= 0 && length <= bytes.Length - startIndex, "length >= 0 && length <= bytes.Length - startIndex");
            if (length == 0)
                return String.Empty;
            String s = FastAllocateString(length);
            char[] pChars = s.m_chars;
                for (int i = 0; i < length; i++)
                    pChars[i] = (char) (bytes[i+startIndex] & 0x7f);
            return s;
        }

        // For Latin1Encoding::GetString()
        static internal String CreateStringFromLatin1(byte[] bytes, int startIndex, int length) {
            Debug.Assert(bytes != null, "need a byte[].");
            Debug.Assert(startIndex >= 0 && (startIndex < bytes.Length || bytes.Length == 0), "startIndex >= 0 && startIndex < bytes.Length");
            Debug.Assert(length >= 0 && length <= bytes.Length - startIndex, "length >= 0 && length <= bytes.Length - startIndex");
            if (length == 0)
                return String.Empty;
            String s = FastAllocateString(length);
            char[] pChars = s.m_chars;
                for (int i = 0; i < length; i++)
                    pChars[i] = (char) (bytes[i+startIndex]);
            return s;
        }

        private static String FastAllocateString(int stringLength) {
            return new String(stringLength);
        }

        [Inline]
        internal void InitializeStringLength(int stringLength) {
            this.m_arrayLength = stringLength+1;
            this.m_stringLength = stringLength;
        }

        private        static void FillString(String dest, int destPos,
                                              String src) {
            char[] srcPtr = src.m_chars;
                FillStringCharPtr(dest, destPos, srcPtr, 0, src.m_stringLength);
        }

        private        static void FillStringChecked(String dest, int destPos,
                                                     String src) {
            if (!(src.Length <= dest.ArrayLength - destPos)) {
                throw new IndexOutOfRangeException(); // REVIEW: param?
            }
            char[] srcPtr = src.m_chars;
                FillStringCharPtr(dest, destPos, srcPtr, 0, src.m_stringLength);
        }

        private        static void FillStringEx(String dest, int destPos,
                                                String src,int srcLength) {
            // ASSERT(srcLength <= dest.ArrayLength - destPos);
            char[] srcPtr = src.m_chars;
                FillStringCharPtr(dest, destPos, srcPtr, 0, srcLength);
        }

        private        static void FillStringChar(String dest, int destPos,
                                                  char c, int count) {
            char[] destPtr = dest.m_chars;
                int charPtr = destPos;
                while (count >= 0) {
                    destPtr[charPtr] = c;
                    charPtr++;
                    count--;
                }
        }

        private        static void FillStringCharPtr(String dest, int destPos,
                                                     char[] srcPtr, int srcPos, int count) {
            char[] charPtr = dest.m_chars;
            Buffer.MoveMemory(charPtr, srcPtr, destPos, srcPos, count);
        }

        private        static void FillStringArray(String dest, int stringStart,
                                                   char[] array, int charStart,
                                                   int count) {
            FillStringCharPtr(dest, stringStart, array, charStart, count);
        }

        private        static void FillSubstring(String dest, int destPos,
                                                 String src, int startPos,
                                                 int count) {
            char[] srcPtr = src.m_chars;
            FillStringCharPtr(dest, destPos, srcPtr, startPos, count);
        }

        //
        //
        // INSTANCE METHODS
        //
        //

        // Provides a culture-correct string comparison. StrA is compared to StrB
        // to determine whether it is lexicographically less, equal, or greater, and then returns
        // either a negative integer, 0, or a positive integer; respectively.
        //
        //| <include path='docs/doc[@for="String.Compare"]/*' />
        public static int Compare(String strA, String strB) {
            return CompareInfo.Compare(strA, strB, CompareOptions.None);
        }

        // Provides a culture-correct string comparison. strA is compared to strB
        // to determine whether it is lexicographically less, equal, or greater, and then a
        // negative integer, 0, or a positive integer is returned; respectively.
        // The case-sensitive option is set by ignoreCase
        //
        //| <include path='docs/doc[@for="String.Compare1"]/*' />
        public static int Compare(String strA, String strB, bool ignoreCase) {
            if (ignoreCase) {
                return CompareInfo.Compare(strA, strB, CompareOptions.IgnoreCase);
            }
            return CompareInfo.Compare(strA, strB, CompareOptions.None);
        }

        // [Bartok]:
        public static int Compare(String strA, String strB, bool ignoreCase,
                                  CultureInfo info)
        {
            return Compare(strA, strB, ignoreCase);
        }

        // Determines whether two string regions match.  The substring of strA beginning
        // at indexA of length count is compared with the substring of strB
        // beginning at indexB of the same length.
        //
        //| <include path='docs/doc[@for="String.Compare3"]/*' />
        public static int Compare(String strA, int indexA, String strB, int indexB, int length) {
            int lengthA = length;
            int lengthB = length;

            if (strA != null) {
                if (strA.Length - indexA < lengthA) {
                    lengthA = (strA.Length - indexA);
                }
            }

            if (strB != null) {
                if (strB.Length - indexB < lengthB) {
                    lengthB = (strB.Length - indexB);
                }
            }
            return CompareInfo.Compare(strA, indexA, lengthA, strB, indexB, lengthB, CompareOptions.None);
        }


        // Determines whether two string regions match.  The substring of strA beginning
        // at indexA of length count is compared with the substring of strB
        // beginning at indexB of the same length.  Case sensitivity is determined by the ignoreCase boolean.
        //
        //| <include path='docs/doc[@for="String.Compare4"]/*' />
        public static int Compare(String strA, int indexA, String strB, int indexB, int length, bool ignoreCase) {
            int lengthA = length;
            int lengthB = length;

            if (strA != null) {
                if (strA.Length - indexA < lengthA) {
                    lengthA = (strA.Length - indexA);
                }
            }

            if (strB != null) {
                if (strB.Length - indexB < lengthB) {
                    lengthB = (strB.Length - indexB);
                }
            }

            if (ignoreCase) {
                return CompareInfo.Compare(strA, indexA, lengthA, strB, indexB, lengthB, CompareOptions.IgnoreCase);
            }
            return CompareInfo.Compare(strA, indexA, lengthA, strB, indexB, lengthB, CompareOptions.None);
        }

        // Compares this object to another object, returning an integer that
        // indicates the relationship. This method returns a value less than 0 if this is less than value, 0
        // if this is equal to value, or a value greater than 0
        // if this is greater than value.  Strings are considered to be
        // greater than all non-String objects.  Note that this means sorted
        // arrays would contain nulls, other objects, then Strings in that order.
        //
        //| <include path='docs/doc[@for="String.CompareTo"]/*' />
        public int CompareTo(Object value) {
            if (value == null) {
                return 1;
            }

            if (!(value is String)) {
                throw new ArgumentException("Arg_MustBeString");
            }

            return String.Compare(this,(String)value);
        }

        // Determines the sorting relation of StrB to the current instance.
        //
        //| <include path='docs/doc[@for="String.CompareTo1"]/*' />
        public int CompareTo(String strB) {
            if (strB == null) {
                return 1;
            }
            return CompareInfo.Compare(this, strB, 0);
        }

        // Compares strA and strB using an ordinal (code-point) comparison.
        //
        //| <include path='docs/doc[@for="String.CompareOrdinal"]/*' />
        public static int CompareOrdinal(String strA, String strB) {
            if (strA == null || strB == null) {
                if ((Object)strA ==(Object)strB) { //they're both null;
                    return 0;
                }
                return (strA==null)? -1 : 1; //-1 if A is null, 1 if B is null.
            }

            return nativeCompareOrdinal(strA, strB, false);
        }

        // Compares strA and strB using an ordinal (code-point) comparison.
        //
        //| <include path='docs/doc[@for="String.CompareOrdinal"]/*' />
        public static int CompareOrdinal(String strA, String strB, bool ignoreCase) {
            if (strA == null || strB == null) {
                if ((Object)strA ==(Object)strB) { //they're both null;
                    return 0;
                }
                return (strA==null)? -1 : 1; //-1 if A is null, 1 if B is null.
            }

            return nativeCompareOrdinal(strA, strB, ignoreCase);
        }

        // Compares strA and strB using an ordinal (code-point) comparison.
        //
        //| <include path='docs/doc[@for="String.CompareOrdinal1"]/*' />
        public static int CompareOrdinal(String strA, int indexA, String strB, int indexB, int length) {
            if (strA == null || strB == null) {
                if ((Object)strA ==(Object)strB) { //they're both null;
                    return 0;
                }

                return (strA==null)? -1 : 1; //-1 if A is null, 1 if B is null.
            }

            return nativeCompareOrdinalEx(strA, indexA, strB, indexB, length);
        }


        // Determines whether a specified string is a suffix of the the current instance.
        //
        // The case-sensitive and culture-sensitive option is set by options,
        // and the default culture is used.
        //
        //| <include path='docs/doc[@for="String.EndsWith"]/*' />
        public bool EndsWith(String value) {
            if (null == value) {
                throw new ArgumentNullException("value");
            }
            int valueLen = value.Length;
            int thisLen = this.Length;
            if (valueLen > thisLen) {
                return false;
            }
            return (0==Compare(this, thisLen-valueLen, value, 0, valueLen));
        }

        public bool EndsWith(char value) {
            int thisLen = this.Length;
            if (thisLen != 0) {
                if (this[thisLen - 1] == value)
                    return true;
            }
            return false;
        }


        // Returns the index of the first occurrence of value in the current instance.
        // The search starts at startIndex and runs thorough the next count characters.
        //
        //| <include path='docs/doc[@for="String.IndexOf"]/*' />
        public int IndexOf(char value) {
            return IndexOf(value, 0, this.Length);
        }

        //| <include path='docs/doc[@for="String.IndexOf1"]/*' />
        public int IndexOf(char value, int startIndex) {
            return IndexOf(value, startIndex, this.Length - startIndex);
        }

        //| <include path='docs/doc[@for="String.IndexOf2"]/*' />
        public int IndexOf(char value, int startIndex, int count) {
            if (startIndex < 0 || startIndex > this.m_stringLength) {
                throw new ArgumentOutOfRangeException("startIndex out of range");
            }
            if (count < 0 || startIndex + count > this.m_stringLength) {
                throw new ArgumentOutOfRangeException("count out of range");
            }
            char[] charPtr = this.m_chars;
                int cursor = startIndex;
                for (int i = count; i > 0; i--) {
                    if (charPtr[cursor] == value) {
                        return (startIndex + (count - i));
                    }
                    cursor++;
                }
            return -1;
        }

        // Returns the index of the first occurrence of any character in value in the current instance.
        // The search starts at startIndex and runs to endIndex-1. [startIndex,endIndex).
        //

        //| <include path='docs/doc[@for="String.IndexOfAny1"]/*' />
        public int IndexOfAny(char [] anyOf) {
            return IndexOfAny(anyOf,0, this.Length);
        }

        //| <include path='docs/doc[@for="String.IndexOfAny2"]/*' />
        public int IndexOfAny(char [] anyOf, int startIndex) {
            return IndexOfAny(anyOf, startIndex, this.Length - startIndex);
        }

        //| <include path='docs/doc[@for="String.IndexOfAny3"]/*' />
        public int IndexOfAny(char [] anyOf, int startIndex, int count) {
            if (anyOf == null) {
                throw new ArgumentNullException("anyOf array is null");
            }
            if (startIndex < 0 || startIndex > this.m_stringLength) {
                throw new ArgumentOutOfRangeException("startIndex out of range");
            }
            if (count < 0 || startIndex + count > this.m_stringLength) {
                throw new ArgumentOutOfRangeException("count out of range");
            }
            char[] charPtr = this.m_chars;
                int cursor = startIndex;
                for (int i = count; i > 0; i--) {
                    if (ArrayContains(charPtr[cursor], anyOf) >= 0) {
                        return (startIndex + (count - i));
                    }
                    cursor++;
                }
            return -1;
        }

        // Determines the position within this string of the first occurrence of the specified
        // string, according to the specified search criteria.  The search begins at
        // the first character of this string, it is case-sensitive and culture-sensitive,
        // and the default culture is used.
        //
        //| <include path='docs/doc[@for="String.IndexOf6"]/*' />
        public int IndexOf(String value) {
            return CompareInfo.IndexOf(this,value);
        }

        // Determines the position within this string of the first occurrence of the specified
        // string, according to the specified search criteria.  The search begins at
        // startIndex, it is case-sensitive and culture-sensitve, and the default culture is used.
        //
        //| <include path='docs/doc[@for="String.IndexOf7"]/*' />
        public int IndexOf(String value, int startIndex){
            return CompareInfo.IndexOf(this,value,startIndex);
        }

        // Determines the position within this string of the first occurrence of the specified
        // string, according to the specified search criteria.  The search begins at
        // startIndex, ends at endIndex and the default culture is used.
        //
        //| <include path='docs/doc[@for="String.IndexOf8"]/*' />
        public int IndexOf(String value, int startIndex, int count){
            if (startIndex + count > this.Length) {
                throw new ArgumentOutOfRangeException("count", "ArgumentOutOfRange_Index");
            }
            return CompareInfo.IndexOf(this, value, startIndex, count, CompareOptions.None);
        }


        // Returns the index of the last occurrence of value in the current instance.
        // The search starts at startIndex and runs to endIndex. [startIndex,endIndex].
        // The character at position startIndex is included in the search.  startIndex is the larger
        // index within the string.
        //
        //| <include path='docs/doc[@for="String.LastIndexOf"]/*' />
        public int LastIndexOf(char value) {
            return LastIndexOf(value, this.Length-1, this.Length);
        }

        //| <include path='docs/doc[@for="String.LastIndexOf1"]/*' />
        public int LastIndexOf(char value, int startIndex){
            return LastIndexOf(value,startIndex,startIndex + 1);
        }

        //| <include path='docs/doc[@for="String.LastIndexOf2"]/*' />
        public int LastIndexOf(char value, int startIndex, int count) {
            if (this.m_stringLength == 0) {
                return -1;
            }
            if (startIndex < 0 || startIndex >= this.m_stringLength) {
                throw new ArgumentOutOfRangeException("startIndex out of range");
            }
            if (count < 0 || count - 1 > startIndex) {
                throw new ArgumentOutOfRangeException("count out of range");
            }
            char[] charPtr = this.m_chars;
                int cursor = startIndex;
                for (int i = count; i > 0; i--) {
                    if (charPtr[cursor] == value) {
                        return (startIndex - (count - i));
                    }
                    cursor--;
                }
            return -1;
        }

        // Returns the index of the last occurrence of any character in value in the current instance.
        // The search starts at startIndex and runs to endIndex. [startIndex,endIndex].
        // The character at position startIndex is included in the search.  startIndex is the larger
        // index within the string.
        //

        //| <include path='docs/doc[@for="String.LastIndexOfAny1"]/*' />
        public int LastIndexOfAny(char [] anyOf) {
            return LastIndexOfAny(anyOf,this.Length-1,this.Length);
        }

        //| <include path='docs/doc[@for="String.LastIndexOfAny2"]/*' />
        public int LastIndexOfAny(char [] anyOf, int startIndex) {
            return LastIndexOfAny(anyOf,startIndex,startIndex + 1);
        }

        //| <include path='docs/doc[@for="String.LastIndexOfAny3"]/*' />
        public        int LastIndexOfAny(char [] anyOf, int startIndex,
                                         int count) {
            if (anyOf == null) {
                throw new ArgumentNullException("anyOf array is null");
            }
            if (this.m_stringLength == 0) {
                return -1;
            }
            if (startIndex < 0 || startIndex >= this.m_stringLength) {
                throw new ArgumentOutOfRangeException("startIndex out of range");
            }
            if (count < 0 || count - 1 > startIndex) {
                throw new ArgumentOutOfRangeException("count out of range");
            }
            char[] charPtr = this.m_chars;
                int cursor = startIndex;
                for (int i = count; i > 0; i--) {
                    if (ArrayContains(charPtr[cursor], anyOf) >= 0) {
                        return (startIndex - (count - 1));
                    }
                    cursor--;
                }
            return -1;
        }

        // Returns the index of the last occurrence of any character in value in the current instance.
        // The search starts at startIndex and runs to endIndex. [startIndex,endIndex].
        // The character at position startIndex is included in the search.  startIndex is the larger
        // index within the string.
        //
        //| <include path='docs/doc[@for="String.LastIndexOf6"]/*' />
        public int LastIndexOf(String value) {
            return LastIndexOf(value, this.Length-1,this.Length);
        }

        //| <include path='docs/doc[@for="String.LastIndexOf7"]/*' />
        public int LastIndexOf(String value, int startIndex) {
            return LastIndexOf(value, startIndex, startIndex + 1);
        }

        //| <include path='docs/doc[@for="String.LastIndexOf8"]/*' />
        public int LastIndexOf(String value, int startIndex, int count) {
            if (count < 0) {
                throw new ArgumentOutOfRangeException("count", "ArgumentOutOfRange_Count");
            }
            return CompareInfo.LastIndexOf(this, value, startIndex, count, CompareOptions.None);
        }


        //
        //
        //| <include path='docs/doc[@for="String.PadLeft"]/*' />
        public String PadLeft(int totalWidth) {
            return PadHelper(totalWidth, ' ', false);
        }

        //| <include path='docs/doc[@for="String.PadLeft1"]/*' />
        public String PadLeft(int totalWidth, char paddingChar) {
            return PadHelper(totalWidth, paddingChar, false);
        }

        //| <include path='docs/doc[@for="String.PadRight"]/*' />
        public String PadRight(int totalWidth) {
            return PadHelper(totalWidth, ' ', true);
        }

        //| <include path='docs/doc[@for="String.PadRight1"]/*' />
        public String PadRight(int totalWidth, char paddingChar) {
            return PadHelper(totalWidth, paddingChar, true);
        }

        private String PadHelper(int totalWidth, char paddingChar,
                                 bool isRightPadded) {
            // See also Lightning\Src\VM\COMString.cpp::PadHelper
            if (totalWidth < 0) {
                throw new ArgumentOutOfRangeException("totalWidth is negative");
            }
            if (totalWidth <= this.m_stringLength) {
                return this;
            }
            int padCount = totalWidth - this.m_stringLength;
            String result = FastAllocateString(totalWidth);
            if (isRightPadded) {
                FillString(result, 0, this);
                FillStringChar(result, this.m_stringLength,
                               paddingChar, padCount);
            }
            else {

                FillStringChar(result, 0, paddingChar, padCount);
                FillString(result, padCount, this);
            }
            return result;
        }

        // Determines whether a specified string is a prefix of the current instance
        //
        //| <include path='docs/doc[@for="String.StartsWith"]/*' />
        public bool StartsWith(String value) {
            if (null == value) {
                throw new ArgumentNullException("value");
            }
            if (this.Length < value.Length) {
                return false;
            }
            return (0==Compare(this,0, value,0, value.Length));
        }

        // Creates a copy of this string in lower case.
        //| <include path='docs/doc[@for="String.ToLower"]/*' />
        public String ToLower() {
            return TextInfo.ToLower(this);
        }

        // Creates a copy of this string in upper case.
        //| <include path='docs/doc[@for="String.ToUpper"]/*' />
        public String ToUpper() {
            return TextInfo.ToUpper(this);
        }

        // Returns this string.
        //| <include path='docs/doc[@for="String.ToString"]/*' />
        public override String ToString() {
            return this;
        }

        // Method required for the ICloneable interface.
        // There's no point in cloning a string since they're immutable, so we simply return this.
        //| <include path='docs/doc[@for="String.Clone"]/*' />
        public Object Clone() {
            return this;
        }


        // Trims the whitespace from both ends of the string.  Whitespace is defined by
        // CharacterInfo.WhitespaceChars.
        //
        //| <include path='docs/doc[@for="String.Trim1"]/*' />
        public String Trim() {
            return this.Trim(CharacterInfo.WhitespaceChars);
        }

        //
        //
        //| <include path='docs/doc[@for="String.Insert"]/*' />
        public String Insert(int startIndex, String value) {
            // See also Lightning\Src\VM\COMString.cpp::Insert
            if (startIndex < 0 || startIndex > this.m_stringLength) {
                throw new ArgumentOutOfRangeException("startIndex out of range");
            }
            if (value == null) {
                throw new ArgumentNullException("value string is null");
            }
            int newLength = this.m_stringLength + value.m_stringLength;
            String result = FastAllocateString(newLength);
            FillSubstring(result, 0, this, 0, startIndex);
            FillSubstring(result, startIndex, value, 0, value.m_stringLength);
            FillSubstring(result, startIndex + value.m_stringLength,
                          this, startIndex, this.m_stringLength - startIndex);
            return result;
        }

        internal void InsertHoleInString(int startIndex, int holeSize) {
            int tail = this.m_stringLength - startIndex;
            this.m_stringLength += holeSize;
            if (tail > 0) {
                char[] charPtr = this.m_chars;
                    Buffer.MoveMemory(charPtr, charPtr, holeSize, startIndex,
                                      tail);
            }
        }

        internal void InsertStringOverwrite(String value, int startIndex, int valueLength) {
            char[] charPtr = this.m_chars;
                char[] srcPtr = value.m_chars;
                    Buffer.MoveMemory(charPtr, srcPtr, startIndex, 0,
                                      valueLength);
        }

        // Replaces all instances of oldChar with newChar.
        //
        //| <include path='docs/doc[@for="String.Replace"]/*' />
        public String Replace(char oldChar, char newChar) {
            String result = FastAllocateString(this.m_stringLength);
            this.Replace(oldChar, newChar, result);
            return result;
        }

        private void Replace(char oldChar, char newChar, String newString) {
            char[] srcPtr = this.m_chars;
                char[] destPtr = newString.m_chars;
                    int srcCursor = 0;
                    int destCursor = 0;
                    for (int i = this.m_stringLength; i > 0; i--) {
                        char c = srcPtr[srcCursor];
                        destPtr[destCursor] = (c == oldChar) ? newChar : c;
                        srcCursor++;
                        destCursor++;
                    }
        }

        // This method contains the same functionality as StringBuilder Replace. The only difference is that
        // a new String has to be allocated since Strings are immutable
        //| <include path='docs/doc[@for="String.Replace1"]/*' />
        public String Replace(String oldValue, String newValue) {
            // See also Lightning\Src\VM\COMString.cpp::ReplaceString
            if (oldValue == null) {
                throw new ArgumentNullException("oldString is null");
            }
            if (newValue == null) {
                newValue = String.Empty;
            }
            if (oldValue.m_stringLength == 0) {
                throw new ArgumentException("oldString is empty string");
            }
            int matchIndex = LocalIndexOfString(oldValue, 0);
            if (matchIndex < 0) {
                return this;
            }
            // Compute a table of where to insert newValue
            int replaceCount = 0;
            int[] replacementTable = new int[(this.m_stringLength - matchIndex) / oldValue.m_stringLength + 1];
            do {
                replacementTable[replaceCount] = matchIndex;
                replaceCount++;
                matchIndex = LocalIndexOfString(oldValue, matchIndex+oldValue.m_stringLength);
            } while (matchIndex >= 0);
            int newLength = this.m_stringLength + replaceCount * (newValue.m_stringLength - oldValue.m_stringLength);
            String result = FastAllocateString(newLength);
            int srcIndex = 0;
            int destIndex = 0;
            for (int replIndex = 0; replIndex < replaceCount; replIndex++) {
                int count = replacementTable[replIndex] - srcIndex;
                FillSubstring(result, destIndex, this, srcIndex, count);
                srcIndex += count + oldValue.m_stringLength;
                destIndex += count;
                FillString(result, destIndex, newValue);
                destIndex += newValue.m_stringLength;
            }
            FillSubstring(result, destIndex, this, srcIndex,
                          this.m_stringLength - srcIndex);
            return result;
        }

        private int LocalIndexOfString(String pattern, int startPos) {
            char[] stringPtr = this.m_chars;
                char[] patternPtr = pattern.m_chars;
                    int stringCharPtr = startPos;
                    int count = this.m_stringLength - pattern.m_stringLength -
                        startPos + 1;
                    for (int index = 0; index < count; index++) {
                        int stringCursor = stringCharPtr + index;
                        int patternCursor = 0;
                        int i = pattern.m_stringLength;
                        while (i > 0 && stringPtr[stringCursor] == patternPtr[patternCursor]) {
                            i--;
                            stringCursor++;
                            patternCursor++;
                        }
                        if (i == 0) {
                            return (startPos + index);
                        }
                    }
            return -1;
        }

        //| <include path='docs/doc[@for="String.Remove"]/*' />
        public String Remove(int startIndex, int count) {
            if (count < 0) {
                throw new ArgumentOutOfRangeException("count is negative");
            }
            if (startIndex < 0) {
                throw new ArgumentOutOfRangeException("startIndex is negative");
            }
            if (startIndex + count > this.m_stringLength) {
                throw new ArgumentOutOfRangeException("startIndex+count>Length");
            }
            int newLength = this.m_stringLength - count;
            String result = FastAllocateString(newLength);
            FillSubstring(result, 0, this, 0, startIndex);
            FillSubstring(result, startIndex, this, startIndex + count,
                          newLength - startIndex);
            return result;
        }

        internal void RemoveRange(int startIndex, int count) {
            int tail = this.m_stringLength - startIndex;
            this.m_stringLength -= count;
            if (tail > 0) {
                char[] charPtr = this.m_chars;
                    Buffer.MoveMemory(charPtr, charPtr, startIndex, startIndex + count,
                                      tail);
            }
        }

        //| <include path='docs/doc[@for="String.Format"]/*' />
        public static String Format(String format, Object arg0) {
            return Format(format, new Object[] {arg0});
        }

        //| <include path='docs/doc[@for="String.Format1"]/*' />
        public static String Format(String format, Object arg0, Object arg1) {
            return Format(format, new Object[] {arg0, arg1});
        }

        //| <include path='docs/doc[@for="String.Format2"]/*' />
        public static String Format(String format, Object arg0, Object arg1, Object arg2) {
            return Format(format, new Object[] {arg0, arg1, arg2});
        }

        //| <include path='docs/doc[@for="String.Format3"]/*' />
        public static String Format(String format, params Object[] args) {
            if (format == null || args == null)
                throw new ArgumentNullException((format==null)?"format":"args");
            StringBuilder sb = new StringBuilder(format.Length + args.Length * 8);
            sb.AppendFormat(format,args);
            return sb.ToString();
        }

        //| <include path='docs/doc[@for="String.Copy"]/*' />
        public static String Copy (String str) {
            if (str == null) {
                throw new ArgumentNullException("str");
            }

            int length = str.Length;

            String result = FastAllocateString(length);
            FillString(result, 0, str);
            return result;
        }

        // Used by StringBuilder to avoid data corruption
        internal static String InternalCopy (String str) {
            int length = str.Length;
            String result = FastAllocateString(length);
            FillStringEx(result, 0, str, length); // The underlying's String can changed length is StringBuilder
            return result;
        }

        //| <include path='docs/doc[@for="String.Concat"]/*' />
        public static String Concat(Object arg0) {
            if (arg0 == null) {
                return String.Empty;
            }
            return arg0.ToString();
        }

        //| <include path='docs/doc[@for="String.Concat1"]/*' />
        public static String Concat(Object arg0, Object arg1) {
            if (arg0 == null) {
                arg0 = String.Empty;
            }

            if (arg1 == null) {
                arg1 = String.Empty;
            }
            return Concat(arg0.ToString(), arg1.ToString());
        }

        //| <include path='docs/doc[@for="String.Concat2"]/*' />
        public static String Concat(Object arg0, Object arg1, Object arg2) {
            if (arg0 == null) {
                arg0 = String.Empty;
            }

            if (arg1 == null) {
                arg1 = String.Empty;
            }

            if (arg2 == null) {
                arg2 = String.Empty;
            }

            return Concat(arg0.ToString(), arg1.ToString(), arg2.ToString());
        }

        //| <include path='docs/doc[@for="String.Concat4"]/*' />
        public static String Concat(params Object[] args) {
            if (args == null) {
                throw new ArgumentNullException("args");
            }

            String[] sArgs = new String[args.Length];
            int totalLength=0;

            for (int i = 0; i < args.Length; i++) {
                object value = args[i];
                sArgs[i] = ((value==null)?(String.Empty):(value.ToString()));
                totalLength += sArgs[i].Length;
                // check for overflow
                if (totalLength < 0) {
                    throw new OutOfMemoryException();
                }
            }
            return ConcatArray(sArgs, totalLength);
        }


        //| <include path='docs/doc[@for="String.Concat5"]/*' />
        public static String Concat(String str0, String str1) {
            if (str0 == null) {
                if (str1 == null) {
                    return String.Empty;
                }
                return str1;
            }

            if (str1 == null) {
                return str0;
            }

            int str0Length = str0.Length;

            String result = FastAllocateString(str0Length + str1.Length);

            FillStringChecked(result, 0,        str0);
            FillStringChecked(result, str0Length, str1);

            return result;
        }

        //| <include path='docs/doc[@for="String.Concat6"]/*' />
        public static String Concat(String str0, String str1, String str2) {
            if (str0 == null && str1 == null && str2 == null) {
                return String.Empty;
            }

            if (str0 == null) {
                str0 = String.Empty;
            }

            if (str1 == null) {
                str1 = String.Empty;
            }

            if (str2 == null) {
                str2 = String.Empty;
            }

            int totalLength = str0.Length + str1.Length + str2.Length;

            String result = FastAllocateString(totalLength);
            FillStringChecked(result, 0, str0);
            FillStringChecked(result, str0.Length, str1);
            FillStringChecked(result, str0.Length + str1.Length, str2);

            return result;
        }

        //| <include path='docs/doc[@for="String.Concat7"]/*' />
        public static String Concat(String str0, String str1, String str2, String str3) {
            if (str0 == null && str1 == null && str2 == null && str3 == null) {
                return String.Empty;
            }

            if (str0 == null) {
                str0 = String.Empty;
            }

            if (str1 == null) {
                str1 = String.Empty;
            }

            if (str2 == null) {
                str2 = String.Empty;
            }

            if (str3 == null) {
                str3 = String.Empty;
            }

            int totalLength = str0.Length + str1.Length + str2.Length + str3.Length;

            String result = FastAllocateString(totalLength);
            FillStringChecked(result, 0, str0);
            FillStringChecked(result, str0.Length, str1);
            FillStringChecked(result, str0.Length + str1.Length, str2);
            FillStringChecked(result, str0.Length + str1.Length + str2.Length, str3);

            return result;
        }

        private static String ConcatArray(String[] values, int totalLength) {
            String result =  FastAllocateString(totalLength);
            int currPos=0;

            for (int i = 0; i < values.Length; i++) {
                Debug.Assert((currPos + values[i].Length <= totalLength),
                                "[String.ConcatArray](currPos + values[i].Length <= totalLength)");

                FillStringChecked(result, currPos, values[i]);
                currPos+=values[i].Length;
            }

            return result;
        }

        // poor man's varargs for String.Concat: 8 strings
        public static String Concat(object obj0, object obj1, object obj2,
                                    object obj3, string obj4, string obj5,
                                    string obj6, string obj7) {
            String[] strs = new String[8];
            strs[0] = obj0.ToString();
            strs[1] = obj1.ToString();
            strs[2] = obj2.ToString();
            strs[3] = obj3.ToString();
            strs[4] = obj4.ToString();
            strs[5] = obj5.ToString();
            strs[6] = obj6.ToString();
            strs[7] = obj7.ToString();
            return Join(null,strs,0,8);
        }

        private static String[] CopyArrayOnNull(String[] values, out int totalLength) {
            totalLength = 0;

            String[] outValues = new String[values.Length];

            for (int i = 0; i < values.Length; i++) {
                outValues[i] = ((values[i]==null)?String.Empty:values[i]);
                totalLength += outValues[i].Length;
            }
            return outValues;
        }

        //| <include path='docs/doc[@for="String.Concat8"]/*' />
        public static String Concat(params String[] values) {
            int totalLength=0;

            if (values == null) {
                throw new ArgumentNullException("values");
            }

            // Always make a copy to prevent changing the array on another thread.
            String[] internalValues = new String[values.Length];

            for (int i = 0; i < values.Length; i++) {
                string value = values[i];
                internalValues[i] = ((value==null)?(String.Empty):(value));
                totalLength += internalValues[i].Length;
                // check for overflow
                if (totalLength < 0) {
                    throw new OutOfMemoryException();
                }
            }

            return ConcatArray(internalValues, totalLength);
        }

/*TODO
        //| <include path='docs/doc[@for="String.Intern"]/*' />
        public static String Intern(String str) {
            //str.CheckHighChars();
            if (str == null) {
                throw new ArgumentNullException("str");
            }
            if (internedStringVector == null) {
                InitializeInternedTable();
            }
            int code = str.GetHashCode();
            lock (internedStringVector) {
                int[] codeTable= internedStringHashCodes;
                String[] stringTable = internedStringVector;
                int tableSize = codeTable.Length;
                int indexMask = tableSize - 1;
                int hx = code & indexMask;
                //Scan up from our initial hash index guess
                while (true) {   // It is guaranteed there are holes in table
                    int tableCode = codeTable[hx];
                    if (tableCode == code) {
                        String s = stringTable[hx];
                        if (str.Equals(s)) {
                            return s;
                        }
                    }
                    else if (tableCode == 0 && stringTable[hx] == null) {
                        // We need to insert the string here!
                        int stringLength = str.Length;
                        if (str.m_arrayLength > stringLength + 1) {
                            str = NewString(str, 0, stringLength, stringLength);
                        }
                        internedStringCount++;
                        stringTable[hx] = str; // Set string entry first
                        codeTable[hx] = code; // then set code!
                        if (internedStringCount >= internedStringBound) {
                            RehashInternedStrings();
                        }
                        return str;
                    }
                    hx++;
                    hx &= indexMask;
                }
            }
        }

        //| <include path='docs/doc[@for="String.IsInterned"]/*' />
        public static String IsInterned(String str) {
            if (str == null) {
                throw new ArgumentNullException("str");
            }
            if (internedStringVector == null) {
                InitializeInternedTable();
            }
            int code = str.GetHashCode();
            int[] codeTable;
            String[] stringTable;
            do {
                codeTable = internedStringHashCodes;
                stringTable = internedStringVector;
            } while (codeTable.Length != stringTable.Length);
            int tableSize = codeTable.Length;
            int indexMask = tableSize - 1;
            int hx = code & indexMask;
            // Scan up from our initial hash index guess
            while (true) {       // It is guaranteed there are holes in table
                int tableCode = codeTable[hx];
                if (tableCode == code) {
                    String s = stringTable[hx];
                    if (str.Equals(s)) {
                        return s;
                    }
                }
                else if (tableCode == 0 && stringTable[hx] == null) {
                    return null;
                }
                hx++;
                hx &= indexMask;
            }
        }

        private static void InitializeInternedTable() {
            lock (typeof(String)) {
                if (internedStringVector != null) {
                    return;
                }
                int tableSize = 128;
                int constantCount = internedStringConstants.Length;
                while (tableSize <= constantCount) {
                    tableSize <<= 1;
                }
                int indexMask = tableSize - 1;
                String[] stringTable = new String[tableSize];
                int[] codeTable = new int[tableSize];
                for (int i = 0; i < constantCount; i++) {
                    String s = internedStringConstants[i];
                    int code = s.GetHashCode();
                    int hx = code & indexMask;
                    while (true) {
                        int tableCode = codeTable[hx];
                        VTable.Assert(tableCode != code ||
                                      !s.Equals(stringTable[hx]),
                                      "Duplicate interned strings!");
                        if (tableCode == 0 && stringTable[hx] == null) {
                            internedStringCount++;
                            stringTable[hx] = s;
                            codeTable[hx] = code;
                            break;
                        }
                        hx++;
                        hx &= indexMask;
                    }
                }
                internedStringBound = tableSize >> 1;
                internedStringHashCodes = codeTable;
                internedStringVector = stringTable;
            }
        }

        private static void RehashInternedStrings() {
            int[] oldCodeTable = internedStringHashCodes;
            String[] oldStringTable = internedStringVector;
            int oldSize = oldCodeTable.Length;
            int oldIndexMask = oldSize - 1;
            int newSize = oldSize << 1;
            int newIndexMask = newSize -1;
            int[] newCodeTable = new int[newSize];
            String[] newStringTable = new String[newSize];
            for (int i = 0; i < oldSize; i++) {
                String s = oldStringTable[i];
                if (s != null) {
                    int code = oldCodeTable[i];
                    int hx = code & newIndexMask;
                    while (true) {
                        if (newCodeTable[hx] == 0 &&
                            newStringTable[hx] == null) {
                            newStringTable[hx] = s;
                            newCodeTable[hx] = code;
                            break;
                        }
                        hx++;
                        hx &= newIndexMask;
                    }
                }
            }
            internedStringBound = newSize >> 1;
            internedStringHashCodes = newCodeTable;
            internedStringVector = newStringTable;
        }

        private static int internedStringCount;
        private static int internedStringBound;
        private static int[] internedStringHashCodes;
        private static String[] internedStringVector;
        private static String[] internedStringConstants;
*/

        private static readonly bool [] HighCharTable = {
            false, // 0x00, 0x0
            true,  // 0x01, .
            true,  // 0x02, .
            true,  // 0x03, .
            true,  // 0x04, .
            true,  // 0x05, .
            true,  // 0x06, .
            true,  // 0x07, .
            true,  // 0x08, .
            false, // 0x09,
            false, // 0x0A,
            false, // 0x0B, .
            false, // 0x0C, .
            false, // 0x0D,
            true,  // 0x0E, .
            true,  // 0x0F, .
            true,  // 0x10, .
            true,  // 0x11, .
            true,  // 0x12, .
            true,  // 0x13, .
            true,  // 0x14, .
            true,  // 0x15, .
            true,  // 0x16, .
            true,  // 0x17, .
            true,  // 0x18, .
            true,  // 0x19, .
            true,  // 0x1A,
            true,  // 0x1B, .
            true,  // 0x1C, .
            true,  // 0x1D, .
            true,  // 0x1E, .
            true,  // 0x1F, .
            false, // 0x20,
            false, // 0x21, !
            false, // 0x22, "
            false, // 0x23, #
            false, // 0x24, $
            false, // 0x25, %
            false, // 0x26, &
            true,  // 0x27, '
            false, // 0x28, (
            false, // 0x29, )
            false, // 0x2A  *
            false, // 0x2B, +
            false, // 0x2C, ,
            true,  // 0x2D, -
            false, // 0x2E, .
            false, // 0x2F, /
            false, // 0x30, 0
            false, // 0x31, 1
            false, // 0x32, 2
            false, // 0x33, 3
            false, // 0x34, 4
            false, // 0x35, 5
            false, // 0x36, 6
            false, // 0x37, 7
            false, // 0x38, 8
            false, // 0x39, 9
            false, // 0x3A, :
            false, // 0x3B, ;
            false, // 0x3C, <
            false, // 0x3D, =
            false, // 0x3E, >
            false, // 0x3F, ?
            false, // 0x40, @
            false, // 0x41, A
            false, // 0x42, B
            false, // 0x43, C
            false, // 0x44, D
            false, // 0x45, E
            false, // 0x46, F
            false, // 0x47, G
            false, // 0x48, H
            false, // 0x49, I
            false, // 0x4A, J
            false, // 0x4B, K
            false, // 0x4C, L
            false, // 0x4D, M
            false, // 0x4E, N
            false, // 0x4F, O
            false, // 0x50, P
            false, // 0x51, Q
            false, // 0x52, R
            false, // 0x53, S
            false, // 0x54, T
            false, // 0x55, U
            false, // 0x56, V
            false, // 0x57, W
            false, // 0x58, X
            false, // 0x59, Y
            false, // 0x5A, Z
            false, // 0x5B, [
            false, // 0x5C, \
            false, // 0x5D, ]
            false, // 0x5E, ^
            false, // 0x5F, _
            false, // 0x60, `
            false, // 0x61, a
            false, // 0x62, b
            false, // 0x63, c
            false, // 0x64, d
            false, // 0x65, e
            false, // 0x66, f
            false, // 0x67, g
            false, // 0x68, h
            false, // 0x69, i
            false, // 0x6A, j
            false, // 0x6B, k
            false, // 0x6C, l
            false, // 0x6D, m
            false, // 0x6E, n
            false, // 0x6F, o
            false, // 0x70, p
            false, // 0x71, q
            false, // 0x72, r
            false, // 0x73, s
            false, // 0x74, t
            false, // 0x75, u
            false, // 0x76, v
            false, // 0x77, w
            false, // 0x78, x
            false, // 0x79, y
            false, // 0x7A, z
            false, // 0x7B, {
            false, // 0x7C, |
            false, // 0x7D, }
            false, // 0x7E, ~
            true,  // 0x7F, 
        };

        //
        // IValue implementation
        //

        //| <include path='docs/doc[@for="String.GetTypeCode"]/*' />
        [NoHeapAllocation]
        public override TypeCode GetTypeCode() {
            return TypeCode.String;
        }

/*
        // Is this a string that can be compared quickly (that is it has only characters > 0x80
        // and not a - or '
        internal bool IsFastSort() {
            return ((StringState & StringState.SpecialSort) != 0);
        }

        internal bool IsSlowSort() {
            return !IsFastSort();
        }

        internal bool IsFastOpsExceptSort() {
            return ((StringState & StringState.SpecialSort) != 0 ||
                    (StringState & StringState.FastOps) != 0);
        }

        internal bool IsFastCasing() {
            return ((StringState & StringState.SpecialSort) != 0 ||
                    (StringState & StringState.FastOps) != 0);
        }

        internal bool IsFastIndex() {
            return ((StringState & StringState.SpecialSort) != 0 ||
                    (StringState & StringState.FastOps) != 0);
        }

        internal bool IsStringStateUndetermined() {
            return (StringState == StringState.Undetermined);
        }

        internal bool HasHighChars() {
            return (StringState == StringState.HighChars);
        }

        /// <summary>
        /// Get or set the string state for this string.
        /// </summary>
        internal unsafe StringState StringState {
            [NoLoggingForUndo]
                get {
                StringState result = MultiUseWord.GetStringState(this);
                return (StringState)result;
            }
            [NoLoggingForUndo]
                set {
                MultiUseWord.SetStringState(this, value);
            }
        }

        internal StringState CheckHighChars() {
            char c;
            StringState ss = StringState.FastOps;
            int length = m_stringLength;
            char[] charPtr = this.m_chars;
                for (int i = 0; i < length; i++) {
                    c = charPtr[i];
                    if (c >= 0x80) {
                        StringState = StringState.HighChars;
                        return StringState;
                    }
                    else if (HighCharTable[(int)c]) {
                        ss = StringState.SpecialSort;
                    }
                }

            StringState = ss;
            return StringState;
        }
*/

        ///<internalonly/>
        internal void SetChar(int index, char value)
        {
#if _DEBUG
            Debug.Assert(ValidModifiableString(), "Modifiable string must not have highChars flags set");
#endif

            //Bounds check and then set the actual bit.
            if ((UInt32)index >= (UInt32)Length)
                throw new ArgumentOutOfRangeException("index", "ArgumentOutOfRange_Index");

            char[] p = this.m_chars;
                // Set the character.
                p[index] = value;
        }

#if _DEBUG
        // Only used in debug build. Insure that the HighChar state information for a string is not set as known
        [MethodImpl(MethodImplOptions.InternalCall)]
        private bool ValidModifiableString() {
            throw new Exception("System.String.ValidModifiableString not implemented in Bartok!");
        }
#endif

        ///<internalonly/>
        internal void AppendInPlace(char value,int currentLength)
        {
            Debug.Assert(currentLength < m_arrayLength, "[String.AppendInPlace(char)currentLength < m_arrayLength");
#if _DEBUG
            Debug.Assert(ValidModifiableString(), "Modifiable string must not have highChars flags set");
#endif

            char[] p = this.m_chars;
            {
                // Append the character.
                p[currentLength] = value;
                p[++currentLength] = '\0';
                m_stringLength = currentLength;
            }
        }


        ///<internalonly/>
        internal void AppendInPlace(char value, int repeatCount, int currentLength)
        {
            Debug.Assert(currentLength+repeatCount < m_arrayLength, "[String.AppendInPlace]currentLength+repeatCount < m_arrayLength");
#if _DEBUG
            Debug.Assert(ValidModifiableString(), "Modifiable string must not have highChars flags set");
#endif

            int newLength = currentLength + repeatCount;


            char[] p = this.m_chars;
            {
                int i;
                for (i = currentLength; i < newLength; i++) {
                    p[i] = value;
                }
                p[i] = '\0';
            }
            this.m_stringLength = newLength;
        }

        ///<internalonly/>
        internal void AppendInPlace(String value, int currentLength) {
            Debug.Assert(value!=null, "[String.AppendInPlace]value!=null");
            Debug.Assert(value.Length + currentLength < this.m_arrayLength, "[String.AppendInPlace]Length is wrong.");
#if _DEBUG
            Debug.Assert(ValidModifiableString(), "Modifiable string must not have highChars flags set");
#endif

            FillString(this, currentLength, value);
            SetLength(currentLength + value.Length);
            NullTerminate();
        }

        internal void AppendInPlace(String value, int startIndex, int count, int currentLength) {
            Debug.Assert(value!=null, "[String.AppendInPlace]value!=null");
            Debug.Assert(count + currentLength < this.m_arrayLength, "[String.AppendInPlace]count + currentLength < this.m_arrayLength");
            Debug.Assert(count>=0, "[String.AppendInPlace]count>=0");
            Debug.Assert(startIndex>=0, "[String.AppendInPlace]startIndex>=0");
            Debug.Assert(startIndex <= (value.Length - count), "[String.AppendInPlace]startIndex <= (value.Length - count)");
#if _DEBUG
            Debug.Assert(ValidModifiableString(), "Modifiable string must not have highChars flags set");
#endif

            FillSubstring(this, currentLength, value, startIndex, count);
            SetLength(currentLength + count);
            NullTerminate();
        }

        ///<internalonly/>
        internal void AppendInPlace(char[] value, int start, int count, int currentLength) {
            Debug.Assert(value!=null, "[String.AppendInPlace]value!=null");
            Debug.Assert(count + currentLength < this.m_arrayLength, "[String.AppendInPlace]Length is wrong.");
            Debug.Assert(value.Length-count>=start, "[String.AppendInPlace]value.Length-count>=start");
#if _DEBUG
            Debug.Assert(ValidModifiableString(), "Modifiable string must not have highChars flags set");
#endif

            FillStringArray(this, currentLength, value, start, count);
            this.m_stringLength = (currentLength + count);
            this.NullTerminate();
        }


        ///<internalonly/>
        internal void ReplaceCharInPlace(char oldChar, char newChar, int startIndex, int count,int currentLength) {
            Debug.Assert(startIndex>=0, "[String.ReplaceCharInPlace]startIndex>0");
            Debug.Assert(startIndex<=currentLength, "[String.ReplaceCharInPlace]startIndex>=Length");
            Debug.Assert((startIndex<=currentLength-count), "[String.ReplaceCharInPlace]count>0 && startIndex<=currentLength-count");
#if _DEBUG
            Debug.Assert(ValidModifiableString(), "Modifiable string must not have highChars flags set");
#endif

            int endIndex = startIndex+count;

            char[] p = this.m_chars;
                for (int i = startIndex; i < endIndex; i++) {
                    if (p[i] == oldChar) {
                        p[i]=newChar;
                    }
                }
        }


        ///<internalonly/>
        internal static String GetStringForStringBuilder(String value, int capacity) {
            Debug.Assert(value!=null, "[String.GetStringForStringBuilder]value!=null");
            Debug.Assert(capacity>=value.Length,  "[String.GetStringForStringBuilder]capacity>=value.Length");

            String newStr = FastAllocateString(capacity);
            if (value.Length == 0) {
                newStr.m_stringLength=0;
                newStr.m_chars[0]='\0';
                return newStr;
            }
            FillString(newStr, 0, value);
            newStr.m_stringLength = value.m_stringLength;
            return newStr;
        }

        ///<internalonly/>
        private void NullTerminate() {
            char[] p = this.m_chars;
                p[m_stringLength] = '\0';
        }

        ///<internalonly/>
        internal void ClearPostNullChar() {
            int newLength = Length+1;
            if (newLength < m_arrayLength) {
                char[] p = this.m_chars;
                    p[newLength] = '\0';
            }
        }

        ///<internalonly/>
        internal void SetLength(int newLength) {
            Debug.Assert(newLength <= m_arrayLength, "newLength<=m_arrayLength");
            m_stringLength = newLength;
        }



        //| <include path='docs/doc[@for="String.GetEnumerator"]/*' />
        public CharEnumerator GetEnumerator() {
            return new CharEnumerator(this);
        }

        //| <include path='docs/doc[@for="String.IEnumerable.GetEnumerator"]/*' />
        /// <internalonly/>
        IEnumerator IEnumerable.GetEnumerator() {
            return new CharEnumerator(this);
        }

        //
        // This is just designed to prevent compiler warnings.
        // This field is used from native, but we need to prevent the compiler warnings.
        //
#if _DEBUG
        private void DontTouchThis() {
            m_arrayLength = 0;
            m_stringLength = 0;
            m_firstChar = m_firstChar;
        }
#endif

        internal void InternalSetCharNoBoundsCheck(int index, char value) {
            char[] p = this.m_chars;
                p[index] = value;
        }


#if false // Unused

        // NOTE: len is not the length in characters, but the length in bytes

        // Copies the source String (byte buffer) to the destination IntPtr memory allocated with len bytes.
        internal unsafe static void InternalCopy(String src, IntPtr dest,int len)
        {
            if (len == 0)
                return;
            fixed(char* charPtr = &src.m_firstChar) {
                byte* srcPtr = (byte*) charPtr;
                byte* dstPtr = (byte*) dest.ToPointer();
                Buffer.MoveMemory(dstPtr, srcPtr, len);
            }
        }

        // memcopies characters inside a String.
        internal unsafe static void InternalMemCpy(String src, int srcOffset, String dst, int destOffset, int len)
        {
            if (len == 0)
                return;
            fixed(char* srcPtr = &src.m_firstChar) {
                fixed(char* dstPtr = &dst.m_firstChar) {
                    Buffer.MoveMemory((byte*)(dstPtr + destOffset),
                                      (byte*)(srcPtr + srcOffset),
                                      len);
                }
            }
        }
#endif
/*
        // Copies the source String to the destination byte[]
        internal unsafe static void InternalCopy(String src, byte[] dest, int stringLength)
        {
            if (stringLength == 0)
                return;
            int len = stringLength * sizeof(char);

            Debug.Assert(dest.Length >= len);

            fixed(char* charPtr = &src.m_firstChar) {
                fixed(byte* destPtr = &dest[0]) {
                    byte* srcPtr = (byte*) charPtr;
                    Buffer.MoveMemory(destPtr, srcPtr, len);
                }
            }
        }
*/
        internal void InsertInPlace(int index, String value, int repeatCount, int currentLength, int requiredLength) {
            Debug.Assert(requiredLength  < m_arrayLength, "[String.InsertString] requiredLength  < m_arrayLength");
            Debug.Assert(index + value.Length * repeatCount < m_arrayLength, "[String.InsertString] index + value.Length * repeatCount < m_arrayLength");
#if _DEBUG
            Debug.Assert(ValidModifiableString(), "Modifiable string must not have highChars flags set");
#endif
            //Copy the old characters over to make room and then insert the new characters.
            char[] srcPtr = this.m_chars;
                char[] valuePtr = value.m_chars;
                    Buffer.MoveMemory(srcPtr,
                                   srcPtr,
                                   index + value.Length * repeatCount,
                                   index,
                                   currentLength - index);
                    for (int i = 0; i < repeatCount; i++) {
                        Buffer.MoveMemory(srcPtr,
                                       valuePtr,
                                       index + i * value.Length,
                                       0,
                                       value.Length);
                    }
                srcPtr[requiredLength] = '\0';
            this.m_stringLength = requiredLength;
        }

/*
        [CLSCompliant(false)]
        [NoHeapAllocation]
        public int LimitedFormatTo(char[] buffer, int length)
        {
            unchecked {
                char[] fmtBeg = m_chars;
                    int fmtPtr = 0;
                    int fmtEnd = m_stringLength;

                    int outPtr = 0;
                    int outEnd = length;

                    while (fmtPtr < fmtEnd && outPtr < outEnd) {
                        buffer[outPtr++] = fmtBeg[fmtPtr++];
                    }
                    return (int)(outPtr - buffer);
                }
        }

        [CLSCompliant(false)]
        [NoHeapAllocation]
        public static unsafe int LimitedFormatTo(String format,
                                                 ArgIterator args,
                                                 char *buffer,
                                                 int length)
        {
            unchecked {

            fixed (char *fmtBeg = &format.m_firstChar) {
                char *fmtPtr = fmtBeg;
                char *fmtEnd = fmtBeg + format.m_stringLength;

                char *outPtr = buffer;
                char *outEnd = buffer + length;

                while (fmtPtr < fmtEnd && outPtr < outEnd) {
                    if (*fmtPtr == '{') {
                        char * fmtPos = fmtPtr;
                        bool bad = false;
                        fmtPtr++;

                        int ndx = 0;
                        int aln = 0;
                        int wid = 0;
                        char fmt = 'd';

                        if (*fmtPtr == '{') {
                            *outPtr++ = *fmtPtr++;
                        }
                        else if (*fmtPtr >= '0' && *fmtPtr <= '9') {

                            // {index,alignment:type width}
                            // Get Index
                            while (*fmtPtr >= '0' && *fmtPtr <= '9') {
                                ndx = ndx * 10 + (*fmtPtr++ - '0');
                            }

                            // Get Alignment
                            if (*fmtPtr == ',') {
                                fmtPtr++;
                                if (*fmtPtr == '-') {
                                    // We just ignore left alignment for now.
                                    fmtPtr++;
                                }
                                while (*fmtPtr >= '0' && *fmtPtr <= '9') {
                                    aln = aln * 10 + (*fmtPtr++ - '0');
                                }
                            }

                            // Get FormatString
                            if (*fmtPtr == ':') {
                                fmtPtr++;
                                if (*fmtPtr >= 'a' && *fmtPtr <= 'z') {
                                    fmt = *fmtPtr++;
                                }
                                else if (*fmtPtr >= 'A' && *fmtPtr <= 'Z') {
                                    fmt = (char)(*fmtPtr++ - ('A' + 'a'));
                                }
                                while (*fmtPtr >= '0' && *fmtPtr <= '9') {
                                    wid = wid * 10 + (*fmtPtr++ - '0');
                                }
                            }

                            // Get closing brace.
                            if (*fmtPtr == '}') {
                                fmtPtr++;
                            }
                            else {
                                bad = true;
                            }

                            if (ndx >= args.Length) {
                                bad = true;
                            }

                            if (bad) {
                                for (; fmtPos < fmtPtr; fmtPos++) {
                                    if (outPtr < outEnd) {
                                        *outPtr++ = *fmtPos;
                                    }
                                }
                            }
                            else {
                                // Get the value
                                char cvalue = '\0';
                                long ivalue = 0;
                                string svalue = null;
                                ulong value = 0;
                                IntPtr pvalue;
                                RuntimeType type;

                                type = args.GetArg(ndx, out pvalue);

                                switch (type.classVtable.structuralView) {
                                    case StructuralType.Bool:
                                        svalue =*(bool *)pvalue ? "true" : "false";
                                        break;
                                    case StructuralType.Char:
                                        cvalue = *(char *)pvalue;
                                        break;
                                    case StructuralType.Int8:
                                        ivalue = *(sbyte *)pvalue;
                                        break;
                                    case StructuralType.Int16:
                                        ivalue = *(short *)pvalue;
                                        break;
                                    case StructuralType.Int32:
                                        ivalue = *(int *)pvalue;
                                        break;
                                    case StructuralType.Int64:
                                        ivalue = *(long *)pvalue;
                                        break;
                                    case StructuralType.UInt8:
                                        value = *(byte *)pvalue;
                                        break;
                                    case StructuralType.UInt16:
                                        value = *(ushort *)pvalue;
                                        break;
                                    case StructuralType.UInt32:
                                        value = *(uint *)pvalue;
                                        break;
                                    case StructuralType.UInt64:
                                        value = *(ulong *)pvalue;
                                        break;
                                    case StructuralType.IntPtr:
                                        value = (ulong)*(IntPtr *)pvalue;
                                        break;
                                    case StructuralType.UIntPtr:
                                        value = (ulong)*(UIntPtr *)pvalue;
                                        break;
                                    case StructuralType.Reference:
                                        if (type == typeof(String)) {
                                            svalue = Magic.toString(
                                                Magic.fromAddress(*(UIntPtr *)pvalue));
                                        }
                                        else {
                                            svalue = type.Name;
                                        }
                                        break;
                                    default:
                                        svalue = "???";
                                        break;
                                }

                                if (cvalue != '\0') {
                                    outPtr = AddChar(outPtr, outEnd, *(char *)pvalue, aln);
                                }
                                else if (svalue != null) {
                                    outPtr = AddString(outPtr, outEnd, svalue, aln);
                                }
                                else {
                                    if (aln < wid) {
                                        aln = wid;
                                    }

                                    if (fmt == 'x') {
                                        if (ivalue != 0) {
                                            value = (ulong)ivalue;
                                        }
                                        outPtr = AddNumber(outPtr, outEnd, value, aln, 16, '0', '\0');
                                    }
                                    else {
                                        char sign = '\0';
                                        if (ivalue < 0) {
                                            sign = '-';
                                            value = unchecked((ulong)-ivalue);
                                        }
                                        else if (ivalue > 0) {
                                            value = unchecked((ulong)ivalue);
                                        }
                                        outPtr = AddNumber(outPtr, outEnd, value, aln, 10, ' ', sign);
                                    }
                                }
                            }
                        }
                    }
                    else if (*fmtPtr == '}') {
                        if (outPtr < outEnd) {
                            *outPtr++ = *fmtPtr;
                        }
                        fmtPtr++;
                    }
                    else {
                        if (outPtr < outEnd) {
                            *outPtr++ = *fmtPtr;
                        }
                        fmtPtr++;
                    }
                }
                return (int)(outPtr - buffer);
            }
            }
        }

        [NoHeapAllocation]
        private static int CountDigits(ulong value, uint valueBase)
        {
            int used = 0;
            do {
                value /= valueBase;
                used++;
            } while (value != 0);

            return used;
        }

        [NoHeapAllocation]
        private static unsafe char * AddNumber(char *output, char *limit,
                                               ulong value, int width, uint valueBase,
                                               char fill, char sign)
        {
            // Make sure we won't overfill the buffer.
            if (output >= limit) {
                return output;
            }

            // Figure out how wide the string will be.
            int used = CountDigits(value, valueBase);

            // Check for overflow.
            if (output + used + ((sign != '\0') ? 1 : 0) > limit) {
                while (width > 0) {
                    *output++ = '#';
                    width--;
                }
                return output;
            }

            // Handle sign and space fill.
            if (sign != '\0' && used < width) {
                if (fill == ' ') {
                    while (width > used + 1) {
                        *output++ = fill;
                        width--;
                    }
                    fill = '\0';
                }
                *output++ = sign;
            }

            // Handle other non-zero fills.
            if (fill != '\0') {
                while (width > used) {
                    *output++ = fill;
                    width--;
                }
            }

            // Write the characters into the buffer.
            char *outp = output + used;
            do {
                uint digit = (uint)(value % valueBase);
                value /= valueBase;

                if (digit >= 0 && digit <= 9) {
                    *--outp = (char)('0' + digit);
                }
                else {
                    *--outp = (char)('a' + (digit - 10));
                }
            } while (value != 0);

            return output + used;
        }

        [NoHeapAllocation]
        private static unsafe char * AddString(char *output, char *limit,
                                               string value, int width)
        {
            fixed (char *pwString = &value.m_firstChar) {
                return AddString(output, limit, pwString, value.m_stringLength, width);
            }
        }

        [NoHeapAllocation]
        private static unsafe char * AddString(char *output, char *limit,
                                               char *pwString, int cwString,
                                               int width)
        {
            // width < 0: left justified at -width.
            // width = 0: no specified width.
            // width > 0: right justified at width.
            if (width == 0) {
                width = cwString;
            }
            else if (width < 0) {
                width = -width;
                for (; width > cwString; width--) {
                    if (output < limit) {
                        *output++ = ' ';
                    }
                }
            }

            while (cwString > 0 && width > 0) {
                if (output < limit) {
                    *output++ = (char)*pwString++;
                }
                width--;
                cwString--;
            }

            for (; width > 0; width--) {
                if (output < limit) {
                    *output++ = ' ';
                }
            }
            return output;
        }

        [NoHeapAllocation]
        private static unsafe char * AddChar(char *output, char *limit, char value, int width)
        {
            // width < 0: left justified at -width.
            // width = 0: no specified width.
            // width > 0: right justified at width.
            if (width == 0) {
                width = 1;
            }
            else if (width < 0) {
                width = -width;
                for (; width > 1; width--) {
                    if (output < limit) {
                        *output++ = ' ';
                    }
                }
            }

            if (width > 0) {
                if (output < limit) {
                    *output++ = (char)value;
                }
                width--;
            }

            for (; width > 0; width--) {
                if (output < limit) {
                    *output++ = ' ';
                }
            }
            return output;
        }
*/
    }
}
