System.Drawing: added email to icon and test file headers
[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                         while (ptr < format.length) {
1957                                 char c = format[ptr ++];
1958
1959                                 if (c == '{') {
1960                                         result.Append (format, start, ptr - start - 1);
1961
1962                                         // check for escaped open bracket
1963
1964                                         if (format[ptr] == '{') {
1965                                                 start = ptr ++;
1966                                                 continue;
1967                                         }
1968
1969                                         // parse specifier
1970                                 
1971                                         int n, width;
1972                                         bool left_align;
1973                                         string arg_format;
1974
1975                                         ParseFormatSpecifier (format, ref ptr, out n, out width, out left_align, out arg_format);
1976                                         if (n >= args.Length)
1977                                                 throw new FormatException ("Index (zero based) must be greater than or equal to zero and less than the size of the argument list.");
1978
1979                                         // format argument
1980
1981                                         object arg = args[n];
1982
1983                                         string str;
1984                                         ICustomFormatter formatter = null;
1985                                         if (provider != null)
1986                                                 formatter = provider.GetFormat (typeof (ICustomFormatter))
1987                                                         as ICustomFormatter;
1988                                         if (arg == null)
1989                                                 str = Empty;
1990                                         else if (formatter != null)
1991                                                 str = formatter.Format (arg_format, arg, provider);
1992                                         else if (arg is IFormattable)
1993                                                 str = ((IFormattable)arg).ToString (arg_format, provider);
1994                                         else
1995                                                 str = arg.ToString ();
1996
1997                                         // pad formatted string and append to result
1998
1999                                         if (width > str.length) {
2000                                                 const char padchar = ' ';
2001                                                 int padlen = width - str.length;
2002
2003                                                 if (left_align) {
2004                                                         result.Append (str);
2005                                                         result.Append (padchar, padlen);
2006                                                 }
2007                                                 else {
2008                                                         result.Append (padchar, padlen);
2009                                                         result.Append (str);
2010                                                 }
2011                                         }
2012                                         else
2013                                                 result.Append (str);
2014
2015                                         start = ptr;
2016                                 }
2017                                 else if (c == '}' && ptr < format.length && format[ptr] == '}') {
2018                                         result.Append (format, start, ptr - start - 1);
2019                                         start = ptr ++;
2020                                 }
2021                                 else if (c == '}') {
2022                                         throw new FormatException ("Input string was not in a correct format.");
2023                                 }
2024                         }
2025
2026                         if (start < format.length)
2027                                 result.Append (format, start, format.Length - start);
2028
2029                         return result;
2030                 }
2031
2032                 public unsafe static String Copy (String str)
2033                 {
2034                         if (str == null)
2035                                 throw new ArgumentNullException ("str");
2036
2037                         int length = str.length;
2038
2039                         String tmp = InternalAllocateStr (length);
2040                         if (length != 0) {
2041                                 fixed (char *dest = tmp, src = str) {
2042                                         CharCopy (dest, src, length);
2043                                 }
2044                         }
2045                         return tmp;
2046                 }
2047
2048                 public static String Concat (Object arg0)
2049                 {
2050                         if (arg0 == null)
2051                                 return Empty;
2052
2053                         return arg0.ToString ();
2054                 }
2055
2056                 public static String Concat (Object arg0, Object arg1)
2057                 {
2058                         return Concat ((arg0 != null) ? arg0.ToString () : null, (arg1 != null) ? arg1.ToString () : null);
2059                 }
2060
2061                 public static String Concat (Object arg0, Object arg1, Object arg2)
2062                 {
2063                         string s1, s2, s3;
2064                         if (arg0 == null)
2065                                 s1 = Empty;
2066                         else
2067                                 s1 = arg0.ToString ();
2068
2069                         if (arg1 == null)
2070                                 s2 = Empty;
2071                         else
2072                                 s2 = arg1.ToString ();
2073
2074                         if (arg2 == null)
2075                                 s3 = Empty;
2076                         else
2077                                 s3 = arg2.ToString ();
2078
2079                         return Concat (s1, s2, s3);
2080                 }
2081
2082                 [CLSCompliant(false)]
2083                 public static String Concat (Object arg0, Object arg1, Object arg2,
2084                                              Object arg3, __arglist)
2085                 {
2086                         string s1, s2, s3, s4;
2087
2088                         if (arg0 == null)
2089                                 s1 = Empty;
2090                         else
2091                                 s1 = arg0.ToString ();
2092
2093                         if (arg1 == null)
2094                                 s2 = Empty;
2095                         else
2096                                 s2 = arg1.ToString ();
2097
2098                         if (arg2 == null)
2099                                 s3 = Empty;
2100                         else
2101                                 s3 = arg2.ToString ();
2102
2103                         ArgIterator iter = new ArgIterator (__arglist);
2104                         int argCount = iter.GetRemainingCount();
2105
2106                         StringBuilder sb = new StringBuilder ();
2107                         if (arg3 != null)
2108                                 sb.Append (arg3.ToString ());
2109
2110                         for (int i = 0; i < argCount; i++) {
2111                                 TypedReference typedRef = iter.GetNextArg ();
2112                                 sb.Append (TypedReference.ToObject (typedRef));
2113                         }
2114
2115                         s4 = sb.ToString ();
2116
2117                         return Concat (s1, s2, s3, s4);                 
2118                 }
2119
2120                 public unsafe static String Concat (String str0, String str1)
2121                 {
2122                         if (str0 == null || str0.Length == 0) {
2123                                 if (str1 == null || str1.Length == 0)
2124                                         return Empty;
2125                                 return str1;
2126                         }
2127
2128                         if (str1 == null || str1.Length == 0)
2129                                 return str0; 
2130
2131                         String tmp = InternalAllocateStr (str0.length + str1.length);
2132
2133                         fixed (char *dest = tmp, src = str0)
2134                                 CharCopy (dest, src, str0.length);
2135                         fixed (char *dest = tmp, src = str1)
2136                                 CharCopy (dest + str0.Length, src, str1.length);
2137
2138                         return tmp;
2139                 }
2140
2141                 public unsafe static String Concat (String str0, String str1, String str2)
2142                 {
2143                         if (str0 == null || str0.Length == 0){
2144                                 if (str1 == null || str1.Length == 0){
2145                                         if (str2 == null || str2.Length == 0)
2146                                                 return Empty;
2147                                         return str2;
2148                                 } else {
2149                                         if (str2 == null || str2.Length == 0)
2150                                                 return str1;
2151                                 }
2152                                 str0 = Empty;
2153                         } else {
2154                                 if (str1 == null || str1.Length == 0){
2155                                         if (str2 == null || str2.Length == 0)
2156                                                 return str0;
2157                                         else
2158                                                 str1 = Empty;
2159                                 } else {
2160                                         if (str2 == null || str2.Length == 0)
2161                                                 str2 = Empty;
2162                                 }
2163                         }
2164
2165                         String tmp = InternalAllocateStr (str0.length + str1.length + str2.length);
2166
2167                         if (str0.Length != 0) {
2168                                 fixed (char *dest = tmp, src = str0) {
2169                                         CharCopy (dest, src, str0.length);
2170                                 }
2171                         }
2172                         if (str1.Length != 0) {
2173                                 fixed (char *dest = tmp, src = str1) {
2174                                         CharCopy (dest + str0.Length, src, str1.length);
2175                                 }
2176                         }
2177                         if (str2.Length != 0) {
2178                                 fixed (char *dest = tmp, src = str2) {
2179                                         CharCopy (dest + str0.Length + str1.Length, src, str2.length);
2180                                 }
2181                         }
2182
2183                         return tmp;
2184                 }
2185
2186                 public unsafe static String Concat (String str0, String str1, String str2, String str3)
2187                 {
2188                         if (str0 == null && str1 == null && str2 == null && str3 == null)
2189                                 return Empty;
2190
2191                         if (str0 == null)
2192                                 str0 = Empty;
2193                         if (str1 == null)
2194                                 str1 = Empty;
2195                         if (str2 == null)
2196                                 str2 = Empty;
2197                         if (str3 == null)
2198                                 str3 = Empty;
2199
2200                         String tmp = InternalAllocateStr (str0.length + str1.length + str2.length + str3.length);
2201
2202                         if (str0.Length != 0) {
2203                                 fixed (char *dest = tmp, src = str0) {
2204                                         CharCopy (dest, src, str0.length);
2205                                 }
2206                         }
2207                         if (str1.Length != 0) {
2208                                 fixed (char *dest = tmp, src = str1) {
2209                                         CharCopy (dest + str0.Length, src, str1.length);
2210                                 }
2211                         }
2212                         if (str2.Length != 0) {
2213                                 fixed (char *dest = tmp, src = str2) {
2214                                         CharCopy (dest + str0.Length + str1.Length, src, str2.length);
2215                                 }
2216                         }
2217                         if (str3.Length != 0) {
2218                                 fixed (char *dest = tmp, src = str3) {
2219                                         CharCopy (dest + str0.Length + str1.Length + str2.Length, src, str3.length);
2220                                 }
2221                         }
2222
2223                         return tmp;
2224                 }
2225
2226                 public static String Concat (params Object[] args)
2227                 {
2228                         if (args == null)
2229                                 throw new ArgumentNullException ("args");
2230
2231                         int argLen = args.Length;
2232                         if (argLen == 0)
2233                                 return Empty;
2234
2235                         string [] strings = new string [argLen];
2236                         int len = 0;
2237                         for (int i = 0; i < argLen; i++) {
2238                                 if (args[i] != null) {
2239                                         strings[i] = args[i].ToString ();
2240                                         len += strings[i].length;
2241                                 }
2242                         }
2243
2244                         return ConcatInternal (strings, len);
2245                 }
2246
2247                 public static String Concat (params String[] values)
2248                 {
2249                         if (values == null)
2250                                 throw new ArgumentNullException ("values");
2251
2252                         int len = 0;
2253                         for (int i = 0; i < values.Length; i++) {
2254                                 String s = values[i];
2255                                 if (s != null)
2256                                         len += s.length;
2257                         }
2258
2259                         return ConcatInternal (values, len);
2260                 }
2261
2262                 private static unsafe String ConcatInternal (String[] values, int length)
2263                 {
2264                         if (length == 0)
2265                                 return Empty;
2266
2267                         String tmp = InternalAllocateStr (length);
2268
2269                         fixed (char* dest = tmp) {
2270                                 int pos = 0;
2271                                 for (int i = 0; i < values.Length; i++) {
2272                                         String source = values[i];
2273                                         if (source != null) {
2274                                                 fixed (char* src = source) {
2275                                                         CharCopy (dest + pos, src, source.length);
2276                                                 }
2277                                                 pos += source.Length;
2278                                         }
2279                                 }
2280                         }
2281                         return tmp;
2282                 }
2283
2284                 public unsafe String Insert (int startIndex, String value)
2285                 {
2286                         if (value == null)
2287                                 throw new ArgumentNullException ("value");
2288
2289                         if (startIndex < 0 || startIndex > this.length)
2290                                 throw new ArgumentOutOfRangeException ("startIndex", "Cannot be negative and must be less than or equal to length of string.");
2291
2292                         if (value.Length == 0)
2293                                 return this;
2294                         if (this.Length == 0)
2295                                 return value;
2296                         String tmp = InternalAllocateStr (this.length + value.length);
2297
2298                         fixed (char *dest = tmp, src = this, val = value) {
2299                                 char *dst = dest;
2300                                 CharCopy (dst, src, startIndex);
2301                                 dst += startIndex;
2302                                 CharCopy (dst, val, value.length);
2303                                 dst += value.length;
2304                                 CharCopy (dst, src + startIndex, length - startIndex);
2305                         }
2306                         return tmp;
2307                 }
2308
2309                 public static string Intern (string str)
2310                 {
2311                         if (str == null)
2312                                 throw new ArgumentNullException ("str");
2313
2314                         return InternalIntern (str);
2315                 }
2316
2317                 public static string IsInterned (string str)
2318                 {
2319                         if (str == null)
2320                                 throw new ArgumentNullException ("str");
2321
2322                         return InternalIsInterned (str);
2323                 }
2324         
2325 #if NET_4_0 || MOONLIGHT || MOBILE
2326                 public static string Join (string separator, params string [] value)
2327 #else
2328                 public static string Join (string separator, string [] value)
2329 #endif
2330                 {
2331                         if (value == null)
2332                                 throw new ArgumentNullException ("value");
2333                         if (separator == null)
2334                                 separator = Empty;
2335
2336                         return JoinUnchecked (separator, value, 0, value.Length);
2337                 }
2338
2339                 public static string Join (string separator, string[] value, int startIndex, int count)
2340                 {
2341                         if (value == null)
2342                                 throw new ArgumentNullException ("value");
2343                         if (startIndex < 0)
2344                                 throw new ArgumentOutOfRangeException ("startIndex", "< 0");
2345                         if (count < 0)
2346                                 throw new ArgumentOutOfRangeException ("count", "< 0");
2347                         if (startIndex > value.Length - count)
2348                                 throw new ArgumentOutOfRangeException ("startIndex", "startIndex + count > value.length");
2349
2350                         if (startIndex == value.Length)
2351                                 return Empty;
2352                         if (separator == null)
2353                                 separator = Empty;
2354
2355                         return JoinUnchecked (separator, value, startIndex, count);
2356                 }
2357
2358                 private static unsafe string JoinUnchecked (string separator, string[] value, int startIndex, int count)
2359                 {
2360                         // Unchecked parameters
2361                         // startIndex, count must be >= 0; startIndex + count must be <= value.length
2362                         // separator and value must not be null
2363
2364                         int length = 0;
2365                         int maxIndex = startIndex + count;
2366                         // Precount the number of characters that the resulting string will have
2367                         for (int i = startIndex; i < maxIndex; i++) {
2368                                 String s = value[i];
2369                                 if (s != null)
2370                                         length += s.length;
2371                         }
2372                         length += separator.length * (count - 1);
2373                         if (length <= 0)
2374                                 return Empty;
2375
2376                         String tmp = InternalAllocateStr (length);
2377
2378                         maxIndex--;
2379                         fixed (char* dest = tmp, sepsrc = separator) {
2380                                 // Copy each string from value except the last one and add a separator for each
2381                                 int pos = 0;
2382                                 for (int i = startIndex; i < maxIndex; i++) {
2383                                         String source = value[i];
2384                                         if (source != null) {
2385                                                 if (source.Length > 0) {
2386                                                         fixed (char* src = source)
2387                                                                 CharCopy (dest + pos, src, source.Length);
2388                                                         pos += source.Length;
2389                                                 }
2390                                         }
2391                                         if (separator.Length > 0) {
2392                                                 CharCopy (dest + pos, sepsrc, separator.Length);
2393                                                 pos += separator.Length;
2394                                         }
2395                                 }
2396                                 // Append last string that does not get an additional separator
2397                                 String sourceLast = value[maxIndex];
2398                                 if (sourceLast != null) {
2399                                         if (sourceLast.Length > 0) {
2400                                                 fixed (char* src = sourceLast)
2401                                                         CharCopy (dest + pos, src, sourceLast.Length);
2402                                         }
2403                                 }
2404                         }
2405                         return tmp;
2406                 }
2407
2408                 bool IConvertible.ToBoolean (IFormatProvider provider)
2409                 {
2410                         return Convert.ToBoolean (this, provider);
2411                 }
2412
2413                 byte IConvertible.ToByte (IFormatProvider provider)
2414                 {
2415                         return Convert.ToByte (this, provider);
2416                 }
2417
2418                 char IConvertible.ToChar (IFormatProvider provider)
2419                 {
2420                         return Convert.ToChar (this, provider);
2421                 }
2422
2423                 DateTime IConvertible.ToDateTime (IFormatProvider provider)
2424                 {
2425                         return Convert.ToDateTime (this, provider);
2426                 }
2427
2428                 decimal IConvertible.ToDecimal (IFormatProvider provider)
2429                 {
2430                         return Convert.ToDecimal (this, provider);
2431                 }
2432
2433                 double IConvertible.ToDouble (IFormatProvider provider)
2434                 {
2435                         return Convert.ToDouble (this, provider);
2436                 }
2437
2438                 short IConvertible.ToInt16 (IFormatProvider provider)
2439                 {
2440                         return Convert.ToInt16 (this, provider);
2441                 }
2442
2443                 int IConvertible.ToInt32 (IFormatProvider provider)
2444                 {
2445                         return Convert.ToInt32 (this, provider);
2446                 }
2447
2448                 long IConvertible.ToInt64 (IFormatProvider provider)
2449                 {
2450                         return Convert.ToInt64 (this, provider);
2451                 }
2452
2453                 sbyte IConvertible.ToSByte (IFormatProvider provider)
2454                 {
2455                         return Convert.ToSByte (this, provider);
2456                 }
2457
2458                 float IConvertible.ToSingle (IFormatProvider provider)
2459                 {
2460                         return Convert.ToSingle (this, provider);
2461                 }
2462
2463                 object IConvertible.ToType (Type targetType, IFormatProvider provider)
2464                 {
2465                         if (targetType == null)
2466                                 throw new ArgumentNullException ("type");
2467                         return Convert.ToType (this, targetType, provider, false);
2468                 }
2469
2470                 ushort IConvertible.ToUInt16 (IFormatProvider provider)
2471                 {
2472                         return Convert.ToUInt16 (this, provider);
2473                 }
2474
2475                 uint IConvertible.ToUInt32 (IFormatProvider provider)
2476                 {
2477                         return Convert.ToUInt32 (this, provider);
2478                 }
2479
2480                 ulong IConvertible.ToUInt64 (IFormatProvider provider)
2481                 {
2482                         return Convert.ToUInt64 (this, provider);
2483                 }
2484
2485                 public int Length {
2486                         get {
2487                                 return length;
2488                         }
2489                 }
2490
2491                 public CharEnumerator GetEnumerator ()
2492                 {
2493                         return new CharEnumerator (this);
2494                 }
2495
2496                 IEnumerator<char> IEnumerable<char>.GetEnumerator ()
2497                 {
2498                         return new CharEnumerator (this);
2499                 }
2500
2501                 IEnumerator IEnumerable.GetEnumerator ()
2502                 {
2503                         return new CharEnumerator (this);
2504                 }
2505
2506                 private static void ParseFormatSpecifier (string str, ref int ptr, out int n, out int width,
2507                                                           out bool left_align, out string format)
2508                 {
2509                         int max = str.Length;
2510                         
2511                         // parses format specifier of form:
2512                         //   N,[\ +[-]M][:F]}
2513                         //
2514                         // where:
2515                         // N = argument number (non-negative integer)
2516                         
2517                         n = ParseDecimal (str, ref ptr);
2518                         if (n < 0)
2519                                 throw new FormatException ("Input string was not in a correct format.");
2520                         
2521                         // M = width (non-negative integer)
2522                         
2523                         if (ptr < max && str[ptr] == ',') {
2524                                 // White space between ',' and number or sign.
2525                                 ++ptr;
2526                                 while (ptr < max && Char.IsWhiteSpace (str [ptr]))
2527                                         ++ptr;
2528                                 int start = ptr;
2529                                 
2530                                 format = str.Substring (start, ptr - start);
2531                                 
2532                                 left_align = (ptr < max && str [ptr] == '-');
2533                                 if (left_align)
2534                                         ++ ptr;
2535                                 
2536                                 width = ParseDecimal (str, ref ptr);
2537                                 if (width < 0)
2538                                         throw new FormatException ("Input string was not in a correct format.");
2539                         }
2540                         else {
2541                                 width = 0;
2542                                 left_align = false;
2543                                 format = Empty;
2544                         }
2545                         
2546                         // F = argument format (string)
2547                         
2548                         if (ptr < max && str[ptr] == ':') {
2549                                 int start = ++ ptr;
2550                                 while (ptr < max && str[ptr] != '}')
2551                                         ++ ptr;
2552                                 
2553                                 format += str.Substring (start, ptr - start);
2554                         }
2555                         else
2556                                 format = null;
2557                         
2558                         if ((ptr >= max) || str[ptr ++] != '}')
2559                                 throw new FormatException ("Input string was not in a correct format.");
2560                 }
2561
2562                 private static int ParseDecimal (string str, ref int ptr)
2563                 {
2564                         int p = ptr;
2565                         int n = 0;
2566                         int max = str.Length;
2567                         
2568                         while (p < max) {
2569                                 char c = str[p];
2570                                 if (c < '0' || '9' < c)
2571                                         break;
2572
2573                                 n = n * 10 + c - '0';
2574                                 ++ p;
2575                         }
2576
2577                         if (p == ptr || p == max)
2578                                 return -1;
2579
2580                         ptr = p;
2581                         return n;
2582                 }
2583
2584                 internal unsafe void InternalSetChar (int idx, char val)
2585                 {
2586                         if ((uint) idx >= (uint) Length)
2587                                 throw new ArgumentOutOfRangeException ("idx");
2588
2589                         fixed (char * pStr = &start_char) 
2590                         {
2591                                 pStr [idx] = val;
2592                         }
2593                 }
2594
2595                 internal unsafe void InternalSetLength (int newLength)
2596                 {
2597                         if (newLength > length)
2598                                 throw new ArgumentOutOfRangeException ("newLength", "newLength as to be <= length");
2599
2600                         // zero terminate, we can pass string objects directly via pinvoke
2601                         // we also zero the rest of the string, since the new GC needs to be
2602                         // able to handle the changing size (it will skip the 0 bytes).
2603                         fixed (char * pStr = &start_char) {
2604                                 char *p = pStr + newLength;
2605                                 char *end = pStr + length;
2606                                 while (p < end) {
2607                                         p [0] = '\0';
2608                                         p++;
2609                                 }
2610                         }
2611                         length = newLength;
2612                 }
2613
2614                 [ReliabilityContractAttribute (Consistency.WillNotCorruptState, Cer.MayFail)]
2615                 // When modifying it, GetCaseInsensitiveHashCode() should be modified as well.
2616                 public unsafe override int GetHashCode ()
2617                 {
2618                         fixed (char * c = this) {
2619                                 char * cc = c;
2620                                 char * end = cc + length - 1;
2621                                 int h = 0;
2622                                 for (;cc < end; cc += 2) {
2623                                         h = (h << 5) - h + *cc;
2624                                         h = (h << 5) - h + cc [1];
2625                                 }
2626                                 ++end;
2627                                 if (cc < end)
2628                                         h = (h << 5) - h + *cc;
2629                                 return h;
2630                         }
2631                 }
2632
2633 #if MOONLIGHT || MOBILE || NET_4_0
2634                 [ComVisible(false)]
2635                 public static string Concat (IEnumerable<string> values)
2636                 {
2637                         if (values == null)
2638                                 throw new ArgumentNullException ("values");
2639
2640                         var stringList = new List<string> ();
2641                         int len = 0;
2642                         foreach (var v in values){
2643                                 if (v == null)
2644                                         continue;
2645                                 len += v.Length;
2646                                 stringList.Add (v);
2647                         }
2648                         return ConcatInternal (stringList.ToArray (), len);
2649                 }
2650
2651                 [ComVisibleAttribute(false)]
2652                 public static string Concat<T> (IEnumerable<T> values)
2653                 {
2654                         if (values == null)
2655                                 throw new ArgumentNullException ("values");
2656
2657                         var stringList = new List<string> ();
2658                         int len = 0;
2659                         foreach (var v in values){
2660                                 string sr = v.ToString ();
2661                                 len += sr.Length;
2662                                 stringList.Add (sr);
2663                         }
2664                         return ConcatInternal (stringList.ToArray (), len);
2665                 }
2666
2667                 [ComVisibleAttribute(false)]
2668                 public static string Join (string separator, IEnumerable<string> values)
2669                 {
2670                         if (separator == null)
2671                                 return Concat (values);
2672                         
2673                         if (values == null)
2674                                 throw new ArgumentNullException ("values");
2675                         
2676                         var stringList = new List<string> ();
2677                         foreach (var v in values)
2678                                 stringList.Add (v);
2679
2680                         return JoinUnchecked (separator, stringList.ToArray (), 0, stringList.Count);
2681                 }
2682
2683                 [ComVisibleAttribute(false)]
2684                 public static string Join (string separator, params object [] values)
2685                 {
2686                         if (separator == null)
2687                                 return Concat (values);
2688                         
2689                         if (values == null)
2690                                 throw new ArgumentNullException ("values");
2691
2692                         var strCopy = new string [values.Length];
2693                         int i = 0;
2694                         foreach (var v in values)
2695                                 strCopy [i++] = v.ToString ();
2696
2697                         return JoinUnchecked (separator, strCopy, 0, strCopy.Length);
2698                 }
2699                 
2700                 [ComVisible (false)]
2701                 public static string Join<T> (string separator, IEnumerable<T> values)
2702                 {
2703                         if (separator == null)
2704                                 return Concat<T> (values);
2705                                 
2706                         if (values == null)
2707                                 throw new ArgumentNullException ("values");
2708                         
2709                         var stringList = new List<string> ();
2710                         foreach (var v in values)
2711                                 stringList.Add (v.ToString ());
2712
2713                         return JoinUnchecked (separator, stringList.ToArray (), 0, stringList.Count);
2714                 }
2715
2716                 public static bool IsNullOrWhiteSpace (string value)
2717 #else
2718                 internal static bool IsNullOrWhiteSpace (string value)
2719 #endif
2720                 {
2721                         if ((value == null) || (value.Length == 0))
2722                                 return true;
2723                         foreach (char c in value)
2724                                 if (!Char.IsWhiteSpace (c))
2725                                         return false;
2726                         return true;
2727                 }
2728
2729                 internal unsafe int GetCaseInsensitiveHashCode ()
2730                 {
2731                         fixed (char * c = this) {
2732                                 char * cc = c;
2733                                 char * end = cc + length - 1;
2734                                 int h = 0;
2735                                 for (;cc < end; cc += 2) {
2736                                         h = (h << 5) - h + Char.ToUpperInvariant (*cc);
2737                                         h = (h << 5) - h + Char.ToUpperInvariant (cc [1]);
2738                                 }
2739                                 ++end;
2740                                 if (cc < end)
2741                                         h = (h << 5) - h + Char.ToUpperInvariant (*cc);
2742                                 return h;
2743                         }
2744                 }
2745
2746                 // Certain constructors are redirected to CreateString methods with
2747                 // matching argument list. The this pointer should not be used.
2748
2749                 private unsafe String CreateString (sbyte* value)
2750                 {
2751                         if (value == null)
2752                                 return Empty;
2753
2754                         byte* bytes = (byte*) value;
2755                         int length = 0;
2756
2757                         try {
2758                                 while (bytes++ [0] != 0)
2759                                         length++;
2760                         } catch (NullReferenceException) {
2761                                 throw new ArgumentOutOfRangeException ("ptr", "Value does not refer to a valid string.");
2762                         } catch (AccessViolationException) {
2763                                 throw new ArgumentOutOfRangeException ("ptr", "Value does not refer to a valid string.");
2764                 }
2765
2766                         return CreateString (value, 0, length, null);
2767                 }
2768
2769                 private unsafe String CreateString (sbyte* value, int startIndex, int length)
2770                 {
2771                         return CreateString (value, startIndex, length, null);
2772                 }
2773
2774                 private unsafe String CreateString (sbyte* value, int startIndex, int length, Encoding enc)
2775                 {
2776                         if (length < 0)
2777                                 throw new ArgumentOutOfRangeException ("length", "Non-negative number required.");
2778                         if (startIndex < 0)
2779                                 throw new ArgumentOutOfRangeException ("startIndex", "Non-negative number required.");
2780                         if (value + startIndex < value)
2781                                 throw new ArgumentOutOfRangeException ("startIndex", "Value, startIndex and length do not refer to a valid string.");
2782
2783                         bool isDefaultEncoding;
2784
2785                         if (isDefaultEncoding = (enc == null)) {
2786                                 if (value == null)
2787                                         throw new ArgumentNullException ("value");
2788                                 if (length == 0)
2789                                         return Empty;
2790
2791                                 enc = Encoding.Default;
2792                         }
2793
2794                         byte [] bytes = new byte [length];
2795
2796                         if (length != 0)
2797                                 fixed (byte* bytePtr = bytes)
2798                                         try {
2799                                                 memcpy (bytePtr, (byte*) (value + startIndex), length);
2800                                         } catch (NullReferenceException) {
2801                                                 throw new ArgumentOutOfRangeException ("ptr", "Value, startIndex and length do not refer to a valid string.");
2802                                         } catch (AccessViolationException) {
2803                                                 if (!isDefaultEncoding)
2804                                                         throw;
2805
2806                                                 throw new ArgumentOutOfRangeException ("value", "Value, startIndex and length do not refer to a valid string.");
2807                                         }
2808
2809                         // GetString () is called even when length == 0
2810                         return enc.GetString (bytes);
2811                 }
2812
2813                 unsafe string CreateString (char *value)
2814                 {
2815                         if (value == null)
2816                                 return Empty;
2817                         char *p = value;
2818                         int i = 0;
2819                         while (*p != 0) {
2820                                 ++i;
2821                                 ++p;
2822                         }
2823                         string result = InternalAllocateStr (i);
2824
2825                         if (i != 0) {
2826                                 fixed (char *dest = result) {
2827                                         CharCopy (dest, value, i);
2828                                 }
2829                         }
2830                         return result;
2831                 }
2832
2833                 unsafe string CreateString (char *value, int startIndex, int length)
2834                 {
2835                         if (length == 0)
2836                                 return Empty;
2837                         if (value == null)
2838                                 throw new ArgumentNullException ("value");
2839                         if (startIndex < 0)
2840                                 throw new ArgumentOutOfRangeException ("startIndex");
2841                         if (length < 0)
2842                                 throw new ArgumentOutOfRangeException ("length");
2843
2844                         string result = InternalAllocateStr (length);
2845
2846                         fixed (char *dest = result) {
2847                                 CharCopy (dest, value + startIndex, length);
2848                         }
2849                         return result;
2850                 }
2851
2852                 unsafe string CreateString (char [] val, int startIndex, int length)
2853                 {
2854                         if (val == null)
2855                                 throw new ArgumentNullException ("value");
2856                         if (startIndex < 0)
2857                                 throw new ArgumentOutOfRangeException ("startIndex", "Cannot be negative.");
2858                         if (length < 0)
2859                                 throw new ArgumentOutOfRangeException ("length", "Cannot be negative.");
2860                         if (startIndex > val.Length - length)
2861                                 throw new ArgumentOutOfRangeException ("startIndex", "Cannot be negative, and should be less than length of string.");
2862                         if (length == 0)
2863                                 return Empty;
2864
2865                         string result = InternalAllocateStr (length);
2866
2867                         fixed (char *dest = result, src = val) {
2868                                 CharCopy (dest, src + startIndex, length);
2869                         }
2870                         return result;
2871                 }
2872
2873                 unsafe string CreateString (char [] val)
2874                 {
2875                         if (val == null || val.Length == 0)
2876                                 return Empty;
2877                         string result = InternalAllocateStr (val.Length);
2878
2879                         fixed (char *dest = result, src = val) {
2880                                 CharCopy (dest, src, val.Length);
2881                         }
2882                         return result;
2883                 }
2884
2885                 unsafe string CreateString (char c, int count)
2886                 {
2887                         if (count < 0)
2888                                 throw new ArgumentOutOfRangeException ("count");
2889                         if (count == 0)
2890                                 return Empty;
2891                         string result = InternalAllocateStr (count);
2892                         fixed (char *dest = result) {
2893                                 char *p = dest;
2894                                 char *end = p + count;
2895                                 while (p < end) {
2896                                         *p = c;
2897                                         p++;
2898                                 }
2899                         }
2900                         return result;
2901                 }
2902
2903                 /* helpers used by the runtime as well as above or eslewhere in corlib */
2904                 internal static unsafe void memset (byte *dest, int val, int len)
2905                 {
2906                         if (len < 8) {
2907                                 while (len != 0) {
2908                                         *dest = (byte)val;
2909                                         ++dest;
2910                                         --len;
2911                                 }
2912                                 return;
2913                         }
2914                         if (val != 0) {
2915                                 val = val | (val << 8);
2916                                 val = val | (val << 16);
2917                         }
2918                         // align to 4
2919                         int rest = (int)dest & 3;
2920                         if (rest != 0) {
2921                                 rest = 4 - rest;
2922                                 len -= rest;
2923                                 do {
2924                                         *dest = (byte)val;
2925                                         ++dest;
2926                                         --rest;
2927                                 } while (rest != 0);
2928                         }
2929                         while (len >= 16) {
2930                                 ((int*)dest) [0] = val;
2931                                 ((int*)dest) [1] = val;
2932                                 ((int*)dest) [2] = val;
2933                                 ((int*)dest) [3] = val;
2934                                 dest += 16;
2935                                 len -= 16;
2936                         }
2937                         while (len >= 4) {
2938                                 ((int*)dest) [0] = val;
2939                                 dest += 4;
2940                                 len -= 4;
2941                         }
2942                         // tail bytes
2943                         while (len > 0) {
2944                                 *dest = (byte)val;
2945                                 dest++;
2946                                 len--;
2947                         }
2948                 }
2949
2950                 static unsafe void memcpy4 (byte *dest, byte *src, int size) {
2951                         /*while (size >= 32) {
2952                                 // using long is better than int and slower than double
2953                                 // FIXME: enable this only on correct alignment or on platforms
2954                                 // that can tolerate unaligned reads/writes of doubles
2955                                 ((double*)dest) [0] = ((double*)src) [0];
2956                                 ((double*)dest) [1] = ((double*)src) [1];
2957                                 ((double*)dest) [2] = ((double*)src) [2];
2958                                 ((double*)dest) [3] = ((double*)src) [3];
2959                                 dest += 32;
2960                                 src += 32;
2961                                 size -= 32;
2962                         }*/
2963                         while (size >= 16) {
2964                                 ((int*)dest) [0] = ((int*)src) [0];
2965                                 ((int*)dest) [1] = ((int*)src) [1];
2966                                 ((int*)dest) [2] = ((int*)src) [2];
2967                                 ((int*)dest) [3] = ((int*)src) [3];
2968                                 dest += 16;
2969                                 src += 16;
2970                                 size -= 16;
2971                         }
2972                         while (size >= 4) {
2973                                 ((int*)dest) [0] = ((int*)src) [0];
2974                                 dest += 4;
2975                                 src += 4;
2976                                 size -= 4;
2977                         }
2978                         while (size > 0) {
2979                                 ((byte*)dest) [0] = ((byte*)src) [0];
2980                                 dest += 1;
2981                                 src += 1;
2982                                 --size;
2983                         }
2984                 }
2985                 static unsafe void memcpy2 (byte *dest, byte *src, int size) {
2986                         while (size >= 8) {
2987                                 ((short*)dest) [0] = ((short*)src) [0];
2988                                 ((short*)dest) [1] = ((short*)src) [1];
2989                                 ((short*)dest) [2] = ((short*)src) [2];
2990                                 ((short*)dest) [3] = ((short*)src) [3];
2991                                 dest += 8;
2992                                 src += 8;
2993                                 size -= 8;
2994                         }
2995                         while (size >= 2) {
2996                                 ((short*)dest) [0] = ((short*)src) [0];
2997                                 dest += 2;
2998                                 src += 2;
2999                                 size -= 2;
3000                         }
3001                         if (size > 0)
3002                                 ((byte*)dest) [0] = ((byte*)src) [0];
3003                 }
3004                 static unsafe void memcpy1 (byte *dest, byte *src, int size) {
3005                         while (size >= 8) {
3006                                 ((byte*)dest) [0] = ((byte*)src) [0];
3007                                 ((byte*)dest) [1] = ((byte*)src) [1];
3008                                 ((byte*)dest) [2] = ((byte*)src) [2];
3009                                 ((byte*)dest) [3] = ((byte*)src) [3];
3010                                 ((byte*)dest) [4] = ((byte*)src) [4];
3011                                 ((byte*)dest) [5] = ((byte*)src) [5];
3012                                 ((byte*)dest) [6] = ((byte*)src) [6];
3013                                 ((byte*)dest) [7] = ((byte*)src) [7];
3014                                 dest += 8;
3015                                 src += 8;
3016                                 size -= 8;
3017                         }
3018                         while (size >= 2) {
3019                                 ((byte*)dest) [0] = ((byte*)src) [0];
3020                                 ((byte*)dest) [1] = ((byte*)src) [1];
3021                                 dest += 2;
3022                                 src += 2;
3023                                 size -= 2;
3024                         }
3025                         if (size > 0)
3026                                 ((byte*)dest) [0] = ((byte*)src) [0];
3027                 }
3028
3029                 internal static unsafe void memcpy (byte *dest, byte *src, int size) {
3030                         // FIXME: if pointers are not aligned, try to align them
3031                         // so a faster routine can be used. Handle the case where
3032                         // the pointers can't be reduced to have the same alignment
3033                         // (just ignore the issue on x86?)
3034                         if ((((int)dest | (int)src) & 3) != 0) {
3035                                 if (((int)dest & 1) != 0 && ((int)src & 1) != 0 && size >= 1) {
3036                                         dest [0] = src [0];
3037                                         ++dest;
3038                                         ++src;
3039                                         --size;
3040                                 }
3041                                 if (((int)dest & 2) != 0 && ((int)src & 2) != 0 && size >= 2) {
3042                                         ((short*)dest) [0] = ((short*)src) [0];
3043                                         dest += 2;
3044                                         src += 2;
3045                                         size -= 2;
3046                                 }
3047                                 if ((((int)dest | (int)src) & 1) != 0) {
3048                                         memcpy1 (dest, src, size);
3049                                         return;
3050                                 }
3051                                 if ((((int)dest | (int)src) & 2) != 0) {
3052                                         memcpy2 (dest, src, size);
3053                                         return;
3054                                 }
3055                         }
3056                         memcpy4 (dest, src, size);
3057                 }
3058
3059                 internal static unsafe void CharCopy (char *dest, char *src, int count) {
3060                         // Same rules as for memcpy, but with the premise that 
3061                         // chars can only be aligned to even addresses if their
3062                         // enclosing types are correctly aligned
3063                         if ((((int)(byte*)dest | (int)(byte*)src) & 3) != 0) {
3064                                 if (((int)(byte*)dest & 2) != 0 && ((int)(byte*)src & 2) != 0 && count > 0) {
3065                                         ((short*)dest) [0] = ((short*)src) [0];
3066                                         dest++;
3067                                         src++;
3068                                         count--;
3069                                 }
3070                                 if ((((int)(byte*)dest | (int)(byte*)src) & 2) != 0) {
3071                                         memcpy2 ((byte*)dest, (byte*)src, count * 2);
3072                                         return;
3073                                 }
3074                         }
3075                         memcpy4 ((byte*)dest, (byte*)src, count * 2);
3076                 }
3077
3078                 internal static unsafe void CharCopyReverse (char *dest, char *src, int count)
3079                 {
3080                         dest += count;
3081                         src += count;
3082                         for (int i = count; i > 0; i--) {
3083                                 dest--;
3084                                 src--;
3085                                 *dest = *src;
3086                         }       
3087                 }
3088
3089                 internal static unsafe void CharCopy (String target, int targetIndex, String source, int sourceIndex, int count)
3090                 {
3091                         fixed (char* dest = target, src = source)
3092                                 CharCopy (dest + targetIndex, src + sourceIndex, count);
3093                 }
3094
3095                 internal static unsafe void CharCopy (String target, int targetIndex, Char[] source, int sourceIndex, int count)
3096                 {
3097                         fixed (char* dest = target, src = source)
3098                                 CharCopy (dest + targetIndex, src + sourceIndex, count);
3099                 }
3100
3101                 // Use this method if you cannot block copy from left to right (e.g. because you are coping within the same string)
3102                 internal static unsafe void CharCopyReverse (String target, int targetIndex, String source, int sourceIndex, int count)
3103                 {
3104                         fixed (char* dest = target, src = source)
3105                                 CharCopyReverse (dest + targetIndex, src + sourceIndex, count);
3106                 }
3107
3108                 [CLSCompliant (false), MethodImplAttribute (MethodImplOptions.InternalCall)]
3109                 unsafe public extern String (char *value);
3110
3111                 [CLSCompliant (false), MethodImplAttribute (MethodImplOptions.InternalCall)]
3112                 unsafe public extern String (char *value, int startIndex, int length);
3113
3114                 [CLSCompliant (false), MethodImplAttribute (MethodImplOptions.InternalCall)]
3115                 unsafe public extern String (sbyte *value);
3116
3117                 [CLSCompliant (false), MethodImplAttribute (MethodImplOptions.InternalCall)]
3118                 unsafe public extern String (sbyte *value, int startIndex, int length);
3119
3120                 [CLSCompliant (false), MethodImplAttribute (MethodImplOptions.InternalCall)]
3121                 unsafe public extern String (sbyte *value, int startIndex, int length, Encoding enc);
3122
3123                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
3124                 public extern String (char [] value, int startIndex, int length);
3125
3126                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
3127                 public extern String (char [] value);
3128
3129                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
3130                 public extern String (char c, int count);
3131
3132                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
3133                 internal extern static String InternalAllocateStr (int length);
3134
3135                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
3136                 private extern static string InternalIntern (string str);
3137
3138                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
3139                 private extern static string InternalIsInterned (string str);
3140
3141                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
3142                 private extern static int GetLOSLimit ();
3143         }
3144 }