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