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