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