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