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