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