minor fix for bug 9520:
[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.Ordinal);
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                         return IndexOf (value) != -1;
1520                 }
1521
1522                 public static bool IsNullOrEmpty (String value)
1523                 {
1524                         return (value == null) || (value.Length == 0);
1525                 }
1526
1527                 public string Normalize ()
1528                 {
1529                         return Normalization.Normalize (this, 0);
1530                 }
1531
1532                 public string Normalize (NormalizationForm normalizationForm)
1533                 {
1534                         switch (normalizationForm) {
1535                         default:
1536                                 return Normalization.Normalize (this, 0);
1537                         case NormalizationForm.FormD:
1538                                 return Normalization.Normalize (this, 1);
1539                         case NormalizationForm.FormKC:
1540                                 return Normalization.Normalize (this, 2);
1541                         case NormalizationForm.FormKD:
1542                                 return Normalization.Normalize (this, 3);
1543                         }
1544                 }
1545
1546                 public bool IsNormalized ()
1547                 {
1548                         return Normalization.IsNormalized (this, 0);
1549                 }
1550
1551                 public bool IsNormalized (NormalizationForm normalizationForm)
1552                 {
1553                         switch (normalizationForm) {
1554                         default:
1555                                 return Normalization.IsNormalized (this, 0);
1556                         case NormalizationForm.FormD:
1557                                 return Normalization.IsNormalized (this, 1);
1558                         case NormalizationForm.FormKC:
1559                                 return Normalization.IsNormalized (this, 2);
1560                         case NormalizationForm.FormKD:
1561                                 return Normalization.IsNormalized (this, 3);
1562                         }
1563                 }
1564
1565                 public string Remove (int startIndex)
1566                 {
1567                         if (startIndex < 0)
1568                                 throw new ArgumentOutOfRangeException ("startIndex", "StartIndex can not be less than zero");
1569                         if (startIndex >= this.length)
1570                                 throw new ArgumentOutOfRangeException ("startIndex", "StartIndex must be less than the length of the string");
1571
1572                         return Remove (startIndex, this.length - startIndex);
1573                 }
1574
1575                 public String PadLeft (int totalWidth)
1576                 {
1577                         return PadLeft (totalWidth, ' ');
1578                 }
1579
1580                 public unsafe String PadLeft (int totalWidth, char paddingChar)
1581                 {
1582                         //LAMESPEC: MSDN Doc says this is reversed for RtL languages, but this seems to be untrue
1583
1584                         if (totalWidth < 0)
1585                                 throw new ArgumentOutOfRangeException ("totalWidth", "< 0");
1586
1587                         if (totalWidth < this.length)
1588                                 return this;
1589                         if (totalWidth == 0)
1590                                 return Empty;
1591
1592                         String tmp = InternalAllocateStr (totalWidth);
1593
1594                         fixed (char* dest = tmp, src = this) {
1595                                 char* padPos = dest;
1596                                 char* padTo = dest + (totalWidth - length);
1597                                 while (padPos != padTo)
1598                                         *padPos++ = paddingChar;
1599
1600                                 CharCopy (padTo, src, length);
1601                         }
1602                         return tmp;
1603                 }
1604
1605                 public String PadRight (int totalWidth)
1606                 {
1607                         return PadRight (totalWidth, ' ');
1608                 }
1609
1610                 public unsafe String PadRight (int totalWidth, char paddingChar)
1611                 {
1612                         //LAMESPEC: MSDN Doc says this is reversed for RtL languages, but this seems to be untrue
1613
1614                         if (totalWidth < 0)
1615                                 throw new ArgumentOutOfRangeException ("totalWidth", "< 0");
1616
1617                         if (totalWidth < this.length)
1618                                 return this;
1619                         if (totalWidth == 0)
1620                                 return Empty;
1621
1622                         String tmp = InternalAllocateStr (totalWidth);
1623
1624                         fixed (char* dest = tmp, src = this) {
1625                                 CharCopy (dest, src, length);
1626
1627                                 char* padPos = dest + length;
1628                                 char* padTo = dest + totalWidth;
1629                                 while (padPos != padTo)
1630                                         *padPos++ = paddingChar;
1631                         }
1632                         return tmp;
1633                 }
1634
1635                 public bool StartsWith (String value)
1636                 {
1637                         if (value == null)
1638                                 throw new ArgumentNullException ("value");
1639
1640                         return CultureInfo.CurrentCulture.CompareInfo.IsPrefix (this, value, CompareOptions.None);
1641                 }
1642
1643                 [ComVisible (false)]
1644                 public bool StartsWith (string value, StringComparison comparisonType)
1645                 {
1646                         if (value == null)
1647                                 throw new ArgumentNullException ("value");
1648
1649                         switch (comparisonType) {
1650                         case StringComparison.CurrentCulture:
1651                                 return CultureInfo.CurrentCulture.CompareInfo.IsPrefix (this, value, CompareOptions.None);
1652                         case StringComparison.CurrentCultureIgnoreCase:
1653                                 return CultureInfo.CurrentCulture.CompareInfo.IsPrefix (this, value, CompareOptions.IgnoreCase);
1654                         case StringComparison.InvariantCulture:
1655                                 return CultureInfo.InvariantCulture.CompareInfo.IsPrefix (this, value, CompareOptions.None);
1656                         case StringComparison.InvariantCultureIgnoreCase:
1657                                 return CultureInfo.InvariantCulture.CompareInfo.IsPrefix (this, value, CompareOptions.IgnoreCase);
1658                         case StringComparison.Ordinal:
1659                                 return StartsWithOrdinalUnchecked (value);
1660                         case StringComparison.OrdinalIgnoreCase:
1661                                 return StartsWithOrdinalCaseInsensitiveUnchecked (value);
1662                         default:
1663                                 string msg = Locale.GetText ("Invalid value '{0}' for StringComparison", comparisonType);
1664                                 throw new ArgumentException (msg, "comparisonType");
1665                         }
1666                 }
1667
1668                 internal bool StartsWithOrdinalUnchecked (string value)
1669                 {
1670                         return length >= value.length && CompareOrdinalUnchecked (this, 0, value.length, value, 0, value.length) == 0;
1671                 }
1672
1673                 internal bool StartsWithOrdinalCaseInsensitiveUnchecked (string value)
1674                 {
1675                         return length >= value.Length && CompareOrdinalCaseInsensitiveUnchecked (this, 0, value.length, value, 0, value.length) == 0;
1676                 }
1677
1678                 [ComVisible (false)]
1679                 public bool EndsWith (string value, StringComparison comparisonType)
1680                 {
1681                         if (value == null)
1682                                 throw new ArgumentNullException ("value");
1683
1684                         switch (comparisonType) {
1685                         case StringComparison.CurrentCulture:
1686                                 return CultureInfo.CurrentCulture.CompareInfo.IsSuffix (this, value, CompareOptions.None);
1687                         case StringComparison.CurrentCultureIgnoreCase:
1688                                 return CultureInfo.CurrentCulture.CompareInfo.IsSuffix (this, value, CompareOptions.IgnoreCase);
1689                         case StringComparison.InvariantCulture:
1690                                 return CultureInfo.InvariantCulture.CompareInfo.IsSuffix (this, value, CompareOptions.None);
1691                         case StringComparison.InvariantCultureIgnoreCase:
1692                                 return CultureInfo.InvariantCulture.CompareInfo.IsSuffix (this, value, CompareOptions.IgnoreCase);
1693                         case StringComparison.Ordinal:
1694                                 return CultureInfo.CurrentCulture.CompareInfo.IsSuffix (this, value, CompareOptions.Ordinal);
1695                         case StringComparison.OrdinalIgnoreCase:
1696                                 return CultureInfo.CurrentCulture.CompareInfo.IsSuffix (this, value, CompareOptions.OrdinalIgnoreCase);
1697                         default:
1698                                 string msg = Locale.GetText ("Invalid value '{0}' for StringComparison", comparisonType);
1699                                 throw new ArgumentException (msg, "comparisonType");
1700                         }
1701                 }
1702
1703                 public bool StartsWith (String value, bool ignoreCase, CultureInfo culture)
1704                 {
1705                         if (culture == null)
1706                                 culture = CultureInfo.CurrentCulture;
1707                         
1708                         return culture.CompareInfo.IsPrefix (this, value, ignoreCase ? CompareOptions.IgnoreCase : CompareOptions.None);
1709                 }
1710
1711                 // Following method is culture-insensitive
1712                 public unsafe String Replace (char oldChar, char newChar)
1713                 {
1714                         if (this.length == 0 || oldChar == newChar)
1715                                 return this;
1716
1717                         int start_pos = IndexOfUnchecked (oldChar, 0, this.length);
1718                         if (start_pos == -1)
1719                                 return this;
1720
1721                         if (start_pos < 4)
1722                                 start_pos = 0;
1723
1724                         string tmp = InternalAllocateStr (length);
1725                         fixed (char* dest = tmp, src = &start_char) {
1726                                 if (start_pos != 0)
1727                                         CharCopy (dest, src, start_pos);
1728
1729                                 char* end_ptr = dest + length;
1730                                 char* dest_ptr = dest + start_pos;
1731                                 char* src_ptr = src + start_pos;
1732
1733                                 while (dest_ptr != end_ptr) {
1734                                         if (*src_ptr == oldChar)
1735                                                 *dest_ptr = newChar;
1736                                         else
1737                                                 *dest_ptr = *src_ptr;
1738
1739                                         ++src_ptr;
1740                                         ++dest_ptr;
1741                                 }
1742                         }
1743                         return tmp;
1744                 }
1745
1746                 // culture-insensitive using ordinal search (See testcase StringTest.ReplaceStringCultureTests)
1747                 public String Replace (String oldValue, String newValue)
1748                 {
1749                         // LAMESPEC: According to MSDN the following method is culture-sensitive but this seems to be incorrect
1750                         // LAMESPEC: Result is undefined if result length is longer than maximum string length
1751
1752                         if (oldValue == null)
1753                                 throw new ArgumentNullException ("oldValue");
1754
1755                         if (oldValue.Length == 0)
1756                                 throw new ArgumentException ("oldValue is the empty string.");
1757
1758                         if (this.Length == 0)
1759                                 return this;
1760                         
1761                         if (newValue == null)
1762                                 newValue = Empty;
1763
1764                         return ReplaceUnchecked (oldValue, newValue);
1765                 }
1766
1767                 private unsafe String ReplaceUnchecked (String oldValue, String newValue)
1768                 {
1769                         if (oldValue.length > length)
1770                                 return this;
1771                         if (oldValue.length == 1 && newValue.length == 1) {
1772                                 return Replace (oldValue[0], newValue[0]);
1773                                 // ENHANCE: It would be possible to special case oldValue.length == newValue.length
1774                                 // because the length of the result would be this.length and length calculation unneccesary
1775                         }
1776
1777                         const int maxValue = 200; // Allocate 800 byte maximum
1778                         int* dat = stackalloc int[maxValue];
1779                         fixed (char* source = this, replace = newValue) {
1780                                 int i = 0, count = 0;
1781                                 while (i < length) {
1782                                         int found = IndexOfOrdinalUnchecked (oldValue, i, length - i);
1783                                         if (found < 0)
1784                                                 break;
1785                                         else {
1786                                                 if (count < maxValue)
1787                                                         dat[count++] = found;
1788                                                 else
1789                                                         return ReplaceFallback (oldValue, newValue, maxValue);
1790                                         }
1791                                         i = found + oldValue.length;
1792                                 }
1793                                 if (count == 0)
1794                                         return this;
1795                                 int nlen = this.length + ((newValue.length - oldValue.length) * count);
1796                                 String tmp = InternalAllocateStr (nlen);
1797
1798                                 int curPos = 0, lastReadPos = 0;
1799                                 fixed (char* dest = tmp) {
1800                                         for (int j = 0; j < count; j++) {
1801                                                 int precopy = dat[j] - lastReadPos;
1802                                                 CharCopy (dest + curPos, source + lastReadPos, precopy);
1803                                                 curPos += precopy;
1804                                                 lastReadPos = dat[j] + oldValue.length;
1805                                                 CharCopy (dest + curPos, replace, newValue.length);
1806                                                 curPos += newValue.length;
1807                                         }
1808                                         CharCopy (dest + curPos, source + lastReadPos, length - lastReadPos);
1809                                 }
1810                                 return tmp;
1811                         }
1812                 }
1813
1814                 private String ReplaceFallback (String oldValue, String newValue, int testedCount)
1815                 {
1816                         int lengthEstimate = this.length + ((newValue.length - oldValue.length) * testedCount);
1817                         StringBuilder sb = new StringBuilder (lengthEstimate);
1818                         for (int i = 0; i < length;) {
1819                                 int found = IndexOfOrdinalUnchecked (oldValue, i, length - i);
1820                                 if (found < 0) {
1821                                         sb.Append (SubstringUnchecked (i, length - i));
1822                                         break;
1823                                 }
1824                                 sb.Append (SubstringUnchecked (i, found - i));
1825                                 sb.Append (newValue);
1826                                 i = found + oldValue.Length;
1827                         }
1828                         return sb.ToString ();
1829
1830                 }
1831
1832                 public unsafe String Remove (int startIndex, int count)
1833                 {
1834                         if (startIndex < 0)
1835                                 throw new ArgumentOutOfRangeException ("startIndex", "Cannot be negative.");
1836                         if (count < 0)
1837                                 throw new ArgumentOutOfRangeException ("count", "Cannot be negative.");
1838                         if (startIndex > this.length - count)
1839                                 throw new ArgumentOutOfRangeException ("count", "startIndex + count > this.length");
1840
1841                         String tmp = InternalAllocateStr (this.length - count);
1842
1843                         fixed (char *dest = tmp, src = this) {
1844                                 char *dst = dest;
1845                                 CharCopy (dst, src, startIndex);
1846                                 int skip = startIndex + count;
1847                                 dst += startIndex;
1848                                 CharCopy (dst, src + skip, length - skip);
1849                         }
1850                         return tmp;
1851                 }
1852
1853                 public String ToLower ()
1854                 {
1855                         return ToLower (CultureInfo.CurrentCulture);
1856                 }
1857
1858                 public String ToLower (CultureInfo culture)
1859                 {
1860                         if (culture == null)
1861                                 throw new ArgumentNullException ("culture");
1862
1863                         if (culture.LCID == 0x007F) // Invariant
1864                                 return ToLowerInvariant ();
1865
1866                         return culture.TextInfo.ToLower (this);
1867                 }
1868
1869                 public unsafe String ToLowerInvariant ()
1870                 {
1871                         if (length == 0)
1872                                 return Empty;
1873
1874                         string tmp = InternalAllocateStr (length);
1875                         fixed (char* source = &start_char, dest = tmp) {
1876
1877                                 char* destPtr = (char*)dest;
1878                                 char* sourcePtr = (char*)source;
1879
1880                                 for (int n = 0; n < length; n++) {
1881                                         *destPtr = Char.ToLowerInvariant (*sourcePtr);
1882                                         sourcePtr++;
1883                                         destPtr++;
1884                                 }
1885                         }
1886                         return tmp;
1887                 }
1888
1889                 public String ToUpper ()
1890                 {
1891                         return ToUpper (CultureInfo.CurrentCulture);
1892                 }
1893
1894                 public String ToUpper (CultureInfo culture)
1895                 {
1896                         if (culture == null)
1897                                 throw new ArgumentNullException ("culture");
1898
1899                         if (culture.LCID == 0x007F) // Invariant
1900                                 return ToUpperInvariant ();
1901
1902                         return culture.TextInfo.ToUpper (this);
1903                 }
1904
1905                 public unsafe String ToUpperInvariant ()
1906                 {
1907                         if (length == 0)
1908                                 return Empty;
1909
1910                         string tmp = InternalAllocateStr (length);
1911                         fixed (char* source = &start_char, dest = tmp) {
1912
1913                                 char* destPtr = (char*)dest;
1914                                 char* sourcePtr = (char*)source;
1915
1916                                 for (int n = 0; n < length; n++) {
1917                                         *destPtr = Char.ToUpperInvariant (*sourcePtr);
1918                                         sourcePtr++;
1919                                         destPtr++;
1920                                 }
1921                         }
1922                         return tmp;
1923                 }
1924
1925                 public override String ToString ()
1926                 {
1927                         return this;
1928                 }
1929
1930                 public String ToString (IFormatProvider provider)
1931                 {
1932                         return this;
1933                 }
1934
1935                 public static String Format (String format, Object arg0)
1936                 {
1937                         return Format (null, format, new Object[] {arg0});
1938                 }
1939
1940                 public static String Format (String format, Object arg0, Object arg1)
1941                 {
1942                         return Format (null, format, new Object[] {arg0, arg1});
1943                 }
1944
1945                 public static String Format (String format, Object arg0, Object arg1, Object arg2)
1946                 {
1947                         return Format (null, format, new Object[] {arg0, arg1, arg2});
1948                 }
1949
1950                 public static string Format (string format, params object[] args)
1951                 {
1952                         return Format (null, format, args);
1953                 }
1954         
1955                 public static string Format (IFormatProvider provider, string format, params object[] args)
1956                 {
1957                         StringBuilder b = FormatHelper (null, provider, format, args);
1958                         return b.ToString ();
1959                 }
1960                 
1961                 internal static StringBuilder FormatHelper (StringBuilder result, IFormatProvider provider, string format, params object[] args)
1962                 {
1963                         if (format == null)
1964                                 throw new ArgumentNullException ("format");
1965                         if (args == null)
1966                                 throw new ArgumentNullException ("args");
1967
1968                         if (result == null) {
1969                                 /* Try to approximate the size of result to avoid reallocations */
1970                                 int i, len;
1971
1972                                 len = 0;
1973                                 for (i = 0; i < args.Length; ++i) {
1974                                         string s = args [i] as string;
1975                                         if (s != null)
1976                                                 len += s.length;
1977                                         else
1978                                                 break;
1979                                 }
1980                                 if (i == args.Length)
1981                                         result = new StringBuilder (len + format.length);
1982                                 else
1983                                         result = new StringBuilder ();
1984                         }
1985
1986                         int ptr = 0;
1987                         int start = ptr;
1988                         var formatter = provider != null ? provider.GetFormat (typeof (ICustomFormatter)) as ICustomFormatter : null;
1989
1990                         while (ptr < format.length) {
1991                                 char c = format[ptr ++];
1992
1993                                 if (c == '{') {
1994                                         result.Append (format, start, ptr - start - 1);
1995
1996                                         // check for escaped open bracket
1997
1998                                         if (format[ptr] == '{') {
1999                                                 start = ptr ++;
2000                                                 continue;
2001                                         }
2002
2003                                         // parse specifier
2004                                 
2005                                         int n, width;
2006                                         bool left_align;
2007                                         string arg_format;
2008
2009                                         ParseFormatSpecifier (format, ref ptr, out n, out width, out left_align, out arg_format);
2010                                         if (n >= args.Length)
2011                                                 throw new FormatException ("Index (zero based) must be greater than or equal to zero and less than the size of the argument list.");
2012
2013                                         // format argument
2014
2015                                         object arg = args[n];
2016
2017                                         string str;
2018                                         if (arg == null)
2019                                                 str = Empty;
2020                                         else if (formatter != null)
2021                                                 str = formatter.Format (arg_format, arg, provider);
2022                                         else
2023                                                 str = null;
2024
2025                                         if (str == null) {
2026                                                 if (arg is IFormattable)
2027                                                         str = ((IFormattable)arg).ToString (arg_format, provider);
2028                                                 else
2029                                                         str = arg.ToString ();
2030                                         }
2031
2032                                         // pad formatted string and append to result
2033                                         if (width > str.length) {
2034                                                 const char padchar = ' ';
2035                                                 int padlen = width - str.length;
2036
2037                                                 if (left_align) {
2038                                                         result.Append (str);
2039                                                         result.Append (padchar, padlen);
2040                                                 }
2041                                                 else {
2042                                                         result.Append (padchar, padlen);
2043                                                         result.Append (str);
2044                                                 }
2045                                         } else {
2046                                                 result.Append (str);
2047                                         }
2048
2049                                         start = ptr;
2050                                 }
2051                                 else if (c == '}' && ptr < format.length && format[ptr] == '}') {
2052                                         result.Append (format, start, ptr - start - 1);
2053                                         start = ptr ++;
2054                                 }
2055                                 else if (c == '}') {
2056                                         throw new FormatException ("Input string was not in a correct format.");
2057                                 }
2058                         }
2059
2060                         if (start < format.length)
2061                                 result.Append (format, start, format.Length - start);
2062
2063                         return result;
2064                 }
2065
2066                 public unsafe static String Copy (String str)
2067                 {
2068                         if (str == null)
2069                                 throw new ArgumentNullException ("str");
2070
2071                         int length = str.length;
2072
2073                         String tmp = InternalAllocateStr (length);
2074                         if (length != 0) {
2075                                 fixed (char *dest = tmp, src = str) {
2076                                         CharCopy (dest, src, length);
2077                                 }
2078                         }
2079                         return tmp;
2080                 }
2081
2082                 public static String Concat (Object arg0)
2083                 {
2084                         if (arg0 == null)
2085                                 return Empty;
2086
2087                         return arg0.ToString ();
2088                 }
2089
2090                 public static String Concat (Object arg0, Object arg1)
2091                 {
2092                         return Concat ((arg0 != null) ? arg0.ToString () : null, (arg1 != null) ? arg1.ToString () : null);
2093                 }
2094
2095                 public static String Concat (Object arg0, Object arg1, Object arg2)
2096                 {
2097                         string s1, s2, s3;
2098                         if (arg0 == null)
2099                                 s1 = Empty;
2100                         else
2101                                 s1 = arg0.ToString ();
2102
2103                         if (arg1 == null)
2104                                 s2 = Empty;
2105                         else
2106                                 s2 = arg1.ToString ();
2107
2108                         if (arg2 == null)
2109                                 s3 = Empty;
2110                         else
2111                                 s3 = arg2.ToString ();
2112
2113                         return Concat (s1, s2, s3);
2114                 }
2115
2116                 [CLSCompliant(false)]
2117                 public static String Concat (Object arg0, Object arg1, Object arg2,
2118                                              Object arg3, __arglist)
2119                 {
2120                         string s1, s2, s3, s4;
2121
2122                         if (arg0 == null)
2123                                 s1 = Empty;
2124                         else
2125                                 s1 = arg0.ToString ();
2126
2127                         if (arg1 == null)
2128                                 s2 = Empty;
2129                         else
2130                                 s2 = arg1.ToString ();
2131
2132                         if (arg2 == null)
2133                                 s3 = Empty;
2134                         else
2135                                 s3 = arg2.ToString ();
2136
2137                         ArgIterator iter = new ArgIterator (__arglist);
2138                         int argCount = iter.GetRemainingCount();
2139
2140                         StringBuilder sb = new StringBuilder ();
2141                         if (arg3 != null)
2142                                 sb.Append (arg3.ToString ());
2143
2144                         for (int i = 0; i < argCount; i++) {
2145                                 TypedReference typedRef = iter.GetNextArg ();
2146                                 sb.Append (TypedReference.ToObject (typedRef));
2147                         }
2148
2149                         s4 = sb.ToString ();
2150
2151                         return Concat (s1, s2, s3, s4);                 
2152                 }
2153
2154                 public unsafe static String Concat (String str0, String str1)
2155                 {
2156                         if (str0 == null || str0.Length == 0) {
2157                                 if (str1 == null || str1.Length == 0)
2158                                         return Empty;
2159                                 return str1;
2160                         }
2161
2162                         if (str1 == null || str1.Length == 0)
2163                                 return str0; 
2164
2165                         String tmp = InternalAllocateStr (str0.length + str1.length);
2166
2167                         fixed (char *dest = tmp, src = str0)
2168                                 CharCopy (dest, src, str0.length);
2169                         fixed (char *dest = tmp, src = str1)
2170                                 CharCopy (dest + str0.Length, src, str1.length);
2171
2172                         return tmp;
2173                 }
2174
2175                 public unsafe static String Concat (String str0, String str1, String str2)
2176                 {
2177                         if (str0 == null || str0.Length == 0){
2178                                 if (str1 == null || str1.Length == 0){
2179                                         if (str2 == null || str2.Length == 0)
2180                                                 return Empty;
2181                                         return str2;
2182                                 } else {
2183                                         if (str2 == null || str2.Length == 0)
2184                                                 return str1;
2185                                 }
2186                                 str0 = Empty;
2187                         } else {
2188                                 if (str1 == null || str1.Length == 0){
2189                                         if (str2 == null || str2.Length == 0)
2190                                                 return str0;
2191                                         else
2192                                                 str1 = Empty;
2193                                 } else {
2194                                         if (str2 == null || str2.Length == 0)
2195                                                 str2 = Empty;
2196                                 }
2197                         }
2198
2199                         String tmp = InternalAllocateStr (str0.length + str1.length + str2.length);
2200
2201                         if (str0.Length != 0) {
2202                                 fixed (char *dest = tmp, src = str0) {
2203                                         CharCopy (dest, src, str0.length);
2204                                 }
2205                         }
2206                         if (str1.Length != 0) {
2207                                 fixed (char *dest = tmp, src = str1) {
2208                                         CharCopy (dest + str0.Length, src, str1.length);
2209                                 }
2210                         }
2211                         if (str2.Length != 0) {
2212                                 fixed (char *dest = tmp, src = str2) {
2213                                         CharCopy (dest + str0.Length + str1.Length, src, str2.length);
2214                                 }
2215                         }
2216
2217                         return tmp;
2218                 }
2219
2220                 public unsafe static String Concat (String str0, String str1, String str2, String str3)
2221                 {
2222                         if (str0 == null && str1 == null && str2 == null && str3 == null)
2223                                 return Empty;
2224
2225                         if (str0 == null)
2226                                 str0 = Empty;
2227                         if (str1 == null)
2228                                 str1 = Empty;
2229                         if (str2 == null)
2230                                 str2 = Empty;
2231                         if (str3 == null)
2232                                 str3 = Empty;
2233
2234                         String tmp = InternalAllocateStr (str0.length + str1.length + str2.length + str3.length);
2235
2236                         if (str0.Length != 0) {
2237                                 fixed (char *dest = tmp, src = str0) {
2238                                         CharCopy (dest, src, str0.length);
2239                                 }
2240                         }
2241                         if (str1.Length != 0) {
2242                                 fixed (char *dest = tmp, src = str1) {
2243                                         CharCopy (dest + str0.Length, src, str1.length);
2244                                 }
2245                         }
2246                         if (str2.Length != 0) {
2247                                 fixed (char *dest = tmp, src = str2) {
2248                                         CharCopy (dest + str0.Length + str1.Length, src, str2.length);
2249                                 }
2250                         }
2251                         if (str3.Length != 0) {
2252                                 fixed (char *dest = tmp, src = str3) {
2253                                         CharCopy (dest + str0.Length + str1.Length + str2.Length, src, str3.length);
2254                                 }
2255                         }
2256
2257                         return tmp;
2258                 }
2259
2260                 public static String Concat (params Object[] args)
2261                 {
2262                         if (args == null)
2263                                 throw new ArgumentNullException ("args");
2264
2265                         int argLen = args.Length;
2266                         if (argLen == 0)
2267                                 return Empty;
2268
2269                         string [] strings = new string [argLen];
2270                         int len = 0;
2271                         for (int i = 0; i < argLen; i++) {
2272                                 if (args[i] != null) {
2273                                         strings[i] = args[i].ToString ();
2274                                         len += strings[i].length;
2275                                 }
2276                         }
2277
2278                         return ConcatInternal (strings, len);
2279                 }
2280
2281                 public static String Concat (params String[] values)
2282                 {
2283                         if (values == null)
2284                                 throw new ArgumentNullException ("values");
2285
2286                         int len = 0;
2287                         for (int i = 0; i < values.Length; i++) {
2288                                 String s = values[i];
2289                                 if (s != null)
2290                                         len += s.length;
2291                         }
2292
2293                         return ConcatInternal (values, len);
2294                 }
2295
2296                 private static unsafe String ConcatInternal (String[] values, int length)
2297                 {
2298                         if (length == 0)
2299                                 return Empty;
2300
2301                         String tmp = InternalAllocateStr (length);
2302
2303                         fixed (char* dest = tmp) {
2304                                 int pos = 0;
2305                                 for (int i = 0; i < values.Length; i++) {
2306                                         String source = values[i];
2307                                         if (source != null) {
2308                                                 fixed (char* src = source) {
2309                                                         CharCopy (dest + pos, src, source.length);
2310                                                 }
2311                                                 pos += source.Length;
2312                                         }
2313                                 }
2314                         }
2315                         return tmp;
2316                 }
2317
2318                 public unsafe String Insert (int startIndex, String value)
2319                 {
2320                         if (value == null)
2321                                 throw new ArgumentNullException ("value");
2322
2323                         if (startIndex < 0 || startIndex > this.length)
2324                                 throw new ArgumentOutOfRangeException ("startIndex", "Cannot be negative and must be less than or equal to length of string.");
2325
2326                         if (value.Length == 0)
2327                                 return this;
2328                         if (this.Length == 0)
2329                                 return value;
2330                         String tmp = InternalAllocateStr (this.length + value.length);
2331
2332                         fixed (char *dest = tmp, src = this, val = value) {
2333                                 char *dst = dest;
2334                                 CharCopy (dst, src, startIndex);
2335                                 dst += startIndex;
2336                                 CharCopy (dst, val, value.length);
2337                                 dst += value.length;
2338                                 CharCopy (dst, src + startIndex, length - startIndex);
2339                         }
2340                         return tmp;
2341                 }
2342
2343                 public static string Intern (string str)
2344                 {
2345                         if (str == null)
2346                                 throw new ArgumentNullException ("str");
2347
2348                         return InternalIntern (str);
2349                 }
2350
2351                 public static string IsInterned (string str)
2352                 {
2353                         if (str == null)
2354                                 throw new ArgumentNullException ("str");
2355
2356                         return InternalIsInterned (str);
2357                 }
2358         
2359 #if NET_4_0
2360                 public static string Join (string separator, params string [] value)
2361 #else
2362                 public static string Join (string separator, string [] value)
2363 #endif
2364                 {
2365                         if (value == null)
2366                                 throw new ArgumentNullException ("value");
2367                         if (separator == null)
2368                                 separator = Empty;
2369
2370                         return JoinUnchecked (separator, value, 0, value.Length);
2371                 }
2372
2373                 public static string Join (string separator, string[] value, int startIndex, int count)
2374                 {
2375                         if (value == null)
2376                                 throw new ArgumentNullException ("value");
2377                         if (startIndex < 0)
2378                                 throw new ArgumentOutOfRangeException ("startIndex", "< 0");
2379                         if (count < 0)
2380                                 throw new ArgumentOutOfRangeException ("count", "< 0");
2381                         if (startIndex > value.Length - count)
2382                                 throw new ArgumentOutOfRangeException ("startIndex", "startIndex + count > value.length");
2383
2384                         if (startIndex == value.Length)
2385                                 return Empty;
2386                         if (separator == null)
2387                                 separator = Empty;
2388
2389                         return JoinUnchecked (separator, value, startIndex, count);
2390                 }
2391
2392                 private static unsafe string JoinUnchecked (string separator, string[] value, int startIndex, int count)
2393                 {
2394                         // Unchecked parameters
2395                         // startIndex, count must be >= 0; startIndex + count must be <= value.length
2396                         // separator and value must not be null
2397
2398                         int length = 0;
2399                         int maxIndex = startIndex + count;
2400                         // Precount the number of characters that the resulting string will have
2401                         for (int i = startIndex; i < maxIndex; i++) {
2402                                 String s = value[i];
2403                                 if (s != null)
2404                                         length += s.length;
2405                         }
2406                         length += separator.length * (count - 1);
2407                         if (length <= 0)
2408                                 return Empty;
2409
2410                         String tmp = InternalAllocateStr (length);
2411
2412                         maxIndex--;
2413                         fixed (char* dest = tmp, sepsrc = separator) {
2414                                 // Copy each string from value except the last one and add a separator for each
2415                                 int pos = 0;
2416                                 for (int i = startIndex; i < maxIndex; i++) {
2417                                         String source = value[i];
2418                                         if (source != null) {
2419                                                 if (source.Length > 0) {
2420                                                         fixed (char* src = source)
2421                                                                 CharCopy (dest + pos, src, source.Length);
2422                                                         pos += source.Length;
2423                                                 }
2424                                         }
2425                                         if (separator.Length > 0) {
2426                                                 CharCopy (dest + pos, sepsrc, separator.Length);
2427                                                 pos += separator.Length;
2428                                         }
2429                                 }
2430                                 // Append last string that does not get an additional separator
2431                                 String sourceLast = value[maxIndex];
2432                                 if (sourceLast != null) {
2433                                         if (sourceLast.Length > 0) {
2434                                                 fixed (char* src = sourceLast)
2435                                                         CharCopy (dest + pos, src, sourceLast.Length);
2436                                         }
2437                                 }
2438                         }
2439                         return tmp;
2440                 }
2441
2442                 bool IConvertible.ToBoolean (IFormatProvider provider)
2443                 {
2444                         return Convert.ToBoolean (this, provider);
2445                 }
2446
2447                 byte IConvertible.ToByte (IFormatProvider provider)
2448                 {
2449                         return Convert.ToByte (this, provider);
2450                 }
2451
2452                 char IConvertible.ToChar (IFormatProvider provider)
2453                 {
2454                         return Convert.ToChar (this, provider);
2455                 }
2456
2457                 DateTime IConvertible.ToDateTime (IFormatProvider provider)
2458                 {
2459                         return Convert.ToDateTime (this, provider);
2460                 }
2461
2462                 decimal IConvertible.ToDecimal (IFormatProvider provider)
2463                 {
2464                         return Convert.ToDecimal (this, provider);
2465                 }
2466
2467                 double IConvertible.ToDouble (IFormatProvider provider)
2468                 {
2469                         return Convert.ToDouble (this, provider);
2470                 }
2471
2472                 short IConvertible.ToInt16 (IFormatProvider provider)
2473                 {
2474                         return Convert.ToInt16 (this, provider);
2475                 }
2476
2477                 int IConvertible.ToInt32 (IFormatProvider provider)
2478                 {
2479                         return Convert.ToInt32 (this, provider);
2480                 }
2481
2482                 long IConvertible.ToInt64 (IFormatProvider provider)
2483                 {
2484                         return Convert.ToInt64 (this, provider);
2485                 }
2486
2487                 sbyte IConvertible.ToSByte (IFormatProvider provider)
2488                 {
2489                         return Convert.ToSByte (this, provider);
2490                 }
2491
2492                 float IConvertible.ToSingle (IFormatProvider provider)
2493                 {
2494                         return Convert.ToSingle (this, provider);
2495                 }
2496
2497                 object IConvertible.ToType (Type targetType, IFormatProvider provider)
2498                 {
2499                         if (targetType == null)
2500                                 throw new ArgumentNullException ("type");
2501                         return Convert.ToType (this, targetType, provider, false);
2502                 }
2503
2504                 ushort IConvertible.ToUInt16 (IFormatProvider provider)
2505                 {
2506                         return Convert.ToUInt16 (this, provider);
2507                 }
2508
2509                 uint IConvertible.ToUInt32 (IFormatProvider provider)
2510                 {
2511                         return Convert.ToUInt32 (this, provider);
2512                 }
2513
2514                 ulong IConvertible.ToUInt64 (IFormatProvider provider)
2515                 {
2516                         return Convert.ToUInt64 (this, provider);
2517                 }
2518
2519                 public int Length {
2520                         get {
2521                                 return length;
2522                         }
2523                 }
2524
2525                 public CharEnumerator GetEnumerator ()
2526                 {
2527                         return new CharEnumerator (this);
2528                 }
2529
2530                 IEnumerator<char> IEnumerable<char>.GetEnumerator ()
2531                 {
2532                         return new CharEnumerator (this);
2533                 }
2534
2535                 IEnumerator IEnumerable.GetEnumerator ()
2536                 {
2537                         return new CharEnumerator (this);
2538                 }
2539
2540                 private static void ParseFormatSpecifier (string str, ref int ptr, out int n, out int width,
2541                                                           out bool left_align, out string format)
2542                 {
2543                         int max = str.Length;
2544                         
2545                         // parses format specifier of form:
2546                         //   N,[\ +[-]M][:F]}
2547                         //
2548                         // where:
2549                         // N = argument number (non-negative integer)
2550                         
2551                         n = ParseDecimal (str, ref ptr);
2552                         if (n < 0)
2553                                 throw new FormatException ("Input string was not in a correct format.");
2554                         
2555                         // M = width (non-negative integer)
2556                         
2557                         if (ptr < max && str[ptr] == ',') {
2558                                 // White space between ',' and number or sign.
2559                                 ++ptr;
2560                                 while (ptr < max && Char.IsWhiteSpace (str [ptr]))
2561                                         ++ptr;
2562                                 int start = ptr;
2563                                 
2564                                 format = str.Substring (start, ptr - start);
2565                                 
2566                                 left_align = (ptr < max && str [ptr] == '-');
2567                                 if (left_align)
2568                                         ++ ptr;
2569                                 
2570                                 width = ParseDecimal (str, ref ptr);
2571                                 if (width < 0)
2572                                         throw new FormatException ("Input string was not in a correct format.");
2573                         }
2574                         else {
2575                                 width = 0;
2576                                 left_align = false;
2577                                 format = Empty;
2578                         }
2579                         
2580                         // F = argument format (string)
2581                         
2582                         if (ptr < max && str[ptr] == ':') {
2583                                 int start = ++ ptr;
2584                                 while (ptr < max && str[ptr] != '}')
2585                                         ++ ptr;
2586                                 
2587                                 format += str.Substring (start, ptr - start);
2588                         }
2589                         else
2590                                 format = null;
2591                         
2592                         if ((ptr >= max) || str[ptr ++] != '}')
2593                                 throw new FormatException ("Input string was not in a correct format.");
2594                 }
2595
2596                 private static int ParseDecimal (string str, ref int ptr)
2597                 {
2598                         int p = ptr;
2599                         int n = 0;
2600                         int max = str.Length;
2601                         
2602                         while (p < max) {
2603                                 char c = str[p];
2604                                 if (c < '0' || '9' < c)
2605                                         break;
2606
2607                                 n = n * 10 + c - '0';
2608                                 ++ p;
2609                         }
2610
2611                         if (p == ptr || p == max)
2612                                 return -1;
2613
2614                         ptr = p;
2615                         return n;
2616                 }
2617
2618                 internal unsafe void InternalSetChar (int idx, char val)
2619                 {
2620                         if ((uint) idx >= (uint) Length)
2621                                 throw new ArgumentOutOfRangeException ("idx");
2622
2623                         fixed (char * pStr = &start_char) 
2624                         {
2625                                 pStr [idx] = val;
2626                         }
2627                 }
2628
2629                 internal unsafe void InternalSetLength (int newLength)
2630                 {
2631                         if (newLength > length)
2632                                 throw new ArgumentOutOfRangeException ("newLength", "newLength as to be <= length");
2633
2634                         // zero terminate, we can pass string objects directly via pinvoke
2635                         // we also zero the rest of the string, since the new GC needs to be
2636                         // able to handle the changing size (it will skip the 0 bytes).
2637                         fixed (char * pStr = &start_char) {
2638                                 char *p = pStr + newLength;
2639                                 char *end = pStr + length;
2640                                 while (p < end) {
2641                                         p [0] = '\0';
2642                                         p++;
2643                                 }
2644                         }
2645                         length = newLength;
2646                 }
2647
2648                 [ReliabilityContractAttribute (Consistency.WillNotCorruptState, Cer.MayFail)]
2649                 // When modifying it, GetCaseInsensitiveHashCode() should be modified as well.
2650                 public unsafe override int GetHashCode ()
2651                 {
2652                         fixed (char * c = this) {
2653                                 char * cc = c;
2654                                 char * end = cc + length - 1;
2655                                 int h = 0;
2656                                 for (;cc < end; cc += 2) {
2657                                         h = (h << 5) - h + *cc;
2658                                         h = (h << 5) - h + cc [1];
2659                                 }
2660                                 ++end;
2661                                 if (cc < end)
2662                                         h = (h << 5) - h + *cc;
2663                                 return h;
2664                         }
2665                 }
2666
2667 #if NET_4_0
2668                 [ComVisible(false)]
2669                 public static string Concat (IEnumerable<string> values)
2670                 {
2671                         if (values == null)
2672                                 throw new ArgumentNullException ("values");
2673
2674                         var stringList = new List<string> ();
2675                         int len = 0;
2676                         foreach (var v in values){
2677                                 if (v == null)
2678                                         continue;
2679                                 len += v.Length;
2680                                 stringList.Add (v);
2681                         }
2682                         return ConcatInternal (stringList.ToArray (), len);
2683                 }
2684
2685                 [ComVisibleAttribute(false)]
2686                 public static string Concat<T> (IEnumerable<T> values)
2687                 {
2688                         if (values == null)
2689                                 throw new ArgumentNullException ("values");
2690
2691                         var stringList = new List<string> ();
2692                         int len = 0;
2693                         foreach (var v in values){
2694                                 string sr = v.ToString ();
2695                                 len += sr.Length;
2696                                 stringList.Add (sr);
2697                         }
2698                         return ConcatInternal (stringList.ToArray (), len);
2699                 }
2700
2701                 [ComVisibleAttribute(false)]
2702                 public static string Join (string separator, IEnumerable<string> values)
2703                 {
2704                         if (separator == null)
2705                                 return Concat (values);
2706                         
2707                         if (values == null)
2708                                 throw new ArgumentNullException ("values");
2709                         
2710                         var stringList = new List<string> ();
2711                         foreach (var v in values)
2712                                 stringList.Add (v);
2713
2714                         return JoinUnchecked (separator, stringList.ToArray (), 0, stringList.Count);
2715                 }
2716
2717                 [ComVisibleAttribute(false)]
2718                 public static string Join (string separator, params object [] values)
2719                 {
2720                         if (separator == null)
2721                                 return Concat (values);
2722                         
2723                         if (values == null)
2724                                 throw new ArgumentNullException ("values");
2725
2726                         var strCopy = new string [values.Length];
2727                         int i = 0;
2728                         foreach (var v in values)
2729                                 strCopy [i++] = v.ToString ();
2730
2731                         return JoinUnchecked (separator, strCopy, 0, strCopy.Length);
2732                 }
2733                 
2734                 [ComVisible (false)]
2735                 public static string Join<T> (string separator, IEnumerable<T> values)
2736                 {
2737                         if (separator == null)
2738                                 return Concat<T> (values);
2739                                 
2740                         if (values == null)
2741                                 throw new ArgumentNullException ("values");
2742                         
2743                         var stringList = new List<string> ();
2744                         foreach (var v in values)
2745                                 stringList.Add (v.ToString ());
2746
2747                         return JoinUnchecked (separator, stringList.ToArray (), 0, stringList.Count);
2748                 }
2749
2750                 public static bool IsNullOrWhiteSpace (string value)
2751 #else
2752                 internal static bool IsNullOrWhiteSpace (string value)
2753 #endif
2754                 {
2755                         if ((value == null) || (value.Length == 0))
2756                                 return true;
2757                         foreach (char c in value)
2758                                 if (!Char.IsWhiteSpace (c))
2759                                         return false;
2760                         return true;
2761                 }
2762
2763                 internal unsafe int GetCaseInsensitiveHashCode ()
2764                 {
2765                         fixed (char * c = this) {
2766                                 char * cc = c;
2767                                 char * end = cc + length - 1;
2768                                 int h = 0;
2769                                 for (;cc < end; cc += 2) {
2770                                         h = (h << 5) - h + Char.ToUpperInvariant (*cc);
2771                                         h = (h << 5) - h + Char.ToUpperInvariant (cc [1]);
2772                                 }
2773                                 ++end;
2774                                 if (cc < end)
2775                                         h = (h << 5) - h + Char.ToUpperInvariant (*cc);
2776                                 return h;
2777                         }
2778                 }
2779
2780                 // Certain constructors are redirected to CreateString methods with
2781                 // matching argument list. The this pointer should not be used.
2782
2783                 private unsafe String CreateString (sbyte* value)
2784                 {
2785                         if (value == null)
2786                                 return Empty;
2787
2788                         byte* bytes = (byte*) value;
2789                         int length = 0;
2790
2791                         try {
2792                                 while (bytes++ [0] != 0)
2793                                         length++;
2794                         } catch (NullReferenceException) {
2795                                 throw new ArgumentOutOfRangeException ("ptr", "Value does not refer to a valid string.");
2796                 }
2797
2798                         return CreateString (value, 0, length, null);
2799                 }
2800
2801                 private unsafe String CreateString (sbyte* value, int startIndex, int length)
2802                 {
2803                         return CreateString (value, startIndex, length, null);
2804                 }
2805
2806                 private unsafe String CreateString (sbyte* value, int startIndex, int length, Encoding enc)
2807                 {
2808                         if (length < 0)
2809                                 throw new ArgumentOutOfRangeException ("length", "Non-negative number required.");
2810                         if (startIndex < 0)
2811                                 throw new ArgumentOutOfRangeException ("startIndex", "Non-negative number required.");
2812                         if (value + startIndex < value)
2813                                 throw new ArgumentOutOfRangeException ("startIndex", "Value, startIndex and length do not refer to a valid string.");
2814
2815                         if (enc == null) {
2816                                 if (value == null)
2817                                         throw new ArgumentNullException ("value");
2818                                 if (length == 0)
2819                                         return Empty;
2820
2821                                 enc = Encoding.Default;
2822                         }
2823
2824                         byte [] bytes = new byte [length];
2825
2826                         if (length != 0)
2827                                 fixed (byte* bytePtr = bytes)
2828                                         try {
2829                                                 memcpy (bytePtr, (byte*) (value + startIndex), length);
2830                                         } catch (NullReferenceException) {
2831                                                 throw new ArgumentOutOfRangeException ("ptr", "Value, startIndex and length do not refer to a valid string.");
2832                                         }
2833
2834                         // GetString () is called even when length == 0
2835                         return enc.GetString (bytes);
2836                 }
2837
2838                 unsafe string CreateString (char *value)
2839                 {
2840                         if (value == null)
2841                                 return Empty;
2842                         char *p = value;
2843                         int i = 0;
2844                         while (*p != 0) {
2845                                 ++i;
2846                                 ++p;
2847                         }
2848                         string result = InternalAllocateStr (i);
2849
2850                         if (i != 0) {
2851                                 fixed (char *dest = result) {
2852                                         CharCopy (dest, value, i);
2853                                 }
2854                         }
2855                         return result;
2856                 }
2857
2858                 unsafe string CreateString (char *value, int startIndex, int length)
2859                 {
2860                         if (length == 0)
2861                                 return Empty;
2862                         if (value == null)
2863                                 throw new ArgumentNullException ("value");
2864                         if (startIndex < 0)
2865                                 throw new ArgumentOutOfRangeException ("startIndex");
2866                         if (length < 0)
2867                                 throw new ArgumentOutOfRangeException ("length");
2868
2869                         string result = InternalAllocateStr (length);
2870
2871                         fixed (char *dest = result) {
2872                                 CharCopy (dest, value + startIndex, length);
2873                         }
2874                         return result;
2875                 }
2876
2877                 unsafe string CreateString (char [] val, int startIndex, int length)
2878                 {
2879                         if (val == null)
2880                                 throw new ArgumentNullException ("value");
2881                         if (startIndex < 0)
2882                                 throw new ArgumentOutOfRangeException ("startIndex", "Cannot be negative.");
2883                         if (length < 0)
2884                                 throw new ArgumentOutOfRangeException ("length", "Cannot be negative.");
2885                         if (startIndex > val.Length - length)
2886                                 throw new ArgumentOutOfRangeException ("startIndex", "Cannot be negative, and should be less than length of string.");
2887                         if (length == 0)
2888                                 return Empty;
2889
2890                         string result = InternalAllocateStr (length);
2891
2892                         fixed (char *dest = result, src = val) {
2893                                 CharCopy (dest, src + startIndex, length);
2894                         }
2895                         return result;
2896                 }
2897
2898                 unsafe string CreateString (char [] val)
2899                 {
2900                         if (val == null || val.Length == 0)
2901                                 return Empty;
2902                         string result = InternalAllocateStr (val.Length);
2903
2904                         fixed (char *dest = result, src = val) {
2905                                 CharCopy (dest, src, val.Length);
2906                         }
2907                         return result;
2908                 }
2909
2910                 unsafe string CreateString (char c, int count)
2911                 {
2912                         if (count < 0)
2913                                 throw new ArgumentOutOfRangeException ("count");
2914                         if (count == 0)
2915                                 return Empty;
2916                         string result = InternalAllocateStr (count);
2917                         fixed (char *dest = result) {
2918                                 char *p = dest;
2919                                 char *end = p + count;
2920                                 while (p < end) {
2921                                         *p = c;
2922                                         p++;
2923                                 }
2924                         }
2925                         return result;
2926                 }
2927
2928                 /* helpers used by the runtime as well as above or eslewhere in corlib */
2929                 internal static unsafe void memset (byte *dest, int val, int len)
2930                 {
2931                         if (len < 8) {
2932                                 while (len != 0) {
2933                                         *dest = (byte)val;
2934                                         ++dest;
2935                                         --len;
2936                                 }
2937                                 return;
2938                         }
2939                         if (val != 0) {
2940                                 val = val | (val << 8);
2941                                 val = val | (val << 16);
2942                         }
2943                         // align to 4
2944                         int rest = (int)dest & 3;
2945                         if (rest != 0) {
2946                                 rest = 4 - rest;
2947                                 len -= rest;
2948                                 do {
2949                                         *dest = (byte)val;
2950                                         ++dest;
2951                                         --rest;
2952                                 } while (rest != 0);
2953                         }
2954                         while (len >= 16) {
2955                                 ((int*)dest) [0] = val;
2956                                 ((int*)dest) [1] = val;
2957                                 ((int*)dest) [2] = val;
2958                                 ((int*)dest) [3] = val;
2959                                 dest += 16;
2960                                 len -= 16;
2961                         }
2962                         while (len >= 4) {
2963                                 ((int*)dest) [0] = val;
2964                                 dest += 4;
2965                                 len -= 4;
2966                         }
2967                         // tail bytes
2968                         while (len > 0) {
2969                                 *dest = (byte)val;
2970                                 dest++;
2971                                 len--;
2972                         }
2973                 }
2974
2975                 static unsafe void memcpy4 (byte *dest, byte *src, int size) {
2976                         /*while (size >= 32) {
2977                                 // using long is better than int and slower than double
2978                                 // FIXME: enable this only on correct alignment or on platforms
2979                                 // that can tolerate unaligned reads/writes of doubles
2980                                 ((double*)dest) [0] = ((double*)src) [0];
2981                                 ((double*)dest) [1] = ((double*)src) [1];
2982                                 ((double*)dest) [2] = ((double*)src) [2];
2983                                 ((double*)dest) [3] = ((double*)src) [3];
2984                                 dest += 32;
2985                                 src += 32;
2986                                 size -= 32;
2987                         }*/
2988                         while (size >= 16) {
2989                                 ((int*)dest) [0] = ((int*)src) [0];
2990                                 ((int*)dest) [1] = ((int*)src) [1];
2991                                 ((int*)dest) [2] = ((int*)src) [2];
2992                                 ((int*)dest) [3] = ((int*)src) [3];
2993                                 dest += 16;
2994                                 src += 16;
2995                                 size -= 16;
2996                         }
2997                         while (size >= 4) {
2998                                 ((int*)dest) [0] = ((int*)src) [0];
2999                                 dest += 4;
3000                                 src += 4;
3001                                 size -= 4;
3002                         }
3003                         while (size > 0) {
3004                                 ((byte*)dest) [0] = ((byte*)src) [0];
3005                                 dest += 1;
3006                                 src += 1;
3007                                 --size;
3008                         }
3009                 }
3010                 static unsafe void memcpy2 (byte *dest, byte *src, int size) {
3011                         while (size >= 8) {
3012                                 ((short*)dest) [0] = ((short*)src) [0];
3013                                 ((short*)dest) [1] = ((short*)src) [1];
3014                                 ((short*)dest) [2] = ((short*)src) [2];
3015                                 ((short*)dest) [3] = ((short*)src) [3];
3016                                 dest += 8;
3017                                 src += 8;
3018                                 size -= 8;
3019                         }
3020                         while (size >= 2) {
3021                                 ((short*)dest) [0] = ((short*)src) [0];
3022                                 dest += 2;
3023                                 src += 2;
3024                                 size -= 2;
3025                         }
3026                         if (size > 0)
3027                                 ((byte*)dest) [0] = ((byte*)src) [0];
3028                 }
3029                 static unsafe void memcpy1 (byte *dest, byte *src, int size) {
3030                         while (size >= 8) {
3031                                 ((byte*)dest) [0] = ((byte*)src) [0];
3032                                 ((byte*)dest) [1] = ((byte*)src) [1];
3033                                 ((byte*)dest) [2] = ((byte*)src) [2];
3034                                 ((byte*)dest) [3] = ((byte*)src) [3];
3035                                 ((byte*)dest) [4] = ((byte*)src) [4];
3036                                 ((byte*)dest) [5] = ((byte*)src) [5];
3037                                 ((byte*)dest) [6] = ((byte*)src) [6];
3038                                 ((byte*)dest) [7] = ((byte*)src) [7];
3039                                 dest += 8;
3040                                 src += 8;
3041                                 size -= 8;
3042                         }
3043                         while (size >= 2) {
3044                                 ((byte*)dest) [0] = ((byte*)src) [0];
3045                                 ((byte*)dest) [1] = ((byte*)src) [1];
3046                                 dest += 2;
3047                                 src += 2;
3048                                 size -= 2;
3049                         }
3050                         if (size > 0)
3051                                 ((byte*)dest) [0] = ((byte*)src) [0];
3052                 }
3053
3054                 internal static unsafe void memcpy (byte *dest, byte *src, int size) {
3055                         // FIXME: if pointers are not aligned, try to align them
3056                         // so a faster routine can be used. Handle the case where
3057                         // the pointers can't be reduced to have the same alignment
3058                         // (just ignore the issue on x86?)
3059                         if ((((int)dest | (int)src) & 3) != 0) {
3060                                 if (((int)dest & 1) != 0 && ((int)src & 1) != 0 && size >= 1) {
3061                                         dest [0] = src [0];
3062                                         ++dest;
3063                                         ++src;
3064                                         --size;
3065                                 }
3066                                 if (((int)dest & 2) != 0 && ((int)src & 2) != 0 && size >= 2) {
3067                                         ((short*)dest) [0] = ((short*)src) [0];
3068                                         dest += 2;
3069                                         src += 2;
3070                                         size -= 2;
3071                                 }
3072                                 if ((((int)dest | (int)src) & 1) != 0) {
3073                                         memcpy1 (dest, src, size);
3074                                         return;
3075                                 }
3076                                 if ((((int)dest | (int)src) & 2) != 0) {
3077                                         memcpy2 (dest, src, size);
3078                                         return;
3079                                 }
3080                         }
3081                         memcpy4 (dest, src, size);
3082                 }
3083
3084                 internal static unsafe void CharCopy (char *dest, char *src, int count) {
3085                         // Same rules as for memcpy, but with the premise that 
3086                         // chars can only be aligned to even addresses if their
3087                         // enclosing types are correctly aligned
3088                         if ((((int)(byte*)dest | (int)(byte*)src) & 3) != 0) {
3089                                 if (((int)(byte*)dest & 2) != 0 && ((int)(byte*)src & 2) != 0 && count > 0) {
3090                                         ((short*)dest) [0] = ((short*)src) [0];
3091                                         dest++;
3092                                         src++;
3093                                         count--;
3094                                 }
3095                                 if ((((int)(byte*)dest | (int)(byte*)src) & 2) != 0) {
3096                                         memcpy2 ((byte*)dest, (byte*)src, count * 2);
3097                                         return;
3098                                 }
3099                         }
3100                         memcpy4 ((byte*)dest, (byte*)src, count * 2);
3101                 }
3102
3103                 internal static unsafe void CharCopyReverse (char *dest, char *src, int count)
3104                 {
3105                         dest += count;
3106                         src += count;
3107                         for (int i = count; i > 0; i--) {
3108                                 dest--;
3109                                 src--;
3110                                 *dest = *src;
3111                         }       
3112                 }
3113
3114                 internal static unsafe void CharCopy (String target, int targetIndex, String source, int sourceIndex, int count)
3115                 {
3116                         fixed (char* dest = target, src = source)
3117                                 CharCopy (dest + targetIndex, src + sourceIndex, count);
3118                 }
3119
3120                 internal static unsafe void CharCopy (String target, int targetIndex, Char[] source, int sourceIndex, int count)
3121                 {
3122                         fixed (char* dest = target, src = source)
3123                                 CharCopy (dest + targetIndex, src + sourceIndex, count);
3124                 }
3125
3126                 // Use this method if you cannot block copy from left to right (e.g. because you are coping within the same string)
3127                 internal static unsafe void CharCopyReverse (String target, int targetIndex, String source, int sourceIndex, int count)
3128                 {
3129                         fixed (char* dest = target, src = source)
3130                                 CharCopyReverse (dest + targetIndex, src + sourceIndex, count);
3131                 }
3132
3133                 [CLSCompliant (false), MethodImplAttribute (MethodImplOptions.InternalCall)]
3134                 unsafe public extern String (char *value);
3135
3136                 [CLSCompliant (false), MethodImplAttribute (MethodImplOptions.InternalCall)]
3137                 unsafe public extern String (char *value, int startIndex, int length);
3138
3139                 [CLSCompliant (false), MethodImplAttribute (MethodImplOptions.InternalCall)]
3140                 unsafe public extern String (sbyte *value);
3141
3142                 [CLSCompliant (false), MethodImplAttribute (MethodImplOptions.InternalCall)]
3143                 unsafe public extern String (sbyte *value, int startIndex, int length);
3144
3145                 [CLSCompliant (false), MethodImplAttribute (MethodImplOptions.InternalCall)]
3146                 unsafe public extern String (sbyte *value, int startIndex, int length, Encoding enc);
3147
3148                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
3149                 public extern String (char [] value, int startIndex, int length);
3150
3151                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
3152                 public extern String (char [] value);
3153
3154                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
3155                 public extern String (char c, int count);
3156
3157                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
3158                 internal extern static String InternalAllocateStr (int length);
3159
3160                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
3161                 private extern static string InternalIntern (string str);
3162
3163                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
3164                 private extern static string InternalIsInterned (string str);
3165
3166                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
3167                 private extern static int GetLOSLimit ();
3168         }
3169 }