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