* String.cs: Comment out 2.0 compatibility fix for SubString, since it
[mono.git] / mcs / class / corlib / System / String.cs
1 //
2 // System.String.cs
3 //
4 // Authors:
5 //   Patrik Torstensson
6 //   Jeffrey Stedfast (fejj@ximian.com)
7 //   Dan Lewis (dihlewis@yahoo.co.uk)
8 //   Sebastien Pouliot  <sebastien@ximian.com>
9 //   Marek Safar (marek.safar@seznam.cz)
10 //   Andreas Nahr (Classdevelopment@A-SoftTech.com)
11 //
12 // (C) 2001 Ximian, Inc.  http://www.ximian.com
13 // Copyright (C) 2004-2005 Novell (http://www.novell.com)
14 //
15 // Permission is hereby granted, free of charge, to any person obtaining
16 // a copy of this software and associated documentation files (the
17 // "Software"), to deal in the Software without restriction, including
18 // without limitation the rights to use, copy, modify, merge, publish,
19 // distribute, sublicense, and/or sell copies of the Software, and to
20 // permit persons to whom the Software is furnished to do so, subject to
21 // the following conditions:
22 // 
23 // The above copyright notice and this permission notice shall be
24 // included in all copies or substantial portions of the Software.
25 // 
26 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
27 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
28 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
29 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
30 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
31 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
32 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
33 //
34 //
35 //
36 // This class contains all implementation for culture-insensitive methods.
37 // Culture-sensitive methods are implemented in the System.Globalization or
38 // Mono.Globalization namespace.
39 //
40 // Ensure that argument checks on methods don't overflow
41 //
42
43 using System.Text;
44 using System.Collections;
45 using System.Globalization;
46 using System.Runtime.CompilerServices;
47
48 #if NET_2_0
49 using System.Collections.Generic;
50 using System.Runtime.ConstrainedExecution;
51 using System.Runtime.InteropServices;
52 using Mono.Globalization.Unicode;
53 #endif
54
55 namespace System
56 {
57         [Serializable]
58 #if NET_2_0
59         [ComVisible (true)]
60         public sealed class String : IConvertible, ICloneable, IEnumerable, IComparable, IComparable<String>, IEquatable <String>, IEnumerable<char>
61 #else
62         public sealed class String : IConvertible, ICloneable, IEnumerable, IComparable
63 #endif
64         {
65                 [NonSerialized] private int length;
66                 [NonSerialized] private char start_char;
67
68                 public static readonly String Empty = "";
69
70                 public static unsafe bool Equals (string a, string b)
71                 {
72                         if ((a as object) == (b as object))
73                                 return true;
74
75                         if (a == null || b == null)
76                                 return false;
77
78                         int len = a.length;
79
80                         if (len != b.length)
81                                 return false;
82
83                         fixed (char* s1 = &a.start_char, s2 = &b.start_char) {
84                                 char* s1_ptr = s1;
85                                 char* s2_ptr = s2;
86
87                                 while (len >= 8) {
88                                         if (((int*)s1_ptr)[0] != ((int*)s2_ptr)[0] ||
89                                                 ((int*)s1_ptr)[1] != ((int*)s2_ptr)[1] ||
90                                                 ((int*)s1_ptr)[2] != ((int*)s2_ptr)[2] ||
91                                                 ((int*)s1_ptr)[3] != ((int*)s2_ptr)[3])
92                                                 return false;
93
94                                         s1_ptr += 8;
95                                         s2_ptr += 8;
96                                         len -= 8;
97                                 }
98
99                                 if (len >= 4) {
100                                         if (((int*)s1_ptr)[0] != ((int*)s2_ptr)[0] ||
101                                                 ((int*)s1_ptr)[1] != ((int*)s2_ptr)[1])
102                                                 return false;
103
104                                         s1_ptr += 4;
105                                         s2_ptr += 4;
106                                         len -= 4;
107                                 }
108
109                                 if (len > 1) {
110                                         if (((int*)s1_ptr)[0] != ((int*)s2_ptr)[0])
111                                                 return false;
112
113                                         s1_ptr += 2;
114                                         s2_ptr += 2;
115                                         len -= 2;
116                                 }
117
118                                 return len == 0 || *s1_ptr == *s2_ptr;
119                         }
120                 }
121
122                 public static bool operator == (String a, String b)
123                 {
124                         return Equals (a, b);
125                 }
126
127                 public static bool operator != (String a, String b)
128                 {
129                         return !Equals (a, b);
130                 }
131
132 #if NET_2_0
133                 [ReliabilityContractAttribute (Consistency.WillNotCorruptState, Cer.MayFail)]
134 #endif
135                 public override bool Equals (Object obj)
136                 {
137                         return Equals (this, obj as String);
138                 }
139
140 #if NET_2_0
141                 [ReliabilityContractAttribute (Consistency.WillNotCorruptState, Cer.MayFail)]
142 #endif
143                 public bool Equals (String value)
144                 {
145                         return Equals (this, value);
146                 }
147
148                 [IndexerName ("Chars")]
149                 public unsafe char this [int index] {
150                         get {
151                                 if (index < 0 || index >= length)
152                                         throw new IndexOutOfRangeException ();
153                                 fixed (char* c = &start_char)
154                                         return c[index];
155                         }
156                 }
157
158                 public Object Clone ()
159                 {
160                         return this;
161                 }
162
163                 public TypeCode GetTypeCode ()
164                 {
165                         return TypeCode.String;
166                 }
167
168                 public unsafe void CopyTo (int sourceIndex, char[] destination, int destinationIndex, int count)
169                 {
170                         if (destination == null)
171                                 throw new ArgumentNullException ("destination");
172                         if (sourceIndex < 0)
173                                 throw new ArgumentOutOfRangeException ("sourceIndex", "Cannot be negative");
174                         if (destinationIndex < 0)
175                                 throw new ArgumentOutOfRangeException ("destinationIndex", "Cannot be negative.");
176                         if (count < 0)
177                                 throw new ArgumentOutOfRangeException ("count", "Cannot be negative.");
178                         if (sourceIndex > Length - count)
179                                 throw new ArgumentOutOfRangeException ("sourceIndex", "sourceIndex + count > Length");
180                         if (destinationIndex > destination.Length - count)
181                                 throw new ArgumentOutOfRangeException ("destinationIndex", "destinationIndex + count > destination.Length");
182
183                         fixed (char* dest = destination, src = this)
184                                 CharCopy (dest + destinationIndex, src + sourceIndex, count);
185                 }
186
187                 public unsafe char[] ToCharArray ()
188                 {
189                         char[] tmp = new char [length];
190                         fixed (char* dest = tmp, src = this)
191                                 CharCopy (dest, src, length);
192                         return tmp;
193                 }
194
195                 public unsafe char[] ToCharArray (int startIndex, int length)
196                 {
197                         if (startIndex < 0)
198                                 throw new ArgumentOutOfRangeException ("startIndex", "< 0"); 
199                         if (length < 0)
200                                 throw new ArgumentOutOfRangeException ("length", "< 0"); 
201                         if (startIndex > this.length - length)
202                                 throw new ArgumentOutOfRangeException ("startIndex", "Must be greater than the length of the string.");
203
204                         char[] tmp = new char [length];
205                         fixed (char* dest = tmp, src = this)
206                                 CharCopy (dest, src + startIndex, length);
207                         return tmp;
208                 }
209
210                 public String [] Split (params char [] separator)
211                 {
212                         return Split (separator, Int32.MaxValue);
213                 }
214
215                 public String[] Split (char[] separator, int count)
216                 {
217                         if (separator == null || separator.Length == 0)
218                                 separator = WhiteChars;
219
220                         if (count < 0)
221                                 throw new ArgumentOutOfRangeException ("count");
222
223                         if (count == 0) 
224                                 return new String[0];
225
226                         if (count == 1) 
227                                 return new String[1] { this };
228
229                         return InternalSplit (separator, count, 0);
230                 }
231
232 #if NET_2_0
233                 [ComVisible (false)]
234                 [MonoDocumentationNote ("code should be moved to managed")]
235                 public String[] Split (char[] separator, int count, StringSplitOptions options)
236                 {
237                         if (separator == null || separator.Length == 0)
238                                 return Split (WhiteChars, count, options);
239
240                         if (count < 0)
241                                 throw new ArgumentOutOfRangeException ("count", "Count cannot be less than zero.");
242                         if ((options != StringSplitOptions.None) && (options != StringSplitOptions.RemoveEmptyEntries))
243                                 throw new ArgumentException ("Illegal enum value: " + options + ".");
244
245                         if (count == 0)
246                                 return new string [0];
247
248                         return InternalSplit (separator, count, (int)options);
249                 }
250
251                 [ComVisible (false)]
252                 public String[] Split (string[] separator, int count, StringSplitOptions options)
253                 {
254                         if (separator == null || separator.Length == 0)
255                                 return Split (WhiteChars, count, options);
256
257                         if (count < 0)
258                                 throw new ArgumentOutOfRangeException ("count", "Count cannot be less than zero.");
259                         if ((options != StringSplitOptions.None) && (options != StringSplitOptions.RemoveEmptyEntries))
260                                 throw new ArgumentException ("Illegal enum value: " + options + ".");
261
262                         bool removeEmpty = (options & StringSplitOptions.RemoveEmptyEntries) == StringSplitOptions.RemoveEmptyEntries;
263
264                         if (count == 0 || (this == String.Empty && removeEmpty))
265                                 return new String [0];
266
267                         ArrayList arr = new ArrayList ();
268
269                         int pos = 0;
270                         int matchCount = 0;
271                         while (pos < this.Length) {
272                                 int matchIndex = -1;
273                                 int matchPos = Int32.MaxValue;
274
275                                 // Find the first position where any of the separators matches
276                                 for (int i = 0; i < separator.Length; ++i) {
277                                         string sep = separator [i];
278                                         if (sep == null || sep == String.Empty)
279                                                 continue;
280
281                                         int match = IndexOf (sep, pos);
282                                         if (match > -1 && match < matchPos) {
283                                                 matchIndex = i;
284                                                 matchPos = match;
285                                         }
286                                 }
287
288                                 if (matchIndex == -1)
289                                         break;
290
291                                 if (!(matchPos == pos && removeEmpty))
292                                         arr.Add (this.Substring (pos, matchPos - pos));
293
294                                 pos = matchPos + separator [matchIndex].Length;
295
296                                 matchCount ++;
297
298                                 if (matchCount == count - 1)
299                                         break;
300                         }
301
302                         if (matchCount == 0)
303                                 return new String [] { this };
304                         else {
305                                 if (removeEmpty && pos == this.Length) {
306                                         String[] res = new String [arr.Count];
307                                         arr.CopyTo (0, res, 0, arr.Count);
308
309                                         return res;
310                                 }
311                                 else {
312                                         String[] res = new String [arr.Count + 1];
313                                         arr.CopyTo (0, res, 0, arr.Count);
314                                         res [arr.Count] = this.Substring (pos);
315
316                                         return res;
317                                 }
318                         }
319                 }
320
321                 [ComVisible (false)]
322                 public String[] Split (char[] separator, StringSplitOptions options)
323                 {
324                         return Split (separator, Int32.MaxValue, options);
325                 }
326
327                 [ComVisible (false)]
328                 public String[] Split (String[] separator, StringSplitOptions options)
329                 {
330                         return Split (separator, Int32.MaxValue, options);
331                 }
332 #endif
333
334                 public String Substring (int startIndex)
335                 {
336 #if NET_2_0
337                         if (startIndex == 0)
338                                 return this;
339                         if (startIndex < 0 || startIndex > this.length)
340                                 throw new ArgumentOutOfRangeException ("startIndex");
341 #else
342                         if (startIndex < 0)
343                                 throw new ArgumentOutOfRangeException ("startIndex", "Cannot be negative.");
344
345                         if (startIndex > this.length)
346                                 throw new ArgumentOutOfRangeException ("length", "Cannot exceed length of string.");
347 #endif
348
349                         return SubstringUnchecked (startIndex, this.length - startIndex);
350                 }
351
352                 public String Substring (int startIndex, int length)
353                 {
354                         if (length < 0)
355                                 throw new ArgumentOutOfRangeException ("length", "Cannot be negative.");
356                         if (startIndex < 0)
357                                 throw new ArgumentOutOfRangeException ("startIndex", "Cannot be negative.");
358 #if NET_2_0
359                         if (startIndex > this.length)
360                                 throw new ArgumentOutOfRangeException ("startIndex", "Cannot exceed length of string.");
361 #endif
362                         if (startIndex > this.length - length)
363                                 throw new ArgumentOutOfRangeException ("length", "startIndex + length > this.length");
364 #if NET_2_0
365                         // FIXME: this causes regressions in System.Xml
366                         /*
367                         if (startIndex == 0 && length == this.length)
368                                 return this;
369                         */
370 #endif
371
372                         return SubstringUnchecked (startIndex, length);
373                 }
374
375                 internal unsafe String SubstringUnchecked (int startIndex, int length)
376                 {
377                         if (length == 0)
378                                 return String.Empty;
379
380                         string tmp = InternalAllocateStr (length);
381                         fixed (char* dest = tmp, src = this) {
382                                 CharCopy (dest, src + startIndex, length);
383                         }
384                         return tmp;
385                 }
386
387                 private static readonly char[] WhiteChars = { (char) 0x9, (char) 0xA, (char) 0xB, (char) 0xC, (char) 0xD,
388 #if NET_2_0
389                         (char) 0x85, (char) 0x1680, (char) 0x2028, (char) 0x2029,
390 #endif
391                         (char) 0x20, (char) 0xA0, (char) 0x2000, (char) 0x2001, (char) 0x2002, (char) 0x2003, (char) 0x2004,
392                         (char) 0x2005, (char) 0x2006, (char) 0x2007, (char) 0x2008, (char) 0x2009, (char) 0x200A, (char) 0x200B,
393                         (char) 0x3000, (char) 0xFEFF };
394
395                 public String Trim ()
396                 {
397                         if (length == 0) 
398                                 return String.Empty;
399                         int start = FindNotWhiteSpace (0, length, 1);
400
401                         if (start == length)
402                                 return String.Empty;
403
404                         int end = FindNotWhiteSpace (length - 1, start, -1);
405
406                         int newLength = end - start + 1;
407                         if (newLength == length)
408                                 return this;
409
410                         return SubstringUnchecked (start, newLength);
411                 }
412
413                 public String Trim (params char[] trimChars)
414                 {
415                         if (trimChars == null || trimChars.Length == 0)
416                                 return Trim ();
417
418                         if (length == 0) 
419                                 return String.Empty;
420                         int start = FindNotInTable (0, length, 1, trimChars);
421
422                         if (start == length)
423                                 return String.Empty;
424
425                         int end = FindNotInTable (length - 1, start, -1, trimChars);
426
427                         int newLength = end - start + 1;
428                         if (newLength == length)
429                                 return this;
430
431                         return SubstringUnchecked (start, newLength);
432                 }
433
434                 public String TrimStart (params char[] trimChars)
435                 {
436                         if (length == 0) 
437                                 return String.Empty;
438                         int start;
439                         if (trimChars == null || trimChars.Length == 0)
440                                 start = FindNotWhiteSpace (0, length, 1);
441                         else
442                                 start = FindNotInTable (0, length, 1, trimChars);
443
444                         if (start == 0)
445                                 return this;
446
447                         return SubstringUnchecked (start, length - start);
448                 }
449
450                 public String TrimEnd (params char[] trimChars)
451                 {
452                         if (length == 0) 
453                                 return String.Empty;
454                         int end;
455                         if (trimChars == null || trimChars.Length == 0)
456                                 end = FindNotWhiteSpace (length - 1, -1, -1);
457                         else
458                                 end = FindNotInTable (length - 1, -1, -1, trimChars);
459
460                         end++;
461                         if (end == length)
462                                 return this;
463
464                         return SubstringUnchecked (0, end);
465                 }
466
467                 private int FindNotWhiteSpace (int pos, int target, int change)
468                 {
469                         while (pos != target) {
470                                 char c = this[pos];
471                                 if (c < 0x85) {
472                                         if (c != 0x20) {
473                                                 if (c < 0x9 || c > 0xD)
474                                                         return pos;
475                                         }
476                                 }
477                                 else {
478                                         if (c != 0xA0 && c != 0xFEFF && c != 0x3000) {
479 #if NET_2_0
480                                                 if (c != 0x85 && c != 0x1680 && c != 0x2028 && c != 0x2029)
481 #endif
482                                                         if (c < 0x2000 || c > 0x200B)
483                                                                 return pos;
484                                         }
485                                 }
486                                 pos += change;
487                         }
488                         return pos;
489                 }
490
491                 private unsafe int FindNotInTable (int pos, int target, int change, char[] table)
492                 {
493                         fixed (char* tablePtr = table, thisPtr = this) {
494                                 while (pos != target) {
495                                         char c = thisPtr[pos];
496                                         int x = 0;
497                                         while (x < table.Length) {
498                                                 if (c == tablePtr[x])
499                                                         break;
500                                                 x++;
501                                         }
502                                         if (x == table.Length)
503                                                 return pos;
504                                         pos += change;
505                                 }
506                         }
507                         return pos;
508                 }
509
510                 public static int Compare (String strA, String strB)
511                 {
512                         return CultureInfo.CurrentCulture.CompareInfo.Compare (strA, strB, CompareOptions.None);
513                 }
514
515                 public static int Compare (String strA, String strB, bool ignoreCase)
516                 {
517                         return CultureInfo.CurrentCulture.CompareInfo.Compare (strA, strB, ignoreCase ? CompareOptions.IgnoreCase : CompareOptions.None);
518                 }
519
520                 public static int Compare (String strA, String strB, bool ignoreCase, CultureInfo culture)
521                 {
522                         if (culture == null)
523                                 throw new ArgumentNullException ("culture");
524
525                         return culture.CompareInfo.Compare (strA, strB, ignoreCase ? CompareOptions.IgnoreCase : CompareOptions.None);
526                 }
527
528                 public static int Compare (String strA, int indexA, String strB, int indexB, int length)
529                 {
530                         return Compare (strA, indexA, strB, indexB, length, false, CultureInfo.CurrentCulture);
531                 }
532
533                 public static int Compare (String strA, int indexA, String strB, int indexB, int length, bool ignoreCase)
534                 {
535                         return Compare (strA, indexA, strB, indexB, length, ignoreCase, CultureInfo.CurrentCulture);
536                 }
537                 
538                 public static int Compare (String strA, int indexA, String strB, int indexB, int length, bool ignoreCase, CultureInfo culture)
539                 {
540                         if (culture == null)
541                                 throw new ArgumentNullException ("culture");
542
543                         if ((indexA > strA.Length) || (indexB > strB.Length) || (indexA < 0) || (indexB < 0) || (length < 0))
544                                 throw new ArgumentOutOfRangeException ();
545
546                         if (length == 0)
547                                 return 0;
548                         
549                         if (strA == null) {
550                                 if (strB == null) {
551                                         return 0;
552                                 } else {
553                                         return -1;
554                                 }
555                         }
556                         else if (strB == null) {
557                                 return 1;
558                         }
559
560                         CompareOptions compopts;
561
562                         if (ignoreCase)
563                                 compopts = CompareOptions.IgnoreCase;
564                         else
565                                 compopts = CompareOptions.None;
566
567                         // Need to cap the requested length to the
568                         // length of the string, because
569                         // CompareInfo.Compare will insist that length
570                         // <= (string.Length - offset)
571
572                         int len1 = length;
573                         int len2 = length;
574                         
575                         if (length > (strA.Length - indexA)) {
576                                 len1 = strA.Length - indexA;
577                         }
578
579                         if (length > (strB.Length - indexB)) {
580                                 len2 = strB.Length - indexB;
581                         }
582
583                         // ENHANCE: Might call internal_compare_switch directly instead of doing all checks twice
584                         return culture.CompareInfo.Compare (strA, indexA, len1, strB, indexB, len2, compopts);
585                 }
586 #if NET_2_0
587                 public static int Compare (string strA, string strB, StringComparison comparisonType)
588                 {
589                         switch (comparisonType) {
590                         case StringComparison.CurrentCulture:
591                                 return Compare (strA, strB, false, CultureInfo.CurrentCulture);
592                         case StringComparison.CurrentCultureIgnoreCase:
593                                 return Compare (strA, strB, true, CultureInfo.CurrentCulture);
594                         case StringComparison.InvariantCulture:
595                                 return Compare (strA, strB, false, CultureInfo.InvariantCulture);
596                         case StringComparison.InvariantCultureIgnoreCase:
597                                 return Compare (strA, strB, true, CultureInfo.InvariantCulture);
598                         case StringComparison.Ordinal:
599                                 return CompareOrdinalUnchecked (strA, 0, Int32.MaxValue, strB, 0, Int32.MaxValue);
600                         case StringComparison.OrdinalIgnoreCase:
601                                 return CompareOrdinalCaseInsensitiveUnchecked (strA, 0, Int32.MaxValue, strB, 0, Int32.MaxValue);
602                         default:
603                                 string msg = Locale.GetText ("Invalid value '{0}' for StringComparison", comparisonType);
604                                 throw new ArgumentException (msg, "comparisonType");
605                         }
606                 }
607
608                 public static int Compare (string strA, int indexA, string strB, int indexB, int length, StringComparison comparisonType)
609                 {
610                         switch (comparisonType) {
611                         case StringComparison.CurrentCulture:
612                                 return Compare (strA, indexA, strB, indexB, length, false, CultureInfo.CurrentCulture);
613                         case StringComparison.CurrentCultureIgnoreCase:
614                                 return Compare (strA, indexA, strB, indexB, length, true, CultureInfo.CurrentCulture);
615                         case StringComparison.InvariantCulture:
616                                 return Compare (strA, indexA, strB, indexB, length, false, CultureInfo.InvariantCulture);
617                         case StringComparison.InvariantCultureIgnoreCase:
618                                 return Compare (strA, indexA, strB, indexB, length, true, CultureInfo.InvariantCulture);
619                         case StringComparison.Ordinal:
620                                 return CompareOrdinal (strA, indexA, strB, indexB, length);
621                         case StringComparison.OrdinalIgnoreCase:
622                                 return CompareOrdinalCaseInsensitive (strA, indexA, strB, indexB, length);
623                         default:
624                                 string msg = Locale.GetText ("Invalid value '{0}' for StringComparison", comparisonType);
625                                 throw new ArgumentException (msg, "comparisonType");
626                         }
627                 }
628
629                 public static bool Equals (string a, string b, StringComparison comparisonType)
630                 {
631                         return String.Compare (a, b, comparisonType) == 0;
632                 }
633
634                 public bool Equals (string value, StringComparison comparisonType)
635                 {
636                         return String.Compare (value, this, comparisonType) == 0;
637                 }
638 #endif
639                 public int CompareTo (Object value)
640                 {
641                         if (value == null)
642                                 return 1;
643
644                         if (!(value is String))
645                                 throw new ArgumentException ();
646
647                         return String.Compare (this, (String) value);
648                 }
649
650                 public int CompareTo (String strB)
651                 {
652                         if (strB == null)
653                                 return 1;
654
655                         return Compare (this, strB);
656                 }
657
658                 public static int CompareOrdinal (String strA, String strB)
659                 {
660                         return CompareOrdinalUnchecked (strA, 0, Int32.MaxValue, strB, 0, Int32.MaxValue);
661                 }
662
663                 public static int CompareOrdinal (String strA, int indexA, String strB, int indexB, int length)
664                 {
665                         if ((indexA > strA.Length) || (indexB > strB.Length) || (indexA < 0) || (indexB < 0) || (length < 0))
666                                 throw new ArgumentOutOfRangeException ();
667
668                         return CompareOrdinalUnchecked (strA, indexA, length, strB, indexB, length);
669                 }
670
671                 internal static int CompareOrdinalCaseInsensitive (String strA, int indexA, String strB, int indexB, int length)
672                 {
673                         if ((indexA > strA.Length) || (indexB > strB.Length) || (indexA < 0) || (indexB < 0) || (length < 0))
674                                 throw new ArgumentOutOfRangeException ();
675
676                         return CompareOrdinalCaseInsensitiveUnchecked (strA, indexA, length, strB, indexB, length);
677                 }
678
679                 internal static unsafe int CompareOrdinalUnchecked (String strA, int indexA, int lenA, String strB, int indexB, int lenB)
680                 {
681                         if (strA == null) {
682                                 if (strB == null)
683                                         return 0;
684                                 else
685                                         return -1;
686                         } else if (strB == null) {
687                                 return 1;
688                         }
689                         int lengthA = Math.Min (lenA, strA.Length - indexA);
690                         int lengthB = Math.Min (lenB, strB.Length - indexB);
691
692                         if (lengthA == lengthB && Object.ReferenceEquals (strA, strB))
693                                 return 0;
694
695                         fixed (char* aptr = strA, bptr = strB) {
696                                 char* ap = aptr + indexA;
697                                 char* end = ap + Math.Min (lengthA, lengthB);
698                                 char* bp = bptr + indexB;
699                                 while (ap < end) {
700                                         if (*ap != *bp)
701                                                 return *ap - *bp;
702                                         ap++;
703                                         bp++;
704                                 }
705                                 return lengthA - lengthB;
706                         }
707                 }
708
709                 internal static unsafe int CompareOrdinalCaseInsensitiveUnchecked (String strA, int indexA, int lenA, String strB, int indexB, int lenB)
710                 {
711                         // Same as above, but checks versus uppercase characters
712                         if (strA == null) {
713                                 if (strB == null)
714                                         return 0;
715                                 else
716                                         return -1;
717                         } else if (strB == null) {
718                                 return 1;
719                         }
720                         int lengthA = Math.Min (lenA, strA.Length - indexA);
721                         int lengthB = Math.Min (lenB, strB.Length - indexB);
722
723                         if (lengthA == lengthB && Object.ReferenceEquals (strA, strB))
724                                 return 0;
725
726                         fixed (char* aptr = strA, bptr = strB) {
727                                 char* ap = aptr + indexA;
728                                 char* end = ap + Math.Min (lengthA, lengthB);
729                                 char* bp = bptr + indexB;
730                                 while (ap < end) {
731                                         if (*ap != *bp) {
732                                                 char c1 = Char.ToUpperInvariant (*ap);
733                                                 char c2 = Char.ToUpperInvariant (*bp);
734                                                 if (c1 != c2)
735                                                         return c1 - c2;
736                                         }
737                                         ap++;
738                                         bp++;
739                                 }
740                                 return lengthA - lengthB;
741                         }
742                 }
743
744                 public bool EndsWith (String value)
745                 {
746                         if (value == null)
747                                 throw new ArgumentNullException ("value");
748
749                         return CultureInfo.CurrentCulture.CompareInfo.IsSuffix (this, value, CompareOptions.None);
750                 }
751
752 #if NET_2_0
753                 public
754 #else
755                 internal
756 #endif
757                 bool EndsWith (String value, bool ignoreCase, CultureInfo culture)
758                 {
759                         if (value == null)
760                                 throw new ArgumentNullException ("value");
761                         if (culture == null)
762                                 culture = CultureInfo.CurrentCulture;
763
764                         return culture.CompareInfo.IsSuffix (this, value,
765                                 ignoreCase ? CompareOptions.IgnoreCase : CompareOptions.None);
766                 }
767
768                 // Following methods are culture-insensitive
769                 public int IndexOfAny (char [] anyOf)
770                 {
771                         if (anyOf == null)
772                                 throw new ArgumentNullException ();
773                         if (this.length == 0)
774                                 return -1;
775
776                         return InternalIndexOfAny (anyOf, 0, this.length);
777                 }
778
779                 public int IndexOfAny (char [] anyOf, int startIndex)
780                 {
781                         if (anyOf == null)
782                                 throw new ArgumentNullException ();
783                         if (startIndex < 0 || startIndex > this.length)
784                                 throw new ArgumentOutOfRangeException ();
785
786                         return InternalIndexOfAny (anyOf, startIndex, this.length - startIndex);
787                 }
788
789                 public int IndexOfAny (char [] anyOf, int startIndex, int count)
790                 {
791                         if (anyOf == null)
792                                 throw new ArgumentNullException ();
793                         if (startIndex < 0 || startIndex > this.length)
794                                 throw new ArgumentOutOfRangeException ();
795                         // re-ordered to avoid possible integer overflow
796                         if (count < 0 || startIndex > this.length - count)
797                                 throw new ArgumentOutOfRangeException ("count", "Count cannot be negative, and startIndex + count must be less than length of the string.");
798
799                         return InternalIndexOfAny (anyOf, startIndex, count);
800                 }
801
802                 unsafe int InternalIndexOfAny (char[] anyOf, int startIndex, int count)
803                 {
804                         if (anyOf.Length == 0)
805                                 return -1;
806
807                         if (anyOf.Length == 1)
808                                 return IndexOfImpl(anyOf[0], startIndex, count);
809
810                         fixed (char* any = anyOf) {
811                                 int highest = *any;
812                                 int lowest = *any;
813
814                                 char* end_any_ptr = any + anyOf.Length;
815                                 char* any_ptr = any;
816                                 while (++any_ptr != end_any_ptr) {
817                                         if (*any_ptr > highest) {
818                                                 highest = *any_ptr;
819                                                 continue;
820                                         }
821
822                                         if (*any_ptr < lowest)
823                                                 lowest = *any_ptr;
824                                 }
825
826                                 fixed (char* start = &start_char) {
827                                         char* ptr = start + startIndex;
828                                         char* end_ptr = ptr + count;
829
830                                         while (ptr != end_ptr) {
831                                                 if (*ptr > highest || *ptr < lowest) {
832                                                         ptr++;
833                                                         continue;
834                                                 }
835
836                                                 if (*ptr == *any)
837                                                         return (int)(ptr - start);
838
839                                                 any_ptr = any;
840                                                 while (++any_ptr != end_any_ptr) {
841                                                         if (*ptr == *any_ptr)
842                                                                 return (int)(ptr - start);
843                                                 }
844
845                                                 ptr++;
846                                         }
847                                 }
848                         }
849                         return -1;
850                 }
851
852
853 #if NET_2_0
854                 public int IndexOf (string value, StringComparison comparison)
855                 {
856                         return IndexOf (value, 0, this.Length, comparison);
857                 }
858
859                 public int IndexOf (string value, int startIndex, StringComparison comparison)
860                 {
861                         return IndexOf (value, startIndex, this.Length - startIndex, comparison);
862                 }
863
864                 public int IndexOf (string value, int startIndex, int count, StringComparison comparison)
865                 {
866                         switch (comparison) {
867                         case StringComparison.CurrentCulture:
868                                 return CultureInfo.CurrentCulture.CompareInfo.IndexOf (this, value, startIndex, count, CompareOptions.None);
869                         case StringComparison.CurrentCultureIgnoreCase:
870                                 return CultureInfo.CurrentCulture.CompareInfo.IndexOf (this, value, startIndex, count, CompareOptions.IgnoreCase);
871                         case StringComparison.InvariantCulture:
872                                 return CultureInfo.InvariantCulture.CompareInfo.IndexOf (this, value, startIndex, count, CompareOptions.None);
873                         case StringComparison.InvariantCultureIgnoreCase:
874                                 return CultureInfo.InvariantCulture.CompareInfo.IndexOf (this, value, startIndex, count, CompareOptions.IgnoreCase);
875                         case StringComparison.Ordinal:
876                                 return CultureInfo.InvariantCulture.CompareInfo.IndexOf (this, value, startIndex, count, CompareOptions.Ordinal);
877                         case StringComparison.OrdinalIgnoreCase:
878                                 return CultureInfo.InvariantCulture.CompareInfo.IndexOf (this, value, startIndex, count, CompareOptions.OrdinalIgnoreCase);
879                         }
880
881                         string msg = Locale.GetText ("Invalid value '{0}' for StringComparison", comparison);
882                         throw new ArgumentException  (msg, "comparisonType");
883                 }
884
885                 public int LastIndexOf (string value, StringComparison comparison)
886                 {
887                         return LastIndexOf (value, value.Length - 1, value.Length, comparison);
888                 }
889
890                 public int LastIndexOf (string value, int startIndex, StringComparison comparison)
891                 {
892                         return LastIndexOf (value, startIndex, startIndex + 1, comparison);
893                 }
894
895                 public int LastIndexOf (string value, int startIndex, int count, StringComparison comparison)
896                 {
897                         switch (comparison) {
898                         case StringComparison.CurrentCulture:
899                                 return CultureInfo.CurrentCulture.CompareInfo.LastIndexOf (this, value, startIndex, count, CompareOptions.None);
900                         case StringComparison.CurrentCultureIgnoreCase:
901                                 return CultureInfo.CurrentCulture.CompareInfo.LastIndexOf (this, value, startIndex, count, CompareOptions.IgnoreCase);
902                         case StringComparison.InvariantCulture:
903                                 return CultureInfo.InvariantCulture.CompareInfo.LastIndexOf (this, value, startIndex, count, CompareOptions.None);
904                         case StringComparison.InvariantCultureIgnoreCase:
905                                 return CultureInfo.InvariantCulture.CompareInfo.LastIndexOf (this, value, startIndex, count, CompareOptions.IgnoreCase);
906                         case StringComparison.Ordinal:
907                                 return CultureInfo.InvariantCulture.CompareInfo.LastIndexOf (this, value, startIndex, count, CompareOptions.Ordinal);
908                         case StringComparison.OrdinalIgnoreCase:
909                                 return CultureInfo.InvariantCulture.CompareInfo.LastIndexOf (this, value, startIndex, count, CompareOptions.OrdinalIgnoreCase);
910                         }
911
912                         string msg = Locale.GetText ("Invalid value '{0}' for StringComparison", comparison);
913                         throw new ArgumentException  (msg, "comparison");
914                 }
915 #endif
916
917                 public int IndexOf (char value)
918                 {
919                         if (this.length == 0)
920                                 return -1;
921
922                         return IndexOfImpl (value, 0, this.length);
923                 }
924
925                 public int IndexOf (String value)
926                 {
927                         return IndexOf (value, 0, this.length);
928                 }
929
930                 public int IndexOf (char value, int startIndex)
931                 {
932                         return IndexOf (value, startIndex, this.length - startIndex);
933                 }
934
935                 public int IndexOf (String value, int startIndex)
936                 {
937                         return IndexOf (value, startIndex, this.length - startIndex);
938                 }
939
940                 /* This method is culture-insensitive */
941                 public int IndexOf (char value, int startIndex, int count)
942                 {
943                         if (startIndex < 0 || startIndex > this.length)
944                                 throw new ArgumentOutOfRangeException ("startIndex", "Cannot be negative and must be< 0");
945                         if (count < 0)
946                                 throw new ArgumentOutOfRangeException ("count", "< 0");
947                         // re-ordered to avoid possible integer overflow
948                         if (startIndex > this.length - count)
949                                 throw new ArgumentOutOfRangeException ("count", "startIndex + count > this.length");
950
951                         if ((startIndex == 0 && this.length == 0) || (startIndex == this.length) || (count == 0))
952                                 return -1;
953
954                         return IndexOfImpl (value, startIndex, count);
955                 }
956
957                 unsafe int IndexOfImpl (char value, int startIndex, int count)
958                 {
959                         // It helps JIT compiler to optimize comparison
960                         int value_32 = (int)value;
961
962                         fixed (char* start = &start_char) {
963                                 char* ptr = start + startIndex;
964                                 char* end_ptr = ptr + (count >> 3 << 3);
965
966                                 while (ptr != end_ptr) {
967                                         if (*ptr == value_32)
968                                                 return (int)(ptr - start);
969                                         if (ptr[1] == value_32)
970                                                 return (int)(ptr - start + 1);
971                                         if (ptr[2] == value_32)
972                                                 return (int)(ptr - start + 2);
973                                         if (ptr[3] == value_32)
974                                                 return (int)(ptr - start + 3);
975                                         if (ptr[4] == value_32)
976                                                 return (int)(ptr - start + 4);
977                                         if (ptr[5] == value_32)
978                                                 return (int)(ptr - start + 5);
979                                         if (ptr[6] == value_32)
980                                                 return (int)(ptr - start + 6);
981                                         if (ptr[7] == value_32)
982                                                 return (int)(ptr - start + 7);
983
984                                         ptr += 8;
985                                 }
986
987                                 end_ptr += count & 0x07;
988                                 while (ptr != end_ptr) {
989                                         if (*ptr == value_32)
990                                                 return (int)(ptr - start);
991
992                                         ptr++;
993                                 }
994                                 return -1;
995                         }
996                 }
997
998                 /* But this one is culture-sensitive */
999                 public int IndexOf (String value, int startIndex, int count)
1000                 {
1001                         if (value == null)
1002 #if NET_2_0
1003                                 throw new ArgumentNullException ("value");
1004 #else
1005                                 throw new ArgumentNullException ("string2");
1006 #endif
1007                         if (startIndex < 0 || startIndex > this.length)
1008                                 throw new ArgumentOutOfRangeException ("startIndex", "Cannot be negative, and should not exceed length of string.");
1009                         if (count < 0 || startIndex > this.length - count)
1010                                 throw new ArgumentOutOfRangeException ("count", "Cannot be negative, and should point to location in string.");
1011
1012                         if (value.length == 0)
1013                                 return startIndex;
1014
1015                         if (startIndex == 0 && this.length == 0)
1016                                 return -1;
1017
1018                         if (count == 0)
1019                                 return -1;
1020
1021                         return CultureInfo.CurrentCulture.CompareInfo.IndexOf (this, value, startIndex, count);
1022                 }
1023
1024                 public int LastIndexOfAny (char [] anyOf)
1025                 {
1026                         if (anyOf == null)
1027                                 throw new ArgumentNullException ();
1028
1029                         return InternalLastIndexOfAny (anyOf, this.length - 1, this.length);
1030                 }
1031
1032                 public int LastIndexOfAny (char [] anyOf, int startIndex)
1033                 {
1034                         if (anyOf == null)
1035                                 throw new ArgumentNullException ();
1036
1037                         if (startIndex < 0 || startIndex >= this.length)
1038                                 throw new ArgumentOutOfRangeException ("startIndex", "Cannot be negative, and should be less than length of string.");
1039
1040                         if (this.length == 0)
1041                                 return -1;
1042
1043                         return InternalLastIndexOfAny (anyOf, startIndex, startIndex + 1);
1044                 }
1045
1046                 public int LastIndexOfAny (char [] anyOf, int startIndex, int count)
1047                 {
1048                         if (anyOf == null) 
1049                                 throw new ArgumentNullException ();
1050
1051                         if ((startIndex < 0) || (startIndex >= this.Length))
1052                                 throw new ArgumentOutOfRangeException ("startIndex", "< 0 || > this.Length");
1053                         if ((count < 0) || (count > this.Length))
1054                                 throw new ArgumentOutOfRangeException ("count", "< 0 || > this.Length");
1055                         if (startIndex - count + 1 < 0)
1056                                 throw new ArgumentOutOfRangeException ("startIndex - count + 1 < 0");
1057
1058                         if (this.length == 0)
1059                                 return -1;
1060
1061                         return InternalLastIndexOfAny (anyOf, startIndex, count);
1062                 }
1063
1064                 public int LastIndexOf (char value)
1065                 {
1066                         if (this.length == 0)
1067                                 return -1;
1068                         
1069                         return LastIndexOfImpl (value, this.length - 1, this.length);
1070                 }
1071
1072                 public int LastIndexOf (String value)
1073                 {
1074                         if (this.length == 0)
1075                                 /* This overload does additional checking */
1076                                 return LastIndexOf (value, 0, 0);
1077                         else
1078                                 return LastIndexOf (value, this.length - 1, this.length);
1079                 }
1080
1081                 public int LastIndexOf (char value, int startIndex)
1082                 {
1083                         return LastIndexOf (value, startIndex, startIndex + 1);
1084                 }
1085
1086                 public int LastIndexOf (String value, int startIndex)
1087                 {
1088                         int max = startIndex;
1089                         if (max < this.Length)
1090                                 max++;
1091                         return LastIndexOf (value, startIndex, max);
1092                 }
1093
1094                 /* This method is culture-insensitive */
1095                 public int LastIndexOf (char value, int startIndex, int count)
1096                 {
1097                         if (startIndex == 0 && this.length == 0)
1098                                 return -1;
1099
1100                         // >= for char (> for string)
1101                         if ((startIndex < 0) || (startIndex >= this.Length))
1102                                 throw new ArgumentOutOfRangeException ("startIndex", "< 0 || >= this.Length");
1103                         if ((count < 0) || (count > this.Length))
1104                                 throw new ArgumentOutOfRangeException ("count", "< 0 || > this.Length");
1105                         if (startIndex - count + 1 < 0)
1106                                 throw new ArgumentOutOfRangeException ("startIndex - count + 1 < 0");
1107
1108                         return LastIndexOfImpl (value, startIndex, count);
1109                 }
1110
1111                 /* This method is culture-insensitive */
1112                 unsafe int LastIndexOfImpl (char value, int startIndex, int count)
1113                 {
1114                         // It helps JIT compiler to optimize comparison
1115                         int value_32 = (int)value;
1116
1117                         fixed (char* start = &start_char) {
1118                                 char* ptr = start + startIndex;
1119                                 char* end_ptr = ptr - (count >> 3 << 3);
1120
1121                                 while (ptr != end_ptr) {
1122                                         if (*ptr == value_32)
1123                                                 return (int)(ptr - start);
1124                                         if (ptr[-1] == value_32)
1125                                                 return (int)(ptr - start) - 1;
1126                                         if (ptr[-2] == value_32)
1127                                                 return (int)(ptr - start) - 2;
1128                                         if (ptr[-3] == value_32)
1129                                                 return (int)(ptr - start) - 3;
1130                                         if (ptr[-4] == value_32)
1131                                                 return (int)(ptr - start) - 4;
1132                                         if (ptr[-5] == value_32)
1133                                                 return (int)(ptr - start) - 5;
1134                                         if (ptr[-6] == value_32)
1135                                                 return (int)(ptr - start) - 6;
1136                                         if (ptr[-7] == value_32)
1137                                                 return (int)(ptr - start) - 7;
1138
1139                                         ptr -= 8;
1140                                 }
1141
1142                                 end_ptr -= count & 0x07;
1143                                 while (ptr != end_ptr) {
1144                                         if (*ptr == value_32)
1145                                                 return (int)(ptr - start);
1146
1147                                         ptr--;
1148                                 }
1149                                 return -1;
1150                         }
1151                 }
1152
1153                 /* But this one is culture-sensitive */
1154                 public int LastIndexOf (String value, int startIndex, int count)
1155                 {
1156                         if (value == null)
1157 #if NET_2_0
1158                                 throw new ArgumentNullException ("value");
1159 #else
1160                                 throw new ArgumentNullException ("string2");
1161 #endif
1162
1163                         // -1 > startIndex > for string (0 > startIndex >= for char)
1164                         if ((startIndex < -1) || (startIndex > this.Length))
1165                                 throw new ArgumentOutOfRangeException ("startIndex", "< 0 || > this.Length");
1166                         if ((count < 0) || (count > this.Length))
1167                                 throw new ArgumentOutOfRangeException ("count", "< 0 || > this.Length");
1168                         if (startIndex - count + 1 < 0)
1169                                 throw new ArgumentOutOfRangeException ("startIndex - count + 1 < 0");
1170
1171                         if (value.Length == 0)
1172                                 return startIndex;
1173
1174                         if (startIndex == 0 && this.length == 0)
1175                                 return -1;
1176
1177                         // This check is needed to match undocumented MS behaviour
1178                         if (this.length == 0 && value.length > 0)
1179                                 return -1;
1180
1181                         if (count == 0)
1182                                 return -1;
1183
1184                         if (startIndex == this.Length)
1185                                 startIndex--;
1186                         return CultureInfo.CurrentCulture.CompareInfo.LastIndexOf (this, value, startIndex, count);
1187                 }
1188
1189 #if NET_2_0
1190                 public bool Contains (String value)
1191                 {
1192                         return IndexOf (value) != -1;
1193                 }
1194
1195                 public static bool IsNullOrEmpty (String value)
1196                 {
1197                         return (value == null) || (value.Length == 0);
1198                 }
1199
1200                 public string Normalize ()
1201                 {
1202                         return Normalization.Normalize (this, 0);
1203                 }
1204
1205                 public string Normalize (NormalizationForm form)
1206                 {
1207                         switch (form) {
1208                         default:
1209                                 return Normalization.Normalize (this, 0);
1210                         case NormalizationForm.FormD:
1211                                 return Normalization.Normalize (this, 1);
1212                         case NormalizationForm.FormKC:
1213                                 return Normalization.Normalize (this, 2);
1214                         case NormalizationForm.FormKD:
1215                                 return Normalization.Normalize (this, 3);
1216                         }
1217                 }
1218
1219                 public bool IsNormalized ()
1220                 {
1221                         return Normalization.IsNormalized (this, 0);
1222                 }
1223
1224                 public bool IsNormalized (NormalizationForm form)
1225                 {
1226                         switch (form) {
1227                         default:
1228                                 return Normalization.IsNormalized (this, 0);
1229                         case NormalizationForm.FormD:
1230                                 return Normalization.IsNormalized (this, 1);
1231                         case NormalizationForm.FormKC:
1232                                 return Normalization.IsNormalized (this, 2);
1233                         case NormalizationForm.FormKD:
1234                                 return Normalization.IsNormalized (this, 3);
1235                         }
1236                 }
1237
1238                 public string Remove (int startIndex)
1239                 {
1240                         if (startIndex < 0)
1241                                 throw new ArgumentOutOfRangeException ("startIndex", "StartIndex can not be less than zero");
1242                         if (startIndex >= this.length)
1243                                 throw new ArgumentOutOfRangeException ("startIndex", "StartIndex must be less than the length of the string");
1244
1245                         return Remove (startIndex, this.length - startIndex);
1246                 }
1247 #endif
1248
1249                 public String PadLeft (int totalWidth)
1250                 {
1251                         return PadLeft (totalWidth, ' ');
1252                 }
1253
1254                 public unsafe String PadLeft (int totalWidth, char paddingChar)
1255                 {
1256                         //LAMESPEC: MSDN Doc says this is reversed for RtL languages, but this seems to be untrue
1257
1258                         if (totalWidth < 0)
1259                                 throw new ArgumentOutOfRangeException ("totalWidth", "< 0");
1260
1261                         if (totalWidth < this.length)
1262                                 return this;
1263
1264                         String tmp = InternalAllocateStr (totalWidth);
1265
1266                         fixed (char* dest = tmp, src = this) {
1267                                 char* padPos = dest;
1268                                 char* padTo = dest + (totalWidth - length);
1269                                 while (padPos != padTo)
1270                                         *padPos++ = paddingChar;
1271
1272                                 CharCopy (padTo, src, length);
1273                         }
1274                         return tmp;
1275                 }
1276
1277                 public String PadRight (int totalWidth)
1278                 {
1279                         return PadRight (totalWidth, ' ');
1280                 }
1281
1282                 public unsafe String PadRight (int totalWidth, char paddingChar)
1283                 {
1284                         //LAMESPEC: MSDN Doc says this is reversed for RtL languages, but this seems to be untrue
1285
1286                         if (totalWidth < 0)
1287                                 throw new ArgumentOutOfRangeException ("totalWidth", "< 0");
1288
1289                         if (totalWidth < this.length)
1290                                 return this;
1291
1292                         String tmp = InternalAllocateStr (totalWidth);
1293
1294                         fixed (char* dest = tmp, src = this) {
1295                                 CharCopy (dest, src, length);
1296
1297                                 char* padPos = dest + length;
1298                                 char* padTo = dest + totalWidth;
1299                                 while (padPos != padTo)
1300                                         *padPos++ = paddingChar;
1301                         }
1302                         return tmp;
1303                 }
1304
1305                 public bool StartsWith (String value)
1306                 {
1307                         if (value == null)
1308                                 throw new ArgumentNullException ("value");
1309
1310                         return CultureInfo.CurrentCulture.CompareInfo.IsPrefix (this, value, CompareOptions.None);
1311                 }
1312
1313 #if NET_2_0
1314                 [ComVisible (false)]
1315                 public bool StartsWith (string value, StringComparison comparisonType)
1316                 {
1317                         if (value == null)
1318                                 throw new ArgumentNullException ("value");
1319
1320                         switch (comparisonType) {
1321                         case StringComparison.CurrentCulture:
1322                                 return CultureInfo.CurrentCulture.CompareInfo.IsPrefix (this, value, CompareOptions.None);
1323                         case StringComparison.CurrentCultureIgnoreCase:
1324                                 return CultureInfo.CurrentCulture.CompareInfo.IsPrefix (this, value, CompareOptions.IgnoreCase);
1325                         case StringComparison.InvariantCulture:
1326                                 return CultureInfo.InvariantCulture.CompareInfo.IsPrefix (this, value, CompareOptions.None);
1327                         case StringComparison.InvariantCultureIgnoreCase:
1328                                 return CultureInfo.InvariantCulture.CompareInfo.IsPrefix (this, value, CompareOptions.IgnoreCase);
1329                         case StringComparison.Ordinal:
1330                                 return CultureInfo.CurrentCulture.CompareInfo.IsPrefix (this, value, CompareOptions.Ordinal);
1331                         case StringComparison.OrdinalIgnoreCase:
1332                                 return CultureInfo.CurrentCulture.CompareInfo.IsPrefix (this, value, CompareOptions.OrdinalIgnoreCase);
1333                         default:
1334                                 string msg = Locale.GetText ("Invalid value '{0}' for StringComparison", comparisonType);
1335                                 throw new ArgumentException (msg, "comparisonType");
1336                         }
1337                 }
1338
1339                 [ComVisible (false)]
1340                 public bool EndsWith (string value, StringComparison comparisonType)
1341                 {
1342                         if (value == null)
1343                                 throw new ArgumentNullException ("value");
1344
1345                         switch (comparisonType) {
1346                         case StringComparison.CurrentCulture:
1347                                 return CultureInfo.CurrentCulture.CompareInfo.IsSuffix (this, value, CompareOptions.None);
1348                         case StringComparison.CurrentCultureIgnoreCase:
1349                                 return CultureInfo.CurrentCulture.CompareInfo.IsSuffix (this, value, CompareOptions.IgnoreCase);
1350                         case StringComparison.InvariantCulture:
1351                                 return CultureInfo.InvariantCulture.CompareInfo.IsSuffix (this, value, CompareOptions.None);
1352                         case StringComparison.InvariantCultureIgnoreCase:
1353                                 return CultureInfo.InvariantCulture.CompareInfo.IsSuffix (this, value, CompareOptions.IgnoreCase);
1354                         case StringComparison.Ordinal:
1355                                 return CultureInfo.CurrentCulture.CompareInfo.IsSuffix (this, value, CompareOptions.Ordinal);
1356                         case StringComparison.OrdinalIgnoreCase:
1357                                 return CultureInfo.CurrentCulture.CompareInfo.IsSuffix (this, value, CompareOptions.OrdinalIgnoreCase);
1358                         default:
1359                                 string msg = Locale.GetText ("Invalid value '{0}' for StringComparison", comparisonType);
1360                                 throw new ArgumentException (msg, "comparisonType");
1361                         }
1362                 }
1363 #endif
1364
1365 #if NET_2_0
1366                 public
1367 #else
1368                 internal
1369 #endif
1370                 bool StartsWith (String value, bool ignoreCase, CultureInfo culture)
1371                 {
1372                         if (culture == null)
1373                                 culture = CultureInfo.CurrentCulture;
1374                         
1375                         return culture.CompareInfo.IsPrefix (this, value, ignoreCase ? CompareOptions.IgnoreCase : CompareOptions.None);
1376                 }
1377
1378                 /* This method is culture insensitive */
1379                 public unsafe String Replace (char oldChar, char newChar)
1380                 {
1381                         if (this.length == 0 || oldChar == newChar)
1382                                 return this;
1383
1384                         int start_pos = IndexOfImpl (oldChar, 0, this.length);
1385                         if (start_pos == -1)
1386                                 return this;
1387
1388                         if (start_pos < 4)
1389                                 start_pos = 0;
1390
1391                         string tmp = InternalAllocateStr(length);
1392                         fixed (char* dest = tmp, src = &start_char) {
1393                                 if (start_pos != 0)
1394                                         memcpy((byte*)dest, (byte*)src, start_pos * 2);
1395
1396                                 char* end_ptr = dest + length;
1397                                 char* dest_ptr = dest + start_pos;
1398                                 char* src_ptr = src + start_pos;
1399
1400                                 while (dest_ptr != end_ptr) {
1401                                         if (*src_ptr == oldChar)
1402                                                 *dest_ptr = newChar;
1403                                         else
1404                                                 *dest_ptr = *src_ptr;
1405
1406                                         ++src_ptr;
1407                                         ++dest_ptr;
1408                                 }
1409                         }
1410                         return tmp;
1411                 }
1412
1413                 /* This method is culture sensitive */
1414                 public String Replace (String oldValue, String newValue)
1415                 {
1416                         if (oldValue == null)
1417                                 throw new ArgumentNullException ("oldValue");
1418
1419                         if (oldValue.Length == 0)
1420                                 throw new ArgumentException ("oldValue is the empty string.");
1421
1422                         if (this.Length == 0)
1423                                 return this;
1424                         
1425                         if (newValue == null)
1426                                 newValue = String.Empty;
1427
1428                         return InternalReplace (oldValue, newValue, CultureInfo.CurrentCulture.CompareInfo);
1429                 }
1430
1431                 public unsafe String Remove (int startIndex, int count)
1432                 {
1433                         if (startIndex < 0)
1434                                 throw new ArgumentOutOfRangeException ("startIndex", "Cannot be negative.");
1435                         if (count < 0)
1436                                 throw new ArgumentOutOfRangeException ("count", "Cannot be negative.");
1437                         if (startIndex > this.length - count)
1438                                 throw new ArgumentOutOfRangeException ("count", "startIndex + count > this.length");
1439
1440                         String tmp = InternalAllocateStr (this.length - count);
1441
1442                         fixed (char *dest = tmp, src = this) {
1443                                 char *dst = dest;
1444                                 CharCopy (dst, src, startIndex);
1445                                 int skip = startIndex + count;
1446                                 dst += startIndex;
1447                                 CharCopy (dst, src + skip, length - skip);
1448                         }
1449                         return tmp;
1450                 }
1451
1452                 public String ToLower ()
1453                 {
1454                         return ToLower (CultureInfo.CurrentCulture);
1455                 }
1456
1457                 public String ToLower (CultureInfo culture)
1458                 {
1459                         if (culture == null)
1460                                 throw new ArgumentNullException ("culture");
1461
1462                         if (culture.LCID == 0x007F) // Invariant
1463                                 return ToLowerInvariant ();
1464
1465                         return culture.TextInfo.ToLower (this);
1466                 }
1467
1468 #if NET_2_0
1469                 public unsafe String ToLowerInvariant ()
1470 #else
1471                 internal unsafe String ToLowerInvariant ()
1472 #endif
1473                 {
1474                         string tmp = InternalAllocateStr (length);
1475                         fixed (char* source = &start_char, dest = tmp) {
1476
1477                                 char* destPtr = (char*)dest;
1478                                 char* sourcePtr = (char*)source;
1479
1480                                 for (int n = 0; n < length; n++) {
1481                                         *destPtr = Char.ToLowerInvariant (*sourcePtr);
1482                                         sourcePtr++;
1483                                         destPtr++;
1484                                 }
1485                         }
1486                         return tmp;
1487                 }
1488
1489                 public String ToUpper ()
1490                 {
1491                         return ToUpper (CultureInfo.CurrentCulture);
1492                 }
1493
1494                 public String ToUpper (CultureInfo culture)
1495                 {
1496                         if (culture == null)
1497                                 throw new ArgumentNullException ("culture");
1498
1499                         if (culture.LCID == 0x007F) // Invariant
1500                                 return ToUpperInvariant ();
1501
1502                         return culture.TextInfo.ToUpper (this);
1503                 }
1504
1505 #if NET_2_0
1506                 public unsafe String ToUpperInvariant ()
1507 #else
1508                 internal unsafe String ToUpperInvariant ()
1509 #endif
1510                 {
1511                         string tmp = InternalAllocateStr (length);
1512                         fixed (char* source = &start_char, dest = tmp) {
1513
1514                                 char* destPtr = (char*)dest;
1515                                 char* sourcePtr = (char*)source;
1516
1517                                 for (int n = 0; n < length; n++) {
1518                                         *destPtr = Char.ToUpperInvariant (*sourcePtr);
1519                                         sourcePtr++;
1520                                         destPtr++;
1521                                 }
1522                         }
1523                         return tmp;
1524                 }
1525
1526                 public override String ToString ()
1527                 {
1528                         return this;
1529                 }
1530
1531                 public String ToString (IFormatProvider provider)
1532                 {
1533                         return this;
1534                 }
1535
1536                 public static String Format (String format, Object arg0)
1537                 {
1538                         return Format (null, format, new Object[] {arg0});
1539                 }
1540
1541                 public static String Format (String format, Object arg0, Object arg1)
1542                 {
1543                         return Format (null, format, new Object[] {arg0, arg1});
1544                 }
1545
1546                 public static String Format (String format, Object arg0, Object arg1, Object arg2)
1547                 {
1548                         return Format (null, format, new Object[] {arg0, arg1, arg2});
1549                 }
1550
1551                 public static string Format (string format, params object[] args)
1552                 {
1553                         return Format (null, format, args);
1554                 }
1555         
1556                 public static string Format (IFormatProvider provider, string format, params object[] args)
1557                 {
1558                         StringBuilder b = new StringBuilder ();
1559                         FormatHelper (b, provider, format, args);
1560                         return b.ToString ();
1561                 }
1562                 
1563                 internal static void FormatHelper (StringBuilder result, IFormatProvider provider, string format, params object[] args)
1564                 {
1565                         if (format == null)
1566                                 throw new ArgumentNullException ("format");
1567                         if (args == null)
1568                                 throw new ArgumentNullException ("args");
1569
1570                         int ptr = 0;
1571                         int start = ptr;
1572                         while (ptr < format.length) {
1573                                 char c = format[ptr ++];
1574
1575                                 if (c == '{') {
1576                                         result.Append (format, start, ptr - start - 1);
1577
1578                                         // check for escaped open bracket
1579
1580                                         if (format[ptr] == '{') {
1581                                                 start = ptr ++;
1582                                                 continue;
1583                                         }
1584
1585                                         // parse specifier
1586                                 
1587                                         int n, width;
1588                                         bool left_align;
1589                                         string arg_format;
1590
1591                                         ParseFormatSpecifier (format, ref ptr, out n, out width, out left_align, out arg_format);
1592                                         if (n >= args.Length)
1593                                                 throw new FormatException ("Index (zero based) must be greater than or equal to zero and less than the size of the argument list.");
1594
1595                                         // format argument
1596
1597                                         object arg = args[n];
1598
1599                                         string str;
1600                                         ICustomFormatter formatter = null;
1601                                         if (provider != null)
1602                                                 formatter = provider.GetFormat (typeof (ICustomFormatter))
1603                                                         as ICustomFormatter;
1604                                         if (arg == null)
1605                                                 str = String.Empty;
1606                                         else if (formatter != null)
1607                                                 str = formatter.Format (arg_format, arg, provider);
1608                                         else if (arg is IFormattable)
1609                                                 str = ((IFormattable)arg).ToString (arg_format, provider);
1610                                         else
1611                                                 str = arg.ToString ();
1612
1613                                         // pad formatted string and append to result
1614
1615                                         if (width > str.length) {
1616                                                 const char padchar = ' ';
1617                                                 int padlen = width - str.length;
1618
1619                                                 if (left_align) {
1620                                                         result.Append (str);
1621                                                         result.Append (padchar, padlen);
1622                                                 }
1623                                                 else {
1624                                                         result.Append (padchar, padlen);
1625                                                         result.Append (str);
1626                                                 }
1627                                         }
1628                                         else
1629                                                 result.Append (str);
1630
1631                                         start = ptr;
1632                                 }
1633                                 else if (c == '}' && ptr < format.length && format[ptr] == '}') {
1634                                         result.Append (format, start, ptr - start - 1);
1635                                         start = ptr ++;
1636                                 }
1637                                 else if (c == '}') {
1638                                         throw new FormatException ("Input string was not in a correct format.");
1639                                 }
1640                         }
1641
1642                         if (start < format.length)
1643                                 result.Append (format, start, format.Length - start);
1644                 }
1645
1646                 public unsafe static String Copy (String str)
1647                 {
1648                         if (str == null)
1649                                 throw new ArgumentNullException ("str");
1650
1651                         int length = str.length;
1652
1653                         String tmp = InternalAllocateStr (length);
1654                         if (length != 0) {
1655                                 fixed (char *dest = tmp, src = str) {
1656                                         CharCopy (dest, src, length);
1657                                 }
1658                         }
1659                         return tmp;
1660                 }
1661
1662                 public static String Concat (Object obj)
1663                 {
1664                         if (obj == null)
1665                                 return String.Empty;
1666
1667                         return obj.ToString ();
1668                 }
1669
1670                 public unsafe static String Concat (Object obj1, Object obj2)
1671                 {
1672                         string s1, s2;
1673
1674                         s1 = (obj1 != null) ? obj1.ToString () : null;
1675                         s2 = (obj2 != null) ? obj2.ToString () : null;
1676                         
1677                         if (s1 == null) {
1678                                 if (s2 == null)
1679                                         return String.Empty;
1680                                 else
1681                                         return s2;
1682                         } else if (s2 == null)
1683                                 return s1;
1684
1685                         String tmp = InternalAllocateStr (s1.Length + s2.Length);
1686                         if (s1.Length != 0) {
1687                                 fixed (char *dest = tmp, src = s1) {
1688                                         CharCopy (dest, src, s1.length);
1689                                 }
1690                         }
1691                         if (s2.Length != 0) {
1692                                 fixed (char *dest = tmp, src = s2) {
1693                                         CharCopy (dest + s1.Length, src, s2.length);
1694                                 }
1695                         }
1696
1697                         return tmp;
1698                 }
1699
1700                 public static String Concat (Object obj1, Object obj2, Object obj3)
1701                 {
1702                         string s1, s2, s3;
1703                         if (obj1 == null)
1704                                 s1 = String.Empty;
1705                         else
1706                                 s1 = obj1.ToString ();
1707
1708                         if (obj2 == null)
1709                                 s2 = String.Empty;
1710                         else
1711                                 s2 = obj2.ToString ();
1712
1713                         if (obj3 == null)
1714                                 s3 = String.Empty;
1715                         else
1716                                 s3 = obj3.ToString ();
1717
1718                         return Concat (s1, s2, s3);
1719                 }
1720
1721 #if ! BOOTSTRAP_WITH_OLDLIB
1722                 [CLSCompliant(false)]
1723                 public static String Concat (Object obj1, Object obj2, Object obj3,
1724                                              Object obj4, __arglist)
1725                 {
1726                         string s1, s2, s3, s4;
1727
1728                         if (obj1 == null)
1729                                 s1 = String.Empty;
1730                         else
1731                                 s1 = obj1.ToString ();
1732
1733                         if (obj2 == null)
1734                                 s2 = String.Empty;
1735                         else
1736                                 s2 = obj2.ToString ();
1737
1738                         if (obj3 == null)
1739                                 s3 = String.Empty;
1740                         else
1741                                 s3 = obj3.ToString ();
1742
1743                         ArgIterator iter = new ArgIterator (__arglist);
1744                         int argCount = iter.GetRemainingCount();
1745
1746                         StringBuilder sb = new StringBuilder ();
1747                         if (obj4 != null)
1748                                 sb.Append (obj4.ToString ());
1749
1750                         for (int i = 0; i < argCount; i++) {
1751                                 TypedReference typedRef = iter.GetNextArg ();
1752                                 sb.Append (TypedReference.ToObject (typedRef));
1753                         }
1754
1755                         s4 = sb.ToString ();
1756
1757                         return Concat (s1, s2, s3, s4);                 
1758                 }
1759 #endif
1760
1761                 public unsafe static String Concat (String s1, String s2)
1762                 {
1763                         if (s1 == null || s1.Length == 0) {
1764                                 if (s2 == null || s2.Length == 0)
1765                                         return String.Empty;
1766                                 return s2;
1767                         }
1768
1769                         if (s2 == null || s2.Length == 0)
1770                                 return s1; 
1771
1772                         String tmp = InternalAllocateStr (s1.length + s2.length);
1773
1774                         fixed (char *dest = tmp, src = s1)
1775                                 CharCopy (dest, src, s1.length);
1776                         fixed (char *dest = tmp, src = s2)
1777                                 CharCopy (dest + s1.Length, src, s2.length);
1778
1779                         return tmp;
1780                 }
1781
1782                 public unsafe static String Concat (String s1, String s2, String s3)
1783                 {
1784                         if (s1 == null || s1.Length == 0){
1785                                 if (s2 == null || s2.Length == 0){
1786                                         if (s3 == null || s3.Length == 0)
1787                                                 return String.Empty;
1788                                         return s3;
1789                                 } else {
1790                                         if (s3 == null || s3.Length == 0)
1791                                                 return s2;
1792                                 }
1793                                 s1 = String.Empty;
1794                         } else {
1795                                 if (s2 == null || s2.Length == 0){
1796                                         if (s3 == null || s3.Length == 0)
1797                                                 return s1;
1798                                         else
1799                                                 s2 = String.Empty;
1800                                 } else {
1801                                         if (s3 == null || s3.Length == 0)
1802                                                 s3 = String.Empty;
1803                                 }
1804                         }
1805
1806                         String tmp = InternalAllocateStr (s1.length + s2.length + s3.length);
1807
1808                         if (s1.Length != 0) {
1809                                 fixed (char *dest = tmp, src = s1) {
1810                                         CharCopy (dest, src, s1.length);
1811                                 }
1812                         }
1813                         if (s2.Length != 0) {
1814                                 fixed (char *dest = tmp, src = s2) {
1815                                         CharCopy (dest + s1.Length, src, s2.length);
1816                                 }
1817                         }
1818                         if (s3.Length != 0) {
1819                                 fixed (char *dest = tmp, src = s3) {
1820                                         CharCopy (dest + s1.Length + s2.Length, src, s3.length);
1821                                 }
1822                         }
1823
1824                         return tmp;
1825                 }
1826
1827                 public unsafe static String Concat (String s1, String s2, String s3, String s4)
1828                 {
1829                         if (s1 == null && s2 == null && s3 == null && s4 == null)
1830                                 return String.Empty;
1831
1832                         if (s1 == null)
1833                                 s1 = String.Empty;
1834                         if (s2 == null)
1835                                 s2 = String.Empty;
1836                         if (s3 == null)
1837                                 s3 = String.Empty;
1838                         if (s4 == null)
1839                                 s4 = String.Empty;
1840
1841                         String tmp = InternalAllocateStr (s1.length + s2.length + s3.length + s4.length);
1842
1843                         if (s1.Length != 0) {
1844                                 fixed (char *dest = tmp, src = s1) {
1845                                         CharCopy (dest, src, s1.length);
1846                                 }
1847                         }
1848                         if (s2.Length != 0) {
1849                                 fixed (char *dest = tmp, src = s2) {
1850                                         CharCopy (dest + s1.Length, src, s2.length);
1851                                 }
1852                         }
1853                         if (s3.Length != 0) {
1854                                 fixed (char *dest = tmp, src = s3) {
1855                                         CharCopy (dest + s1.Length + s2.Length, src, s3.length);
1856                                 }
1857                         }
1858                         if (s4.Length != 0) {
1859                                 fixed (char *dest = tmp, src = s4) {
1860                                         CharCopy (dest + s1.Length + s2.Length + s3.Length, src, s4.length);
1861                                 }
1862                         }
1863
1864                         return tmp;
1865                 }
1866
1867                 public static String Concat (params Object[] args)
1868                 {
1869                         if (args == null)
1870                                 throw new ArgumentNullException ("args");
1871
1872                         int argLen = args.Length;
1873                         if (argLen == 0)
1874                                 return String.Empty;
1875
1876                         string [] strings = new string [argLen];
1877                         int len = 0;
1878                         for (int i = 0; i < argLen; i++) {
1879                                 if (args[i] != null) {
1880                                         strings[i] = args[i].ToString ();
1881                                         len += strings[i].length;
1882                                 }
1883                         }
1884                         if (len == 0)
1885                                 return String.Empty;
1886
1887                         return ConcatInternal (strings, len);
1888                 }
1889
1890                 public static String Concat (params String[] values)
1891                 {
1892                         if (values == null)
1893                                 throw new ArgumentNullException ("values");
1894
1895                         int len = 0;
1896                         for (int i = 0; i < values.Length; i++) {
1897                                 String s = values[i];
1898                                 if (s != null)
1899                                         len += s.length;
1900                         }
1901                         if (len == 0)
1902                                 return String.Empty;
1903
1904                         return ConcatInternal (values, len);
1905                 }
1906
1907                 private static unsafe String ConcatInternal (String[] values, int length)
1908                 {
1909                         String tmp = InternalAllocateStr (length);
1910
1911                         fixed (char* dest = tmp) {
1912                                 int pos = 0;
1913                                 for (int i = 0; i < values.Length; i++) {
1914                                         String source = values[i];
1915                                         if (source != null) {
1916                                                 fixed (char* src = source) {
1917                                                         CharCopy (dest + pos, src, source.length);
1918                                                 }
1919                                                 pos += source.Length;
1920                                         }
1921                                 }
1922                         }
1923                         return tmp;
1924                 }
1925
1926                 public unsafe String Insert (int startIndex, String value)
1927                 {
1928                         if (value == null)
1929                                 throw new ArgumentNullException ("value");
1930
1931                         if (startIndex < 0 || startIndex > this.length)
1932                                 throw new ArgumentOutOfRangeException ("startIndex", "Cannot be negative and must be less than or equal to length of string.");
1933
1934                         if (value.Length == 0)
1935                                 return this;
1936                         if (this.Length == 0)
1937                                 return value;
1938                         String tmp = InternalAllocateStr (this.length + value.length);
1939
1940                         fixed (char *dest = tmp, src = this, val = value) {
1941                                 char *dst = dest;
1942                                 CharCopy (dst, src, startIndex);
1943                                 dst += startIndex;
1944                                 CharCopy (dst, val, value.length);
1945                                 dst += value.length;
1946                                 CharCopy (dst, src + startIndex, length - startIndex);
1947                         }
1948                         return tmp;
1949                 }
1950
1951                 public static string Intern (string str)
1952                 {
1953                         if (str == null)
1954                                 throw new ArgumentNullException ("str");
1955
1956                         return InternalIntern (str);
1957                 }
1958
1959                 public static string IsInterned (string str)
1960                 {
1961                         if (str == null)
1962                                 throw new ArgumentNullException ("str");
1963
1964                         return InternalIsInterned (str);
1965                 }
1966         
1967                 public static string Join (string separator, string [] value)
1968                 {
1969                         if (value == null)
1970                                 throw new ArgumentNullException ("value");
1971                         if (separator == null)
1972                                 separator = String.Empty;
1973
1974                         return JoinUnchecked (separator, value, 0, value.Length);
1975                 }
1976
1977                 public static string Join (string separator, string[] value, int startIndex, int count)
1978                 {
1979                         if (value == null)
1980                                 throw new ArgumentNullException ("value");
1981                         if (startIndex < 0)
1982                                 throw new ArgumentOutOfRangeException ("startIndex", "< 0");
1983                         if (count < 0)
1984                                 throw new ArgumentOutOfRangeException ("count", "< 0");
1985                         if (startIndex > value.Length - count)
1986                                 throw new ArgumentOutOfRangeException ("startIndex", "startIndex + count > value.length");
1987
1988                         if (startIndex == value.Length)
1989                                 return String.Empty;
1990                         if (separator == null)
1991                                 separator = String.Empty;
1992
1993                         return JoinUnchecked (separator, value, startIndex, count);
1994                 }
1995
1996                 private static unsafe string JoinUnchecked (string separator, string[] value, int startIndex, int count)
1997                 {
1998                         // Unchecked parameters
1999                         // startIndex, count must be >= 0; startIndex + count must be <= value.length
2000                         // separator and value must not be null
2001
2002                         int length = 0;
2003                         int maxIndex = startIndex + count;
2004                         // Precount the number of characters that the resulting string will have
2005                         for (int i = startIndex; i < maxIndex; i++) {
2006                                 String s = value[i];
2007                                 if (s != null)
2008                                         length += s.length;
2009                         }
2010                         length += separator.length * (count - 1);
2011                         if (length <= 0)
2012                                 return String.Empty;
2013
2014                         String tmp = InternalAllocateStr (length);
2015
2016                         maxIndex--;
2017                         fixed (char* dest = tmp, sepsrc = separator) {
2018                                 // Copy each string from value except the last one and add a separator for each
2019                                 int pos = 0;
2020                                 for (int i = startIndex; i < maxIndex; i++) {
2021                                         String source = value[i];
2022                                         if (source != null) {
2023                                                 if (source.Length > 0) {
2024                                                         fixed (char* src = source)
2025                                                                 CharCopy (dest + pos, src, source.Length);
2026                                                         pos += source.Length;
2027                                                 }
2028                                         }
2029                                         if (separator.Length > 0) {
2030                                                 CharCopy (dest + pos, sepsrc, separator.Length);
2031                                                 pos += separator.Length;
2032                                         }
2033                                 }
2034                                 // Append last string that does not get an additional separator
2035                                 String sourceLast = value[maxIndex];
2036                                 if (sourceLast != null) {
2037                                         if (sourceLast.Length > 0) {
2038                                                 fixed (char* src = sourceLast)
2039                                                         CharCopy (dest + pos, src, sourceLast.Length);
2040                                         }
2041                                 }
2042                         }
2043                         return tmp;
2044                 }
2045
2046                 bool IConvertible.ToBoolean (IFormatProvider provider)
2047                 {
2048                         return Convert.ToBoolean (this, provider);
2049                 }
2050
2051                 byte IConvertible.ToByte (IFormatProvider provider)
2052                 {
2053                         return Convert.ToByte (this, provider);
2054                 }
2055
2056                 char IConvertible.ToChar (IFormatProvider provider)
2057                 {
2058                         return Convert.ToChar (this, provider);
2059                 }
2060
2061                 DateTime IConvertible.ToDateTime (IFormatProvider provider)
2062                 {
2063                         return Convert.ToDateTime (this, provider);
2064                 }
2065
2066                 decimal IConvertible.ToDecimal (IFormatProvider provider)
2067                 {
2068                         return Convert.ToDecimal (this, provider);
2069                 }
2070
2071                 double IConvertible.ToDouble (IFormatProvider provider)
2072                 {
2073                         return Convert.ToDouble (this, provider);
2074                 }
2075
2076                 short IConvertible.ToInt16 (IFormatProvider provider)
2077                 {
2078                         return Convert.ToInt16 (this, provider);
2079                 }
2080
2081                 int IConvertible.ToInt32 (IFormatProvider provider)
2082                 {
2083                         return Convert.ToInt32 (this, provider);
2084                 }
2085
2086                 long IConvertible.ToInt64 (IFormatProvider provider)
2087                 {
2088                         return Convert.ToInt64 (this, provider);
2089                 }
2090         
2091                 sbyte IConvertible.ToSByte (IFormatProvider provider)
2092                 {
2093                         return Convert.ToSByte (this, provider);
2094                 }
2095
2096                 float IConvertible.ToSingle (IFormatProvider provider)
2097                 {
2098                         return Convert.ToSingle (this, provider);
2099                 }
2100
2101                 object IConvertible.ToType (Type conversionType, IFormatProvider provider)
2102                 {
2103                         return Convert.ToType (this, conversionType,  provider);
2104                 }
2105
2106                 ushort IConvertible.ToUInt16 (IFormatProvider provider)
2107                 {
2108                         return Convert.ToUInt16 (this, provider);
2109                 }
2110
2111                 uint IConvertible.ToUInt32 (IFormatProvider provider)
2112                 {
2113                         return Convert.ToUInt32 (this, provider);
2114                 }
2115
2116                 ulong IConvertible.ToUInt64 (IFormatProvider provider)
2117                 {
2118                         return Convert.ToUInt64 (this, provider);
2119                 }
2120
2121                 public int Length {
2122                         get {
2123                                 return length;
2124                         }
2125                 }
2126
2127                 public CharEnumerator GetEnumerator ()
2128                 {
2129                         return new CharEnumerator (this);
2130                 }
2131
2132 #if NET_2_0
2133                 IEnumerator<char> IEnumerable<char>.GetEnumerator ()
2134                 {
2135                         return new CharEnumerator (this);
2136                 }
2137 #endif
2138
2139                 IEnumerator IEnumerable.GetEnumerator ()
2140                 {
2141                         return new CharEnumerator (this);
2142                 }
2143
2144                 private static void ParseFormatSpecifier (string str, ref int ptr, out int n, out int width,
2145                                                           out bool left_align, out string format)
2146                 {
2147                         // parses format specifier of form:
2148                         //   N,[\ +[-]M][:F]}
2149                         //
2150                         // where:
2151
2152                         try {
2153                                 // N = argument number (non-negative integer)
2154
2155                                 n = ParseDecimal (str, ref ptr);
2156                                 if (n < 0)
2157                                         throw new FormatException ("Input string was not in a correct format.");
2158
2159                                 // M = width (non-negative integer)
2160
2161                                 if (str[ptr] == ',') {
2162                                         // White space between ',' and number or sign.
2163                                         ++ptr;
2164                                         while (Char.IsWhiteSpace (str [ptr]))
2165                                                 ++ptr;
2166                                         int start = ptr;
2167
2168                                         format = str.Substring (start, ptr - start);
2169
2170                                         left_align = (str [ptr] == '-');
2171                                         if (left_align)
2172                                                 ++ ptr;
2173
2174                                         width = ParseDecimal (str, ref ptr);
2175                                         if (width < 0)
2176                                                 throw new FormatException ("Input string was not in a correct format.");
2177                                 }
2178                                 else {
2179                                         width = 0;
2180                                         left_align = false;
2181                                         format = String.Empty;
2182                                 }
2183
2184                                 // F = argument format (string)
2185
2186                                 if (str[ptr] == ':') {
2187                                         int start = ++ ptr;
2188                                         while (str[ptr] != '}')
2189                                                 ++ ptr;
2190
2191                                         format += str.Substring (start, ptr - start);
2192                                 }
2193                                 else
2194                                         format = null;
2195
2196                                 if (str[ptr ++] != '}')
2197                                         throw new FormatException ("Input string was not in a correct format.");
2198                         }
2199                         catch (IndexOutOfRangeException) {
2200                                 throw new FormatException ("Input string was not in a correct format.");
2201                         }
2202                 }
2203
2204                 private static int ParseDecimal (string str, ref int ptr)
2205                 {
2206                         int p = ptr;
2207                         int n = 0;
2208                         while (true) {
2209                                 char c = str[p];
2210                                 if (c < '0' || '9' < c)
2211                                         break;
2212
2213                                 n = n * 10 + c - '0';
2214                                 ++ p;
2215                         }
2216
2217                         if (p == ptr)
2218                                 return -1;
2219
2220                         ptr = p;
2221                         return n;
2222                 }
2223
2224                 internal unsafe void InternalSetChar (int idx, char val)
2225                 {
2226                         if ((uint) idx >= (uint) Length)
2227                                 throw new ArgumentOutOfRangeException ("idx");
2228
2229                         fixed (char * pStr = &start_char) 
2230                         {
2231                                 pStr [idx] = val;
2232                         }
2233                 }
2234
2235                 internal unsafe void InternalSetLength (int newLength)
2236                 {
2237                         if (newLength > length)
2238                                 throw new ArgumentOutOfRangeException ("newLength", "newLength as to be <= length");
2239
2240                         // zero terminate, we can pass string objects directly via pinvoke
2241                         // we also zero the rest of the string, since the new GC needs to be
2242                         // able to handle the changing size (it will skip the 0 bytes).
2243                         fixed (char * pStr = &start_char) {
2244                                 char *p = pStr + newLength;
2245                                 char *end = pStr + length;
2246                                 while (p < end) {
2247                                         p [0] = '\0';
2248                                         p++;
2249                                 }
2250                         }
2251                         length = newLength;
2252                 }
2253
2254 #if NET_2_0
2255                 [ReliabilityContractAttribute (Consistency.WillNotCorruptState, Cer.MayFail)]
2256 #endif
2257                 // When modifying it, GetCaseInsensitiveHashCode() should be modified as well.
2258                 public unsafe override int GetHashCode ()
2259                 {
2260                         fixed (char * c = this) {
2261                                 char * cc = c;
2262                                 char * end = cc + length - 1;
2263                                 int h = 0;
2264                                 for (;cc < end; cc += 2) {
2265                                         h = (h << 5) - h + *cc;
2266                                         h = (h << 5) - h + cc [1];
2267                                 }
2268                                 ++end;
2269                                 if (cc < end)
2270                                         h = (h << 5) - h + *cc;
2271                                 return h;
2272                         }
2273                 }
2274
2275                 internal unsafe int GetCaseInsensitiveHashCode ()
2276                 {
2277                         fixed (char * c = this) {
2278                                 char * cc = c;
2279                                 char * end = cc + length - 1;
2280                                 int h = 0;
2281                                 for (;cc < end; cc += 2) {
2282                                         h = (h << 5) - h + Char.ToUpperInvariant (*cc);
2283                                         h = (h << 5) - h + Char.ToUpperInvariant (cc [1]);
2284                                 }
2285                                 ++end;
2286                                 if (cc < end)
2287                                         h = (h << 5) - h + Char.ToUpperInvariant (*cc);
2288                                 return h;
2289                         }
2290                 }
2291
2292                 // Certain constructors are redirected to CreateString methods with
2293                 // matching argument list. The this pointer should not be used.
2294
2295                 private unsafe String CreateString (sbyte* value)
2296                 {
2297                         if (value == null)
2298                                 return String.Empty;
2299
2300                         byte* bytes = (byte*) value;
2301                         int length = 0;
2302
2303                         try {
2304                                 while (bytes++ [0] != 0)
2305                                         length++;
2306                         } catch (NullReferenceException) {
2307                                 throw new ArgumentOutOfRangeException ("ptr", "Value does not refer to a valid string.");
2308 #if NET_2_0
2309                         } catch (AccessViolationException) {
2310                                 throw new ArgumentOutOfRangeException ("ptr", "Value does not refer to a valid string.");
2311 #endif
2312                         }
2313
2314                         return CreateString (value, 0, length, null);
2315                 }
2316
2317                 private unsafe String CreateString (sbyte* value, int startIndex, int length)
2318                 {
2319                         return CreateString (value, startIndex, length, null);
2320                 }
2321
2322                 private unsafe String CreateString (sbyte* value, int startIndex, int length, Encoding enc)
2323                 {
2324                         if (length < 0)
2325                                 throw new ArgumentOutOfRangeException ("length", "Non-negative number required.");
2326                         if (startIndex < 0)
2327                                 throw new ArgumentOutOfRangeException ("startIndex", "Non-negative number required.");
2328                         if (value + startIndex < value)
2329                                 throw new ArgumentOutOfRangeException ("startIndex", "Value, startIndex and length do not refer to a valid string.");
2330
2331                         bool isDefaultEncoding;
2332
2333                         if (isDefaultEncoding = (enc == null)) {
2334 #if NET_2_0
2335                                 if (value == null)
2336                                         throw new ArgumentNullException ("value");
2337                                 if (length == 0)
2338 #else
2339                                 if (value == null || length == 0)
2340 #endif
2341                                         return String.Empty;
2342
2343                                 enc = Encoding.Default;
2344                         }
2345
2346                         byte [] bytes = new byte [length];
2347
2348                         if (length != 0)
2349                                 fixed (byte* bytePtr = bytes)
2350                                         try {
2351                                                 memcpy (bytePtr, (byte*) (value + startIndex), length);
2352                                         } catch (NullReferenceException) {
2353 #if !NET_2_0
2354                                                 if (!isDefaultEncoding)
2355                                                         throw;
2356 #endif
2357
2358                                                 throw new ArgumentOutOfRangeException ("ptr", "Value, startIndex and length do not refer to a valid string.");
2359 #if NET_2_0
2360                                         } catch (AccessViolationException) {
2361                                                 if (!isDefaultEncoding)
2362                                                         throw;
2363
2364                                                 throw new ArgumentOutOfRangeException ("value", "Value, startIndex and length do not refer to a valid string.");
2365 #endif
2366                                         }
2367
2368                         // GetString () is called even when length == 0
2369                         return enc.GetString (bytes);
2370                 }
2371
2372                 unsafe string CreateString (char *value)
2373                 {
2374                         if (value == null)
2375                                 return string.Empty;
2376                         char *p = value;
2377                         int i = 0;
2378                         while (*p != 0) {
2379                                 ++i;
2380                                 ++p;
2381                         }
2382                         string result = InternalAllocateStr (i);
2383
2384                         if (i != 0) {
2385                                 fixed (char *dest = result) {
2386                                         CharCopy (dest, value, i);
2387                                 }
2388                         }
2389                         return result;
2390                 }
2391
2392                 unsafe string CreateString (char *value, int startIndex, int length)
2393                 {
2394                         if (length == 0)
2395                                 return string.Empty;
2396                         if (value == null)
2397                                 throw new ArgumentNullException ("value");
2398                         if (startIndex < 0)
2399                                 throw new ArgumentOutOfRangeException ("startIndex");
2400                         if (length < 0)
2401                                 throw new ArgumentOutOfRangeException ("length");
2402
2403                         string result = InternalAllocateStr (length);
2404
2405                         fixed (char *dest = result) {
2406                                 CharCopy (dest, value + startIndex, length);
2407                         }
2408                         return result;
2409                 }
2410
2411                 unsafe string CreateString (char [] val, int startIndex, int length)
2412                 {
2413                         if (val == null)
2414                                 throw new ArgumentNullException ("value");
2415                         if (startIndex < 0)
2416                                 throw new ArgumentOutOfRangeException ("startIndex", "Cannot be negative.");
2417                         if (length < 0)
2418                                 throw new ArgumentOutOfRangeException ("length", "Cannot be negative.");
2419                         if (startIndex > val.Length - length)
2420                                 throw new ArgumentOutOfRangeException ("startIndex", "Cannot be negative, and should be less than length of string.");
2421                         if (length == 0)
2422                                 return string.Empty;
2423
2424                         string result = InternalAllocateStr (length);
2425
2426                         fixed (char *dest = result, src = val) {
2427                                 CharCopy (dest, src + startIndex, length);
2428                         }
2429                         return result;
2430                 }
2431
2432                 unsafe string CreateString (char [] val)
2433                 {
2434                         if (val == null)
2435                                 return string.Empty;
2436                         if (val.Length == 0)
2437                                 return string.Empty;
2438                         string result = InternalAllocateStr (val.Length);
2439
2440                         fixed (char *dest = result, src = val) {
2441                                 CharCopy (dest, src, val.Length);
2442                         }
2443                         return result;
2444                 }
2445
2446                 unsafe string CreateString (char c, int count)
2447                 {
2448                         if (count < 0)
2449                                 throw new ArgumentOutOfRangeException ("count");
2450                         if (count == 0)
2451                                 return string.Empty;
2452                         string result = InternalAllocateStr (count);
2453                         fixed (char *dest = result) {
2454                                 char *p = dest;
2455                                 char *end = p + count;
2456                                 while (p < end) {
2457                                         *p = c;
2458                                         p++;
2459                                 }
2460                         }
2461                         return result;
2462                 }
2463
2464                 /* helpers used by the runtime as well as above or eslewhere in corlib */
2465                 internal static unsafe void memset (byte *dest, int val, int len)
2466                 {
2467                         if (len < 8) {
2468                                 while (len != 0) {
2469                                         *dest = (byte)val;
2470                                         ++dest;
2471                                         --len;
2472                                 }
2473                                 return;
2474                         }
2475                         if (val != 0) {
2476                                 val = val | (val << 8);
2477                                 val = val | (val << 16);
2478                         }
2479                         // align to 4
2480                         int rest = (int)dest & 3;
2481                         if (rest != 0) {
2482                                 rest = 4 - rest;
2483                                 len -= rest;
2484                                 do {
2485                                         *dest = (byte)val;
2486                                         ++dest;
2487                                         --rest;
2488                                 } while (rest != 0);
2489                         }
2490                         while (len >= 16) {
2491                                 ((int*)dest) [0] = val;
2492                                 ((int*)dest) [1] = val;
2493                                 ((int*)dest) [2] = val;
2494                                 ((int*)dest) [3] = val;
2495                                 dest += 16;
2496                                 len -= 16;
2497                         }
2498                         while (len >= 4) {
2499                                 ((int*)dest) [0] = val;
2500                                 dest += 4;
2501                                 len -= 4;
2502                         }
2503                         // tail bytes
2504                         while (len > 0) {
2505                                 *dest = (byte)val;
2506                                 dest++;
2507                                 len--;
2508                         }
2509                 }
2510
2511                 static unsafe void memcpy4 (byte *dest, byte *src, int size) {
2512                         /*while (size >= 32) {
2513                                 // using long is better than int and slower than double
2514                                 // FIXME: enable this only on correct alignment or on platforms
2515                                 // that can tolerate unaligned reads/writes of doubles
2516                                 ((double*)dest) [0] = ((double*)src) [0];
2517                                 ((double*)dest) [1] = ((double*)src) [1];
2518                                 ((double*)dest) [2] = ((double*)src) [2];
2519                                 ((double*)dest) [3] = ((double*)src) [3];
2520                                 dest += 32;
2521                                 src += 32;
2522                                 size -= 32;
2523                         }*/
2524                         while (size >= 16) {
2525                                 ((int*)dest) [0] = ((int*)src) [0];
2526                                 ((int*)dest) [1] = ((int*)src) [1];
2527                                 ((int*)dest) [2] = ((int*)src) [2];
2528                                 ((int*)dest) [3] = ((int*)src) [3];
2529                                 dest += 16;
2530                                 src += 16;
2531                                 size -= 16;
2532                         }
2533                         while (size >= 4) {
2534                                 ((int*)dest) [0] = ((int*)src) [0];
2535                                 dest += 4;
2536                                 src += 4;
2537                                 size -= 4;
2538                         }
2539                         while (size > 0) {
2540                                 ((byte*)dest) [0] = ((byte*)src) [0];
2541                                 dest += 1;
2542                                 src += 1;
2543                                 --size;
2544                         }
2545                 }
2546                 static unsafe void memcpy2 (byte *dest, byte *src, int size) {
2547                         while (size >= 8) {
2548                                 ((short*)dest) [0] = ((short*)src) [0];
2549                                 ((short*)dest) [1] = ((short*)src) [1];
2550                                 ((short*)dest) [2] = ((short*)src) [2];
2551                                 ((short*)dest) [3] = ((short*)src) [3];
2552                                 dest += 8;
2553                                 src += 8;
2554                                 size -= 8;
2555                         }
2556                         while (size >= 2) {
2557                                 ((short*)dest) [0] = ((short*)src) [0];
2558                                 dest += 2;
2559                                 src += 2;
2560                                 size -= 2;
2561                         }
2562                         if (size > 0)
2563                                 ((byte*)dest) [0] = ((byte*)src) [0];
2564                 }
2565                 static unsafe void memcpy1 (byte *dest, byte *src, int size) {
2566                         while (size >= 8) {
2567                                 ((byte*)dest) [0] = ((byte*)src) [0];
2568                                 ((byte*)dest) [1] = ((byte*)src) [1];
2569                                 ((byte*)dest) [2] = ((byte*)src) [2];
2570                                 ((byte*)dest) [3] = ((byte*)src) [3];
2571                                 ((byte*)dest) [4] = ((byte*)src) [4];
2572                                 ((byte*)dest) [5] = ((byte*)src) [5];
2573                                 ((byte*)dest) [6] = ((byte*)src) [6];
2574                                 ((byte*)dest) [7] = ((byte*)src) [7];
2575                                 dest += 8;
2576                                 src += 8;
2577                                 size -= 8;
2578                         }
2579                         while (size >= 2) {
2580                                 ((byte*)dest) [0] = ((byte*)src) [0];
2581                                 ((byte*)dest) [1] = ((byte*)src) [1];
2582                                 dest += 2;
2583                                 src += 2;
2584                                 size -= 2;
2585                         }
2586                         if (size > 0)
2587                                 ((byte*)dest) [0] = ((byte*)src) [0];
2588                 }
2589
2590                 internal static unsafe void memcpy (byte *dest, byte *src, int size) {
2591                         // FIXME: if pointers are not aligned, try to align them
2592                         // so a faster routine can be used. Handle the case where
2593                         // the pointers can't be reduced to have the same alignment
2594                         // (just ignore the issue on x86?)
2595                         if ((((int)dest | (int)src) & 3) != 0) {
2596                                 if (((int)dest & 1) != 0 && ((int)src & 1) != 0 && size >= 1) {
2597                                         dest [0] = src [0];
2598                                         ++dest;
2599                                         ++src;
2600                                         --size;
2601                                 }
2602                                 if (((int)dest & 2) != 0 && ((int)src & 2) != 0 && size >= 2) {
2603                                         ((short*)dest) [0] = ((short*)src) [0];
2604                                         dest += 2;
2605                                         src += 2;
2606                                         size -= 2;
2607                                 }
2608                                 if ((((int)dest | (int)src) & 1) != 0) {
2609                                         memcpy1 (dest, src, size);
2610                                         return;
2611                                 }
2612                                 if ((((int)dest | (int)src) & 2) != 0) {
2613                                         memcpy2 (dest, src, size);
2614                                         return;
2615                                 }
2616                         }
2617                         memcpy4 (dest, src, size);
2618                 }
2619
2620                 internal static unsafe void CharCopy (char *dest, char *src, int count) {
2621                         // Same rules as for memcpy, but with the premise that 
2622                         // chars can only be aligned to even addresses if their
2623                         // enclosing types are correctly aligned
2624                         if ((((int)(byte*)dest | (int)(byte*)src) & 3) != 0) {
2625                                 if (((int)(byte*)dest & 2) != 0 && ((int)(byte*)src & 2) != 0 && count > 0) {
2626                                         ((short*)dest) [0] = ((short*)src) [0];
2627                                         dest++;
2628                                         src++;
2629                                         count--;
2630                                 }
2631                                 if ((((int)(byte*)dest | (int)(byte*)src) & 2) != 0) {
2632                                         memcpy2 ((byte*)dest, (byte*)src, count * 2);
2633                                         return;
2634                                 }
2635                         }
2636                         memcpy4 ((byte*)dest, (byte*)src, count * 2);
2637                 }
2638
2639                 internal static unsafe void CharCopyReverse (char *dest, char *src, int count)
2640                 {
2641                         dest += count;
2642                         src += count;
2643                         for (int i = count; i > 0; i--) {
2644                                 dest--;
2645                                 src--;
2646                                 *dest = *src;
2647                         }       
2648                 }
2649
2650                 internal static unsafe void CharCopy (String target, int targetIndex, String source, int sourceIndex, int count)
2651                 {
2652                         fixed (char* dest = target, src = source)
2653                                 CharCopy (dest + targetIndex, src + sourceIndex, count);
2654                 }
2655
2656                 internal static unsafe void CharCopy (String target, int targetIndex, Char[] source, int sourceIndex, int count)
2657                 {
2658                         fixed (char* dest = target, src = source)
2659                                 CharCopy (dest + targetIndex, src + sourceIndex, count);
2660                 }
2661
2662                 // Use this method if you cannot block copy from left to right (e.g. because you are coping within the same string)
2663                 internal static unsafe void CharCopyReverse (String target, int targetIndex, String source, int sourceIndex, int count)
2664                 {
2665                         fixed (char* dest = target, src = source)
2666                                 CharCopyReverse (dest + targetIndex, src + sourceIndex, count);
2667                 }
2668
2669                 [CLSCompliant (false), MethodImplAttribute (MethodImplOptions.InternalCall)]
2670                 unsafe public extern String (char *value);
2671
2672                 [CLSCompliant (false), MethodImplAttribute (MethodImplOptions.InternalCall)]
2673                 unsafe public extern String (char *value, int startIndex, int length);
2674
2675                 [CLSCompliant (false), MethodImplAttribute (MethodImplOptions.InternalCall)]
2676                 unsafe public extern String (sbyte *value);
2677
2678                 [CLSCompliant (false), MethodImplAttribute (MethodImplOptions.InternalCall)]
2679                 unsafe public extern String (sbyte *value, int startIndex, int length);
2680
2681                 [CLSCompliant (false), MethodImplAttribute (MethodImplOptions.InternalCall)]
2682                 unsafe public extern String (sbyte *value, int startIndex, int length, Encoding enc);
2683
2684                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
2685                 public extern String (char [] val, int startIndex, int length);
2686
2687                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
2688                 public extern String (char [] val);
2689
2690                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
2691                 public extern String (char c, int count);
2692
2693                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
2694                 private extern static string InternalJoin (string separator, string[] value, int sIndex, int count);
2695
2696                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
2697                 private extern String InternalReplace (String oldValue, string newValue, CompareInfo comp);
2698
2699                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
2700                 private extern void InternalCopyTo (int sIndex, char[] dest, int destIndex, int count);
2701
2702                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
2703                 private extern String[] InternalSplit (char[] separator, int count, int options);
2704
2705                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
2706                 private extern String InternalTrim (char[] chars, int typ);
2707
2708                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
2709                 private extern int InternalLastIndexOfAny (char [] anyOf, int sIndex, int count);
2710
2711                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
2712                 private extern String InternalPad (int width, char chr, bool right);
2713
2714                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
2715                 internal extern static String InternalAllocateStr (int length);
2716
2717                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
2718                 internal extern static void InternalStrcpy (String dest, int destPos, String src);
2719
2720                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
2721                 internal extern static void InternalStrcpy (String dest, int destPos, char[] chars);
2722
2723                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
2724                 internal extern static void InternalStrcpy (String dest, int destPos, String src, int sPos, int count);
2725
2726                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
2727                 internal extern static void InternalStrcpy (String dest, int destPos, char[] chars, int sPos, int count);
2728
2729                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
2730                 private extern static string InternalIntern (string str);
2731
2732                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
2733                 private extern static string InternalIsInterned (string str);
2734         }
2735 }