2008-12-18 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
638                 public static int Compare (string strA, string strB, CultureInfo culture, CompareOptions options)
639                 {
640                         if (culture == null)
641                                 throw new ArgumentNullException ("culture");
642
643                         return culture.CompareInfo.Compare (strA, strB, options);
644                 }
645
646                 public static int Compare (string strA, int indexA, string strB, int indexB, int length, CultureInfo culture, CompareOptions options)
647                 {
648                         if (culture == null)
649                                 throw new ArgumentNullException ("culture");
650
651                         int len1 = length;
652                         int len2 = length;
653                         
654                         if (length > (strA.Length - indexA))
655                                 len1 = strA.Length - indexA;
656
657                         if (length > (strB.Length - indexB))
658                                 len2 = strB.Length - indexB;
659
660                         return culture.CompareInfo.Compare (strA, indexA, len1, strB, indexB, len2, options);
661                 }
662 #endif
663
664                 public int CompareTo (Object value)
665                 {
666                         if (value == null)
667                                 return 1;
668
669                         if (!(value is String))
670                                 throw new ArgumentException ();
671
672                         return String.Compare (this, (String) value);
673                 }
674
675                 public int CompareTo (String strB)
676                 {
677                         if (strB == null)
678                                 return 1;
679
680                         return Compare (this, strB);
681                 }
682
683                 public static int CompareOrdinal (String strA, String strB)
684                 {
685                         return CompareOrdinalUnchecked (strA, 0, Int32.MaxValue, strB, 0, Int32.MaxValue);
686                 }
687
688                 public static int CompareOrdinal (String strA, int indexA, String strB, int indexB, int length)
689                 {
690                         if ((indexA > strA.Length) || (indexB > strB.Length) || (indexA < 0) || (indexB < 0) || (length < 0))
691                                 throw new ArgumentOutOfRangeException ();
692
693                         return CompareOrdinalUnchecked (strA, indexA, length, strB, indexB, length);
694                 }
695
696                 internal static int CompareOrdinalCaseInsensitive (String strA, int indexA, String strB, int indexB, int length)
697                 {
698                         if ((indexA > strA.Length) || (indexB > strB.Length) || (indexA < 0) || (indexB < 0) || (length < 0))
699                                 throw new ArgumentOutOfRangeException ();
700
701                         return CompareOrdinalCaseInsensitiveUnchecked (strA, indexA, length, strB, indexB, length);
702                 }
703
704                 internal static unsafe int CompareOrdinalUnchecked (String strA, int indexA, int lenA, String strB, int indexB, int lenB)
705                 {
706                         if (strA == null) {
707                                 if (strB == null)
708                                         return 0;
709                                 else
710                                         return -1;
711                         } else if (strB == null) {
712                                 return 1;
713                         }
714                         int lengthA = Math.Min (lenA, strA.Length - indexA);
715                         int lengthB = Math.Min (lenB, strB.Length - indexB);
716
717                         if (lengthA == lengthB && Object.ReferenceEquals (strA, strB))
718                                 return 0;
719
720                         fixed (char* aptr = strA, bptr = strB) {
721                                 char* ap = aptr + indexA;
722                                 char* end = ap + Math.Min (lengthA, lengthB);
723                                 char* bp = bptr + indexB;
724                                 while (ap < end) {
725                                         if (*ap != *bp)
726                                                 return *ap - *bp;
727                                         ap++;
728                                         bp++;
729                                 }
730                                 return lengthA - lengthB;
731                         }
732                 }
733
734                 internal static unsafe int CompareOrdinalCaseInsensitiveUnchecked (String strA, int indexA, int lenA, String strB, int indexB, int lenB)
735                 {
736                         // Same as above, but checks versus uppercase characters
737                         if (strA == null) {
738                                 if (strB == null)
739                                         return 0;
740                                 else
741                                         return -1;
742                         } else if (strB == null) {
743                                 return 1;
744                         }
745                         int lengthA = Math.Min (lenA, strA.Length - indexA);
746                         int lengthB = Math.Min (lenB, strB.Length - indexB);
747
748                         if (lengthA == lengthB && Object.ReferenceEquals (strA, strB))
749                                 return 0;
750
751                         fixed (char* aptr = strA, bptr = strB) {
752                                 char* ap = aptr + indexA;
753                                 char* end = ap + Math.Min (lengthA, lengthB);
754                                 char* bp = bptr + indexB;
755                                 while (ap < end) {
756                                         if (*ap != *bp) {
757                                                 char c1 = Char.ToUpperInvariant (*ap);
758                                                 char c2 = Char.ToUpperInvariant (*bp);
759                                                 if (c1 != c2)
760                                                         return c1 - c2;
761                                         }
762                                         ap++;
763                                         bp++;
764                                 }
765                                 return lengthA - lengthB;
766                         }
767                 }
768
769                 public bool EndsWith (String value)
770                 {
771                         if (value == null)
772                                 throw new ArgumentNullException ("value");
773
774                         return CultureInfo.CurrentCulture.CompareInfo.IsSuffix (this, value, CompareOptions.None);
775                 }
776
777 #if NET_2_0
778                 public
779 #else
780                 internal
781 #endif
782                 bool EndsWith (String value, bool ignoreCase, CultureInfo culture)
783                 {
784                         if (value == null)
785                                 throw new ArgumentNullException ("value");
786                         if (culture == null)
787                                 culture = CultureInfo.CurrentCulture;
788
789                         return culture.CompareInfo.IsSuffix (this, value,
790                                 ignoreCase ? CompareOptions.IgnoreCase : CompareOptions.None);
791                 }
792
793                 // Following methods are culture-insensitive
794                 public int IndexOfAny (char [] anyOf)
795                 {
796                         if (anyOf == null)
797                                 throw new ArgumentNullException ();
798                         if (this.length == 0)
799                                 return -1;
800
801                         return IndexOfAnyUnchecked (anyOf, 0, this.length);
802                 }
803
804                 public int IndexOfAny (char [] anyOf, int startIndex)
805                 {
806                         if (anyOf == null)
807                                 throw new ArgumentNullException ();
808                         if (startIndex < 0 || startIndex > this.length)
809                                 throw new ArgumentOutOfRangeException ();
810
811                         return IndexOfAnyUnchecked (anyOf, startIndex, this.length - startIndex);
812                 }
813
814                 public int IndexOfAny (char [] anyOf, int startIndex, int count)
815                 {
816                         if (anyOf == null)
817                                 throw new ArgumentNullException ();
818                         if (startIndex < 0 || startIndex > this.length)
819                                 throw new ArgumentOutOfRangeException ();
820                         if (count < 0 || startIndex > this.length - count)
821                                 throw new ArgumentOutOfRangeException ("count", "Count cannot be negative, and startIndex + count must be less than length of the string.");
822
823                         return IndexOfAnyUnchecked (anyOf, startIndex, count);
824                 }
825
826                 private unsafe int IndexOfAnyUnchecked (char[] anyOf, int startIndex, int count)
827                 {
828                         if (anyOf.Length == 0)
829                                 return -1;
830
831                         if (anyOf.Length == 1)
832                                 return IndexOfUnchecked (anyOf[0], startIndex, count);
833
834                         fixed (char* any = anyOf) {
835                                 int highest = *any;
836                                 int lowest = *any;
837
838                                 char* end_any_ptr = any + anyOf.Length;
839                                 char* any_ptr = any;
840                                 while (++any_ptr != end_any_ptr) {
841                                         if (*any_ptr > highest) {
842                                                 highest = *any_ptr;
843                                                 continue;
844                                         }
845
846                                         if (*any_ptr < lowest)
847                                                 lowest = *any_ptr;
848                                 }
849
850                                 fixed (char* start = &start_char) {
851                                         char* ptr = start + startIndex;
852                                         char* end_ptr = ptr + count;
853
854                                         while (ptr != end_ptr) {
855                                                 if (*ptr > highest || *ptr < lowest) {
856                                                         ptr++;
857                                                         continue;
858                                                 }
859
860                                                 if (*ptr == *any)
861                                                         return (int)(ptr - start);
862
863                                                 any_ptr = any;
864                                                 while (++any_ptr != end_any_ptr) {
865                                                         if (*ptr == *any_ptr)
866                                                                 return (int)(ptr - start);
867                                                 }
868
869                                                 ptr++;
870                                         }
871                                 }
872                         }
873                         return -1;
874                 }
875
876
877 #if NET_2_0
878                 public int IndexOf (string value, StringComparison comparisonType)
879                 {
880                         return IndexOf (value, 0, this.Length, comparisonType);
881                 }
882
883                 public int IndexOf (string value, int startIndex, StringComparison comparisonType)
884                 {
885                         return IndexOf (value, startIndex, this.Length - startIndex, comparisonType);
886                 }
887
888                 public int IndexOf (string value, int startIndex, int count, StringComparison comparisonType)
889                 {
890                         switch (comparisonType) {
891                         case StringComparison.CurrentCulture:
892                                 return CultureInfo.CurrentCulture.CompareInfo.IndexOf (this, value, startIndex, count, CompareOptions.None);
893                         case StringComparison.CurrentCultureIgnoreCase:
894                                 return CultureInfo.CurrentCulture.CompareInfo.IndexOf (this, value, startIndex, count, CompareOptions.IgnoreCase);
895                         case StringComparison.InvariantCulture:
896                                 return CultureInfo.InvariantCulture.CompareInfo.IndexOf (this, value, startIndex, count, CompareOptions.None);
897                         case StringComparison.InvariantCultureIgnoreCase:
898                                 return CultureInfo.InvariantCulture.CompareInfo.IndexOf (this, value, startIndex, count, CompareOptions.IgnoreCase);
899                         case StringComparison.Ordinal:
900                                 return IndexOfOrdinal (value, startIndex, count, CompareOptions.Ordinal);
901                         case StringComparison.OrdinalIgnoreCase:
902                                 return IndexOfOrdinal (value, startIndex, count, CompareOptions.OrdinalIgnoreCase);
903                         default:
904                                 string msg = Locale.GetText ("Invalid value '{0}' for StringComparison", comparisonType);
905                                 throw new ArgumentException (msg, "comparisonType");
906                         }
907                 }
908 #endif
909
910                 internal int IndexOfOrdinal (string value, int startIndex, int count, CompareOptions options)
911                 {
912                         if (value == null)
913                                 throw new ArgumentNullException ("value");
914                         if (startIndex < 0)
915                                 throw new ArgumentOutOfRangeException ("startIndex");
916                         if (count < 0 || (this.length - startIndex) < count)
917                                 throw new ArgumentOutOfRangeException ("count");
918
919                         if (options == CompareOptions.Ordinal)
920                                 return IndexOfOrdinalUnchecked (value, startIndex, count);
921                         return IndexOfOrdinalIgnoreCaseUnchecked (value, startIndex, count);
922                 }
923
924                 internal unsafe int IndexOfOrdinalUnchecked (string value, int startIndex, int count)
925                 {
926                         int valueLen = value.Length;
927                         if (count < valueLen)
928                                 return -1;
929
930                         if (valueLen <= 1) {
931                                 if (valueLen == 1)
932                                         return IndexOfUnchecked (value[0], startIndex, count);
933                                 return 0;
934                         }
935
936                         fixed (char* thisptr = this, valueptr = value) {
937                                 char* ap = thisptr + startIndex;
938                                 char* thisEnd = ap + count - valueLen + 1;
939                                 while (ap != thisEnd) {
940                                         if (*ap == *valueptr) {
941                                                 for (int i = 1; i < valueLen; i++) {
942                                                         if (ap[i] != valueptr[i])
943                                                                 goto NextVal;
944                                                 }
945                                                 return (int)(ap - thisptr);
946                                         }
947                                         NextVal:
948                                         ap++;
949                                 }
950                         }
951                         return -1;
952                 }
953
954                 internal unsafe int IndexOfOrdinalIgnoreCaseUnchecked (string value, int startIndex, int count)
955                 {
956                         int valueLen = value.Length;
957                         if (count < valueLen)
958                                 return -1;
959
960                         if (valueLen == 0)
961                                 return 0;
962
963                         fixed (char* thisptr = this, valueptr = value) {
964                                 char* ap = thisptr + startIndex;
965                                 char* thisEnd = ap + count - valueLen + 1;
966                                 while (ap != thisEnd) {
967                                         for (int i = 0; i < valueLen; i++) {
968                                                 if (Char.ToUpperInvariant (ap[i]) != Char.ToUpperInvariant (valueptr[i]))
969                                                         goto NextVal;
970                                         }
971                                         return (int)(ap - thisptr);
972                                         NextVal:
973                                         ap++;
974                                 }
975                         }
976                         return -1;
977                 }
978
979 #if NET_2_0
980
981                 public int LastIndexOf (string value, StringComparison comparisonType)
982                 {
983                         return LastIndexOf (value, this.Length - 1, this.Length, comparisonType);
984                 }
985
986                 public int LastIndexOf (string value, int startIndex, StringComparison comparisonType)
987                 {
988                         return LastIndexOf (value, startIndex, startIndex + 1, comparisonType);
989                 }
990
991                 public int LastIndexOf (string value, int startIndex, int count, StringComparison comparisonType)
992                 {
993                         switch (comparisonType) {
994                         case StringComparison.CurrentCulture:
995                                 return CultureInfo.CurrentCulture.CompareInfo.LastIndexOf (this, value, startIndex, count, CompareOptions.None);
996                         case StringComparison.CurrentCultureIgnoreCase:
997                                 return CultureInfo.CurrentCulture.CompareInfo.LastIndexOf (this, value, startIndex, count, CompareOptions.IgnoreCase);
998                         case StringComparison.InvariantCulture:
999                                 return CultureInfo.InvariantCulture.CompareInfo.LastIndexOf (this, value, startIndex, count, CompareOptions.None);
1000                         case StringComparison.InvariantCultureIgnoreCase:
1001                                 return CultureInfo.InvariantCulture.CompareInfo.LastIndexOf (this, value, startIndex, count, CompareOptions.IgnoreCase);
1002                         case StringComparison.Ordinal:
1003                                 return LastIndexOfOrdinal (value, startIndex, count, CompareOptions.Ordinal);
1004                         case StringComparison.OrdinalIgnoreCase:
1005                                 return LastIndexOfOrdinal (value, startIndex, count, CompareOptions.OrdinalIgnoreCase);
1006                         default:
1007                                 string msg = Locale.GetText ("Invalid value '{0}' for StringComparison", comparisonType);
1008                                 throw new ArgumentException (msg, "comparisonType");
1009                         }
1010                 }
1011 #endif
1012
1013                 internal int LastIndexOfOrdinal (string value, int startIndex, int count, CompareOptions options)
1014                 {
1015                         if (value == null)
1016                                 throw new ArgumentNullException ("value");
1017                         if (startIndex < 0 || startIndex > length)
1018                                 throw new ArgumentOutOfRangeException ("startIndex");
1019                         if (count < 0 || (startIndex < count - 1))
1020                                 throw new ArgumentOutOfRangeException ("count");
1021
1022                         if (options == CompareOptions.Ordinal)
1023                                 return LastIndexOfOrdinalUnchecked (value, startIndex, count);
1024                         return LastIndexOfOrdinalIgnoreCaseUnchecked (value, startIndex, count);
1025                 }
1026
1027                 internal unsafe int LastIndexOfOrdinalUnchecked (string value, int startIndex, int count)
1028                 {
1029                         int valueLen = value.Length;
1030                         if (count < valueLen)
1031                                 return -1;
1032
1033                         if (valueLen <= 1) {
1034                                 if (valueLen == 1)
1035                                         return LastIndexOfUnchecked (value[0], startIndex, count);
1036                                 return 0;
1037                         }
1038
1039                         fixed (char* thisptr = this, valueptr = value) {
1040                                 char* ap = thisptr + startIndex - valueLen + 1;
1041                                 char* thisEnd = ap - count + valueLen - 1;
1042                                 while (ap != thisEnd) {
1043                                         if (*ap == *valueptr) {
1044                                                 for (int i = 1; i < valueLen; i++) {
1045                                                         if (ap[i] != valueptr[i])
1046                                                                 goto NextVal;
1047                                                 }
1048                                                 return (int)(ap - thisptr);
1049                                         }
1050                                         NextVal:
1051                                         ap--;
1052                                 }
1053                         }
1054                         return -1;
1055                 }
1056
1057                 internal unsafe int LastIndexOfOrdinalIgnoreCaseUnchecked (string value, int startIndex, int count)
1058                 {
1059                         int valueLen = value.Length;
1060                         if (count < valueLen)
1061                                 return -1;
1062
1063                         if (valueLen == 0)
1064                                 return 0;
1065
1066                         fixed (char* thisptr = this, valueptr = value) {
1067                                 char* ap = thisptr + startIndex - valueLen + 1;
1068                                 char* thisEnd = ap - count + valueLen - 1;
1069                                 while (ap != thisEnd) {
1070                                         for (int i = 0; i < valueLen; i++) {
1071                                                 if (Char.ToUpperInvariant (ap[i]) != Char.ToUpperInvariant (valueptr[i]))
1072                                                         goto NextVal;
1073                                         }
1074                                         return (int)(ap - thisptr);
1075                                         NextVal:
1076                                         ap--;
1077                                 }
1078                         }
1079                         return -1;
1080                 }
1081
1082                 // Following methods are culture-insensitive
1083                 public int IndexOf (char value)
1084                 {
1085                         if (this.length == 0)
1086                                 return -1;
1087
1088                         return IndexOfUnchecked (value, 0, this.length);
1089                 }
1090
1091                 public int IndexOf (char value, int startIndex)
1092                 {
1093                         if (startIndex < 0)
1094                                 throw new ArgumentOutOfRangeException ("startIndex", "< 0");
1095                         if (startIndex > this.length)
1096                                 throw new ArgumentOutOfRangeException ("startIndex", "startIndex > this.length");
1097
1098                         if ((startIndex == 0 && this.length == 0) || (startIndex == this.length))
1099                                 return -1;
1100
1101                         return IndexOfUnchecked (value, startIndex, this.length - startIndex);
1102                 }
1103
1104                 public int IndexOf (char value, int startIndex, int count)
1105                 {
1106                         if (startIndex < 0 || startIndex > this.length)
1107                                 throw new ArgumentOutOfRangeException ("startIndex", "Cannot be negative and must be< 0");
1108                         if (count < 0)
1109                                 throw new ArgumentOutOfRangeException ("count", "< 0");
1110                         if (startIndex > this.length - count)
1111                                 throw new ArgumentOutOfRangeException ("count", "startIndex + count > this.length");
1112
1113                         if ((startIndex == 0 && this.length == 0) || (startIndex == this.length) || (count == 0))
1114                                 return -1;
1115
1116                         return IndexOfUnchecked (value, startIndex, count);
1117                 }
1118
1119                 internal unsafe int IndexOfUnchecked (char value, int startIndex, int count)
1120                 {
1121                         // It helps JIT compiler to optimize comparison
1122                         int value_32 = (int)value;
1123
1124                         fixed (char* start = &start_char) {
1125                                 char* ptr = start + startIndex;
1126                                 char* end_ptr = ptr + (count >> 3 << 3);
1127
1128                                 while (ptr != end_ptr) {
1129                                         if (*ptr == value_32)
1130                                                 return (int)(ptr - start);
1131                                         if (ptr[1] == value_32)
1132                                                 return (int)(ptr - start + 1);
1133                                         if (ptr[2] == value_32)
1134                                                 return (int)(ptr - start + 2);
1135                                         if (ptr[3] == value_32)
1136                                                 return (int)(ptr - start + 3);
1137                                         if (ptr[4] == value_32)
1138                                                 return (int)(ptr - start + 4);
1139                                         if (ptr[5] == value_32)
1140                                                 return (int)(ptr - start + 5);
1141                                         if (ptr[6] == value_32)
1142                                                 return (int)(ptr - start + 6);
1143                                         if (ptr[7] == value_32)
1144                                                 return (int)(ptr - start + 7);
1145
1146                                         ptr += 8;
1147                                 }
1148
1149                                 end_ptr += count & 0x07;
1150                                 while (ptr != end_ptr) {
1151                                         if (*ptr == value_32)
1152                                                 return (int)(ptr - start);
1153
1154                                         ptr++;
1155                                 }
1156                                 return -1;
1157                         }
1158                 }
1159
1160                 internal unsafe int IndexOfOrdinalIgnoreCase (char value, int startIndex, int count)
1161                 {
1162                         if (length == 0)
1163                                 return -1;
1164                         int end = startIndex + count;
1165                         char c = Char.ToUpperInvariant (value);
1166                         fixed (char* s = &start_char) {
1167                                 for (int i = startIndex; i < end; i++)
1168                                         if (Char.ToUpperInvariant (s [i]) == c)
1169                                                 return i;
1170                         }
1171                         return -1;
1172                 }
1173
1174                 // Following methods are culture-sensitive
1175                 public int IndexOf (String value)
1176                 {
1177                         if (value == null)
1178                                 throw new ArgumentNullException ("value");
1179                         if (value.length == 0)
1180                                 return 0;
1181                         if (this.length == 0)
1182                                 return -1;
1183                         return CultureInfo.CurrentCulture.CompareInfo.IndexOf (this, value, 0, length);
1184                 }
1185
1186                 public int IndexOf (String value, int startIndex)
1187                 {
1188                         return IndexOf (value, startIndex, this.length - startIndex);
1189                 }
1190
1191                 public int IndexOf (String value, int startIndex, int count)
1192                 {
1193                         if (value == null)
1194 #if NET_2_0
1195                                 throw new ArgumentNullException ("value");
1196 #else
1197                                 throw new ArgumentNullException ("string2");
1198 #endif
1199                         if (startIndex < 0 || startIndex > this.length)
1200                                 throw new ArgumentOutOfRangeException ("startIndex", "Cannot be negative, and should not exceed length of string.");
1201                         if (count < 0 || startIndex > this.length - count)
1202                                 throw new ArgumentOutOfRangeException ("count", "Cannot be negative, and should point to location in string.");
1203
1204                         if (value.length == 0)
1205                                 return startIndex;
1206
1207                         if (startIndex == 0 && this.length == 0)
1208                                 return -1;
1209
1210                         if (count == 0)
1211                                 return -1;
1212
1213                         return CultureInfo.CurrentCulture.CompareInfo.IndexOf (this, value, startIndex, count);
1214                 }
1215
1216                 // Following methods are culture-insensitive
1217                 public int LastIndexOfAny (char [] anyOf)
1218                 {
1219                         if (anyOf == null)
1220                                 throw new ArgumentNullException ();
1221
1222                         return LastIndexOfAnyUnchecked (anyOf, this.length - 1, this.length);
1223                 }
1224
1225                 public int LastIndexOfAny (char [] anyOf, int startIndex)
1226                 {
1227                         if (anyOf == null)
1228                                 throw new ArgumentNullException ();
1229
1230                         if (startIndex < 0 || startIndex >= this.length)
1231                                 throw new ArgumentOutOfRangeException ("startIndex", "Cannot be negative, and should be less than length of string.");
1232
1233                         if (this.length == 0)
1234                                 return -1;
1235
1236                         return LastIndexOfAnyUnchecked (anyOf, startIndex, startIndex + 1);
1237                 }
1238
1239                 public int LastIndexOfAny (char [] anyOf, int startIndex, int count)
1240                 {
1241                         if (anyOf == null) 
1242                                 throw new ArgumentNullException ();
1243
1244                         if ((startIndex < 0) || (startIndex >= this.Length))
1245                                 throw new ArgumentOutOfRangeException ("startIndex", "< 0 || > this.Length");
1246                         if ((count < 0) || (count > this.Length))
1247                                 throw new ArgumentOutOfRangeException ("count", "< 0 || > this.Length");
1248                         if (startIndex - count + 1 < 0)
1249                                 throw new ArgumentOutOfRangeException ("startIndex - count + 1 < 0");
1250
1251                         if (this.length == 0)
1252                                 return -1;
1253
1254                         return LastIndexOfAnyUnchecked (anyOf, startIndex, count);
1255                 }
1256
1257                 private unsafe int LastIndexOfAnyUnchecked (char [] anyOf, int startIndex, int count)
1258                 {
1259                         if (anyOf.Length == 1)
1260                                 return LastIndexOfUnchecked (anyOf[0], startIndex, count);
1261
1262                         fixed (char* start = this, testStart = anyOf) {
1263                                 char* ptr = start + startIndex;
1264                                 char* ptrEnd = ptr - count;
1265                                 char* test;
1266                                 char* testEnd = testStart + anyOf.Length;
1267
1268                                 while (ptr != ptrEnd) {
1269                                         test = testStart;
1270                                         while (test != testEnd) {
1271                                                 if (*test == *ptr)
1272                                                         return (int)(ptr - start);
1273                                                 test++;
1274                                         }
1275                                         ptr--;
1276                                 }
1277                                 return -1;
1278                         }
1279                 }
1280
1281                 // Following methods are culture-insensitive
1282                 public int LastIndexOf (char value)
1283                 {
1284                         if (this.length == 0)
1285                                 return -1;
1286                         
1287                         return LastIndexOfUnchecked (value, this.length - 1, this.length);
1288                 }
1289
1290                 public int LastIndexOf (char value, int startIndex)
1291                 {
1292                         return LastIndexOf (value, startIndex, startIndex + 1);
1293                 }
1294
1295                 public int LastIndexOf (char value, int startIndex, int count)
1296                 {
1297                         if (startIndex == 0 && this.length == 0)
1298                                 return -1;
1299
1300                         // >= for char (> for string)
1301                         if ((startIndex < 0) || (startIndex >= this.Length))
1302                                 throw new ArgumentOutOfRangeException ("startIndex", "< 0 || >= this.Length");
1303                         if ((count < 0) || (count > this.Length))
1304                                 throw new ArgumentOutOfRangeException ("count", "< 0 || > this.Length");
1305                         if (startIndex - count + 1 < 0)
1306                                 throw new ArgumentOutOfRangeException ("startIndex - count + 1 < 0");
1307
1308                         return LastIndexOfUnchecked (value, startIndex, count);
1309                 }
1310
1311                 internal unsafe int LastIndexOfUnchecked (char value, int startIndex, int count)
1312                 {
1313                         // It helps JIT compiler to optimize comparison
1314                         int value_32 = (int)value;
1315
1316                         fixed (char* start = &start_char) {
1317                                 char* ptr = start + startIndex;
1318                                 char* end_ptr = ptr - (count >> 3 << 3);
1319
1320                                 while (ptr != end_ptr) {
1321                                         if (*ptr == value_32)
1322                                                 return (int)(ptr - start);
1323                                         if (ptr[-1] == value_32)
1324                                                 return (int)(ptr - start) - 1;
1325                                         if (ptr[-2] == value_32)
1326                                                 return (int)(ptr - start) - 2;
1327                                         if (ptr[-3] == value_32)
1328                                                 return (int)(ptr - start) - 3;
1329                                         if (ptr[-4] == value_32)
1330                                                 return (int)(ptr - start) - 4;
1331                                         if (ptr[-5] == value_32)
1332                                                 return (int)(ptr - start) - 5;
1333                                         if (ptr[-6] == value_32)
1334                                                 return (int)(ptr - start) - 6;
1335                                         if (ptr[-7] == value_32)
1336                                                 return (int)(ptr - start) - 7;
1337
1338                                         ptr -= 8;
1339                                 }
1340
1341                                 end_ptr -= count & 0x07;
1342                                 while (ptr != end_ptr) {
1343                                         if (*ptr == value_32)
1344                                                 return (int)(ptr - start);
1345
1346                                         ptr--;
1347                                 }
1348                                 return -1;
1349                         }
1350                 }
1351
1352                 internal unsafe int LastIndexOfOrdinalIgnoreCase (char value, int startIndex, int count)
1353                 {
1354                         if (length == 0)
1355                                 return -1;
1356                         int end = startIndex - count;
1357                         char c = Char.ToUpperInvariant (value);
1358                         fixed (char* s = &start_char) {
1359                                 for (int i = startIndex; i > end; i--)
1360                                         if (Char.ToUpperInvariant (s [i]) == c)
1361                                                 return i;
1362                         }
1363                         return -1;
1364                 }
1365
1366                 // Following methods are culture-sensitive
1367                 public int LastIndexOf (String value)
1368                 {
1369                         if (this.length == 0)
1370                                 // This overload does additional checking
1371                                 return LastIndexOf (value, 0, 0);
1372                         else
1373                                 return LastIndexOf (value, this.length - 1, this.length);
1374                 }
1375
1376                 public int LastIndexOf (String value, int startIndex)
1377                 {
1378                         int max = startIndex;
1379                         if (max < this.Length)
1380                                 max++;
1381                         return LastIndexOf (value, startIndex, max);
1382                 }
1383
1384                 public int LastIndexOf (String value, int startIndex, int count)
1385                 {
1386                         if (value == null)
1387 #if NET_2_0
1388                                 throw new ArgumentNullException ("value");
1389 #else
1390                                 throw new ArgumentNullException ("string2");
1391 #endif
1392
1393                         // -1 > startIndex > for string (0 > startIndex >= for char)
1394                         if ((startIndex < -1) || (startIndex > this.Length))
1395                                 throw new ArgumentOutOfRangeException ("startIndex", "< 0 || > this.Length");
1396                         if ((count < 0) || (count > this.Length))
1397                                 throw new ArgumentOutOfRangeException ("count", "< 0 || > this.Length");
1398                         if (startIndex - count + 1 < 0)
1399                                 throw new ArgumentOutOfRangeException ("startIndex - count + 1 < 0");
1400
1401                         if (value.Length == 0)
1402                                 return startIndex;
1403
1404                         if (startIndex == 0 && this.length == 0)
1405                                 return -1;
1406
1407                         // This check is needed to match undocumented MS behaviour
1408                         if (this.length == 0 && value.length > 0)
1409                                 return -1;
1410
1411                         if (count == 0)
1412                                 return -1;
1413
1414                         if (startIndex == this.Length)
1415                                 startIndex--;
1416                         return CultureInfo.CurrentCulture.CompareInfo.LastIndexOf (this, value, startIndex, count);
1417                 }
1418
1419 #if NET_2_0
1420                 public bool Contains (String value)
1421                 {
1422                         return IndexOf (value) != -1;
1423                 }
1424
1425                 public static bool IsNullOrEmpty (String value)
1426                 {
1427                         return (value == null) || (value.Length == 0);
1428                 }
1429
1430                 public string Normalize ()
1431                 {
1432                         return Normalization.Normalize (this, 0);
1433                 }
1434
1435                 public string Normalize (NormalizationForm normalizationForm)
1436                 {
1437                         switch (normalizationForm) {
1438                         default:
1439                                 return Normalization.Normalize (this, 0);
1440                         case NormalizationForm.FormD:
1441                                 return Normalization.Normalize (this, 1);
1442                         case NormalizationForm.FormKC:
1443                                 return Normalization.Normalize (this, 2);
1444                         case NormalizationForm.FormKD:
1445                                 return Normalization.Normalize (this, 3);
1446                         }
1447                 }
1448
1449                 public bool IsNormalized ()
1450                 {
1451                         return Normalization.IsNormalized (this, 0);
1452                 }
1453
1454                 public bool IsNormalized (NormalizationForm normalizationForm)
1455                 {
1456                         switch (normalizationForm) {
1457                         default:
1458                                 return Normalization.IsNormalized (this, 0);
1459                         case NormalizationForm.FormD:
1460                                 return Normalization.IsNormalized (this, 1);
1461                         case NormalizationForm.FormKC:
1462                                 return Normalization.IsNormalized (this, 2);
1463                         case NormalizationForm.FormKD:
1464                                 return Normalization.IsNormalized (this, 3);
1465                         }
1466                 }
1467
1468                 public string Remove (int startIndex)
1469                 {
1470                         if (startIndex < 0)
1471                                 throw new ArgumentOutOfRangeException ("startIndex", "StartIndex can not be less than zero");
1472                         if (startIndex >= this.length)
1473                                 throw new ArgumentOutOfRangeException ("startIndex", "StartIndex must be less than the length of the string");
1474
1475                         return Remove (startIndex, this.length - startIndex);
1476                 }
1477 #endif
1478
1479                 public String PadLeft (int totalWidth)
1480                 {
1481                         return PadLeft (totalWidth, ' ');
1482                 }
1483
1484                 public unsafe String PadLeft (int totalWidth, char paddingChar)
1485                 {
1486                         //LAMESPEC: MSDN Doc says this is reversed for RtL languages, but this seems to be untrue
1487
1488                         if (totalWidth < 0)
1489                                 throw new ArgumentOutOfRangeException ("totalWidth", "< 0");
1490
1491                         if (totalWidth < this.length)
1492                                 return this;
1493
1494                         String tmp = InternalAllocateStr (totalWidth);
1495
1496                         fixed (char* dest = tmp, src = this) {
1497                                 char* padPos = dest;
1498                                 char* padTo = dest + (totalWidth - length);
1499                                 while (padPos != padTo)
1500                                         *padPos++ = paddingChar;
1501
1502                                 CharCopy (padTo, src, length);
1503                         }
1504                         return tmp;
1505                 }
1506
1507                 public String PadRight (int totalWidth)
1508                 {
1509                         return PadRight (totalWidth, ' ');
1510                 }
1511
1512                 public unsafe String PadRight (int totalWidth, char paddingChar)
1513                 {
1514                         //LAMESPEC: MSDN Doc says this is reversed for RtL languages, but this seems to be untrue
1515
1516                         if (totalWidth < 0)
1517                                 throw new ArgumentOutOfRangeException ("totalWidth", "< 0");
1518
1519                         if (totalWidth < this.length)
1520                                 return this;
1521
1522                         String tmp = InternalAllocateStr (totalWidth);
1523
1524                         fixed (char* dest = tmp, src = this) {
1525                                 CharCopy (dest, src, length);
1526
1527                                 char* padPos = dest + length;
1528                                 char* padTo = dest + totalWidth;
1529                                 while (padPos != padTo)
1530                                         *padPos++ = paddingChar;
1531                         }
1532                         return tmp;
1533                 }
1534
1535                 public bool StartsWith (String value)
1536                 {
1537                         if (value == null)
1538                                 throw new ArgumentNullException ("value");
1539
1540                         return CultureInfo.CurrentCulture.CompareInfo.IsPrefix (this, value, CompareOptions.None);
1541                 }
1542
1543 #if NET_2_0
1544                 [ComVisible (false)]
1545                 public bool StartsWith (string value, StringComparison comparisonType)
1546                 {
1547                         if (value == null)
1548                                 throw new ArgumentNullException ("value");
1549
1550                         switch (comparisonType) {
1551                         case StringComparison.CurrentCulture:
1552                                 return CultureInfo.CurrentCulture.CompareInfo.IsPrefix (this, value, CompareOptions.None);
1553                         case StringComparison.CurrentCultureIgnoreCase:
1554                                 return CultureInfo.CurrentCulture.CompareInfo.IsPrefix (this, value, CompareOptions.IgnoreCase);
1555                         case StringComparison.InvariantCulture:
1556                                 return CultureInfo.InvariantCulture.CompareInfo.IsPrefix (this, value, CompareOptions.None);
1557                         case StringComparison.InvariantCultureIgnoreCase:
1558                                 return CultureInfo.InvariantCulture.CompareInfo.IsPrefix (this, value, CompareOptions.IgnoreCase);
1559                         case StringComparison.Ordinal:
1560                                 return CultureInfo.CurrentCulture.CompareInfo.IsPrefix (this, value, CompareOptions.Ordinal);
1561                         case StringComparison.OrdinalIgnoreCase:
1562                                 return CultureInfo.CurrentCulture.CompareInfo.IsPrefix (this, value, CompareOptions.OrdinalIgnoreCase);
1563                         default:
1564                                 string msg = Locale.GetText ("Invalid value '{0}' for StringComparison", comparisonType);
1565                                 throw new ArgumentException (msg, "comparisonType");
1566                         }
1567                 }
1568
1569                 [ComVisible (false)]
1570                 public bool EndsWith (string value, StringComparison comparisonType)
1571                 {
1572                         if (value == null)
1573                                 throw new ArgumentNullException ("value");
1574
1575                         switch (comparisonType) {
1576                         case StringComparison.CurrentCulture:
1577                                 return CultureInfo.CurrentCulture.CompareInfo.IsSuffix (this, value, CompareOptions.None);
1578                         case StringComparison.CurrentCultureIgnoreCase:
1579                                 return CultureInfo.CurrentCulture.CompareInfo.IsSuffix (this, value, CompareOptions.IgnoreCase);
1580                         case StringComparison.InvariantCulture:
1581                                 return CultureInfo.InvariantCulture.CompareInfo.IsSuffix (this, value, CompareOptions.None);
1582                         case StringComparison.InvariantCultureIgnoreCase:
1583                                 return CultureInfo.InvariantCulture.CompareInfo.IsSuffix (this, value, CompareOptions.IgnoreCase);
1584                         case StringComparison.Ordinal:
1585                                 return CultureInfo.CurrentCulture.CompareInfo.IsSuffix (this, value, CompareOptions.Ordinal);
1586                         case StringComparison.OrdinalIgnoreCase:
1587                                 return CultureInfo.CurrentCulture.CompareInfo.IsSuffix (this, value, CompareOptions.OrdinalIgnoreCase);
1588                         default:
1589                                 string msg = Locale.GetText ("Invalid value '{0}' for StringComparison", comparisonType);
1590                                 throw new ArgumentException (msg, "comparisonType");
1591                         }
1592                 }
1593 #endif
1594
1595 #if NET_2_0
1596                 public
1597 #else
1598                 internal
1599 #endif
1600                 bool StartsWith (String value, bool ignoreCase, CultureInfo culture)
1601                 {
1602                         if (culture == null)
1603                                 culture = CultureInfo.CurrentCulture;
1604                         
1605                         return culture.CompareInfo.IsPrefix (this, value, ignoreCase ? CompareOptions.IgnoreCase : CompareOptions.None);
1606                 }
1607
1608                 // Following method is culture-insensitive
1609                 public unsafe String Replace (char oldChar, char newChar)
1610                 {
1611                         if (this.length == 0 || oldChar == newChar)
1612                                 return this;
1613
1614                         int start_pos = IndexOfUnchecked (oldChar, 0, this.length);
1615                         if (start_pos == -1)
1616                                 return this;
1617
1618                         if (start_pos < 4)
1619                                 start_pos = 0;
1620
1621                         string tmp = InternalAllocateStr (length);
1622                         fixed (char* dest = tmp, src = &start_char) {
1623                                 if (start_pos != 0)
1624                                         CharCopy (dest, src, start_pos);
1625
1626                                 char* end_ptr = dest + length;
1627                                 char* dest_ptr = dest + start_pos;
1628                                 char* src_ptr = src + start_pos;
1629
1630                                 while (dest_ptr != end_ptr) {
1631                                         if (*src_ptr == oldChar)
1632                                                 *dest_ptr = newChar;
1633                                         else
1634                                                 *dest_ptr = *src_ptr;
1635
1636                                         ++src_ptr;
1637                                         ++dest_ptr;
1638                                 }
1639                         }
1640                         return tmp;
1641                 }
1642
1643                 // culture-insensitive using ordinal search (See testcase StringTest.ReplaceStringCultureTests)
1644                 public String Replace (String oldValue, String newValue)
1645                 {
1646                         // LAMESPEC: According to MSDN the following method is culture-sensitive but this seems to be incorrect
1647                         // LAMESPEC: Result is undefined if result length is longer than maximum string length
1648
1649                         if (oldValue == null)
1650                                 throw new ArgumentNullException ("oldValue");
1651
1652                         if (oldValue.Length == 0)
1653                                 throw new ArgumentException ("oldValue is the empty string.");
1654
1655                         if (this.Length == 0)
1656                                 return this;
1657                         
1658                         if (newValue == null)
1659                                 newValue = String.Empty;
1660
1661                         return ReplaceUnchecked (oldValue, newValue);
1662                 }
1663
1664                 private unsafe String ReplaceUnchecked (String oldValue, String newValue)
1665                 {
1666                         if (oldValue.length > length)
1667                                 return this;
1668                         if (oldValue.length == 1 && newValue.length == 1) {
1669                                 return Replace (oldValue[0], newValue[0]);
1670                                 // ENHANCE: It would be possible to special case oldValue.length == newValue.length
1671                                 // because the length of the result would be this.length and length calculation unneccesary
1672                         }
1673
1674                         const int maxValue = 200; // Allocate 800 byte maximum
1675                         int* dat = stackalloc int[maxValue];
1676                         fixed (char* source = this, replace = newValue) {
1677                                 int i = 0, count = 0;
1678                                 while (i < length) {
1679                                         int found = IndexOfOrdinalUnchecked (oldValue, i, length - i);
1680                                         if (found < 0)
1681                                                 break;
1682                                         else {
1683                                                 if (count < maxValue)
1684                                                         dat[count++] = found;
1685                                                 else
1686                                                         return ReplaceFallback (oldValue, newValue, maxValue);
1687                                         }
1688                                         i = found + oldValue.length;
1689                                 }
1690                                 if (count == 0)
1691                                         return this;
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 arg0)
1961                 {
1962                         if (arg0 == null)
1963                                 return String.Empty;
1964
1965                         return arg0.ToString ();
1966                 }
1967
1968                 public unsafe static String Concat (Object arg0, Object arg1)
1969                 {
1970                         string s1, s2;
1971
1972                         s1 = (arg0 != null) ? arg0.ToString () : null;
1973                         s2 = (arg1 != null) ? arg1.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 arg0, Object arg1, Object arg2)
1999                 {
2000                         string s1, s2, s3;
2001                         if (arg0 == null)
2002                                 s1 = String.Empty;
2003                         else
2004                                 s1 = arg0.ToString ();
2005
2006                         if (arg1 == null)
2007                                 s2 = String.Empty;
2008                         else
2009                                 s2 = arg1.ToString ();
2010
2011                         if (arg2 == null)
2012                                 s3 = String.Empty;
2013                         else
2014                                 s3 = arg2.ToString ();
2015
2016                         return Concat (s1, s2, s3);
2017                 }
2018
2019 #if ! BOOTSTRAP_WITH_OLDLIB
2020                 [CLSCompliant(false)]
2021                 public static String Concat (Object arg0, Object arg1, Object arg2,
2022                                              Object arg3, __arglist)
2023                 {
2024                         string s1, s2, s3, s4;
2025
2026                         if (arg0 == null)
2027                                 s1 = String.Empty;
2028                         else
2029                                 s1 = arg0.ToString ();
2030
2031                         if (arg1 == null)
2032                                 s2 = String.Empty;
2033                         else
2034                                 s2 = arg1.ToString ();
2035
2036                         if (arg2 == null)
2037                                 s3 = String.Empty;
2038                         else
2039                                 s3 = arg2.ToString ();
2040
2041                         ArgIterator iter = new ArgIterator (__arglist);
2042                         int argCount = iter.GetRemainingCount();
2043
2044                         StringBuilder sb = new StringBuilder ();
2045                         if (arg3 != null)
2046                                 sb.Append (arg3.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 str0, String str1)
2060                 {
2061                         if (str0 == null || str0.Length == 0) {
2062                                 if (str1 == null || str1.Length == 0)
2063                                         return String.Empty;
2064                                 return str1;
2065                         }
2066
2067                         if (str1 == null || str1.Length == 0)
2068                                 return str0; 
2069
2070                         String tmp = InternalAllocateStr (str0.length + str1.length);
2071
2072                         fixed (char *dest = tmp, src = str0)
2073                                 CharCopy (dest, src, str0.length);
2074                         fixed (char *dest = tmp, src = str1)
2075                                 CharCopy (dest + str0.Length, src, str1.length);
2076
2077                         return tmp;
2078                 }
2079
2080                 public unsafe static String Concat (String str0, String str1, String str2)
2081                 {
2082                         if (str0 == null || str0.Length == 0){
2083                                 if (str1 == null || str1.Length == 0){
2084                                         if (str2 == null || str2.Length == 0)
2085                                                 return String.Empty;
2086                                         return str2;
2087                                 } else {
2088                                         if (str2 == null || str2.Length == 0)
2089                                                 return str1;
2090                                 }
2091                                 str0 = String.Empty;
2092                         } else {
2093                                 if (str1 == null || str1.Length == 0){
2094                                         if (str2 == null || str2.Length == 0)
2095                                                 return str0;
2096                                         else
2097                                                 str1 = String.Empty;
2098                                 } else {
2099                                         if (str2 == null || str2.Length == 0)
2100                                                 str2 = String.Empty;
2101                                 }
2102                         }
2103
2104                         String tmp = InternalAllocateStr (str0.length + str1.length + str2.length);
2105
2106                         if (str0.Length != 0) {
2107                                 fixed (char *dest = tmp, src = str0) {
2108                                         CharCopy (dest, src, str0.length);
2109                                 }
2110                         }
2111                         if (str1.Length != 0) {
2112                                 fixed (char *dest = tmp, src = str1) {
2113                                         CharCopy (dest + str0.Length, src, str1.length);
2114                                 }
2115                         }
2116                         if (str2.Length != 0) {
2117                                 fixed (char *dest = tmp, src = str2) {
2118                                         CharCopy (dest + str0.Length + str1.Length, src, str2.length);
2119                                 }
2120                         }
2121
2122                         return tmp;
2123                 }
2124
2125                 public unsafe static String Concat (String str0, String str1, String str2, String str3)
2126                 {
2127                         if (str0 == null && str1 == null && str2 == null && str3 == null)
2128                                 return String.Empty;
2129
2130                         if (str0 == null)
2131                                 str0 = String.Empty;
2132                         if (str1 == null)
2133                                 str1 = String.Empty;
2134                         if (str2 == null)
2135                                 str2 = String.Empty;
2136                         if (str3 == null)
2137                                 str3 = String.Empty;
2138
2139                         String tmp = InternalAllocateStr (str0.length + str1.length + str2.length + str3.length);
2140
2141                         if (str0.Length != 0) {
2142                                 fixed (char *dest = tmp, src = str0) {
2143                                         CharCopy (dest, src, str0.length);
2144                                 }
2145                         }
2146                         if (str1.Length != 0) {
2147                                 fixed (char *dest = tmp, src = str1) {
2148                                         CharCopy (dest + str0.Length, src, str1.length);
2149                                 }
2150                         }
2151                         if (str2.Length != 0) {
2152                                 fixed (char *dest = tmp, src = str2) {
2153                                         CharCopy (dest + str0.Length + str1.Length, src, str2.length);
2154                                 }
2155                         }
2156                         if (str3.Length != 0) {
2157                                 fixed (char *dest = tmp, src = str3) {
2158                                         CharCopy (dest + str0.Length + str1.Length + str2.Length, src, str3.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 #if ONLY_1_1
2390 #pragma warning disable 3019
2391                 [CLSCompliant (false)]
2392 #endif
2393                 sbyte IConvertible.ToSByte (IFormatProvider provider)
2394                 {
2395                         return Convert.ToSByte (this, provider);
2396                 }
2397 #if ONLY_1_1
2398 #pragma warning restore 3019
2399 #endif
2400
2401                 float IConvertible.ToSingle (IFormatProvider provider)
2402                 {
2403                         return Convert.ToSingle (this, provider);
2404                 }
2405
2406                 object IConvertible.ToType (Type type, IFormatProvider provider)
2407                 {
2408                         return Convert.ToType (this, type, provider);
2409                 }
2410
2411 #if ONLY_1_1
2412 #pragma warning disable 3019
2413                 [CLSCompliant (false)]
2414 #endif
2415                 ushort IConvertible.ToUInt16 (IFormatProvider provider)
2416                 {
2417                         return Convert.ToUInt16 (this, provider);
2418                 }
2419 #if ONLY_1_1
2420 #pragma warning restore 3019
2421 #endif
2422
2423 #if ONLY_1_1
2424 #pragma warning disable 3019
2425                 [CLSCompliant (false)]
2426 #endif
2427                 uint IConvertible.ToUInt32 (IFormatProvider provider)
2428                 {
2429                         return Convert.ToUInt32 (this, provider);
2430                 }
2431 #if ONLY_1_1
2432 #pragma warning restore 3019
2433 #endif
2434
2435 #if ONLY_1_1
2436 #pragma warning disable 3019
2437                 [CLSCompliant (false)]
2438 #endif
2439                 ulong IConvertible.ToUInt64 (IFormatProvider provider)
2440                 {
2441                         return Convert.ToUInt64 (this, provider);
2442                 }
2443 #if ONLY_1_1
2444 #pragma warning restore 3019
2445 #endif
2446
2447                 public int Length {
2448                         get {
2449                                 return length;
2450                         }
2451                 }
2452
2453                 public CharEnumerator GetEnumerator ()
2454                 {
2455                         return new CharEnumerator (this);
2456                 }
2457
2458 #if NET_2_0
2459                 IEnumerator<char> IEnumerable<char>.GetEnumerator ()
2460                 {
2461                         return new CharEnumerator (this);
2462                 }
2463 #endif
2464
2465                 IEnumerator IEnumerable.GetEnumerator ()
2466                 {
2467                         return new CharEnumerator (this);
2468                 }
2469
2470                 private static void ParseFormatSpecifier (string str, ref int ptr, out int n, out int width,
2471                                                           out bool left_align, out string format)
2472                 {
2473                         // parses format specifier of form:
2474                         //   N,[\ +[-]M][:F]}
2475                         //
2476                         // where:
2477
2478                         try {
2479                                 // N = argument number (non-negative integer)
2480
2481                                 n = ParseDecimal (str, ref ptr);
2482                                 if (n < 0)
2483                                         throw new FormatException ("Input string was not in a correct format.");
2484
2485                                 // M = width (non-negative integer)
2486
2487                                 if (str[ptr] == ',') {
2488                                         // White space between ',' and number or sign.
2489                                         ++ptr;
2490                                         while (Char.IsWhiteSpace (str [ptr]))
2491                                                 ++ptr;
2492                                         int start = ptr;
2493
2494                                         format = str.Substring (start, ptr - start);
2495
2496                                         left_align = (str [ptr] == '-');
2497                                         if (left_align)
2498                                                 ++ ptr;
2499
2500                                         width = ParseDecimal (str, ref ptr);
2501                                         if (width < 0)
2502                                                 throw new FormatException ("Input string was not in a correct format.");
2503                                 }
2504                                 else {
2505                                         width = 0;
2506                                         left_align = false;
2507                                         format = String.Empty;
2508                                 }
2509
2510                                 // F = argument format (string)
2511
2512                                 if (str[ptr] == ':') {
2513                                         int start = ++ ptr;
2514                                         while (str[ptr] != '}')
2515                                                 ++ ptr;
2516
2517                                         format += str.Substring (start, ptr - start);
2518                                 }
2519                                 else
2520                                         format = null;
2521
2522                                 if (str[ptr ++] != '}')
2523                                         throw new FormatException ("Input string was not in a correct format.");
2524                         }
2525                         catch (IndexOutOfRangeException) {
2526                                 throw new FormatException ("Input string was not in a correct format.");
2527                         }
2528                 }
2529
2530                 private static int ParseDecimal (string str, ref int ptr)
2531                 {
2532                         int p = ptr;
2533                         int n = 0;
2534                         while (true) {
2535                                 char c = str[p];
2536                                 if (c < '0' || '9' < c)
2537                                         break;
2538
2539                                 n = n * 10 + c - '0';
2540                                 ++ p;
2541                         }
2542
2543                         if (p == ptr)
2544                                 return -1;
2545
2546                         ptr = p;
2547                         return n;
2548                 }
2549
2550                 internal unsafe void InternalSetChar (int idx, char val)
2551                 {
2552                         if ((uint) idx >= (uint) Length)
2553                                 throw new ArgumentOutOfRangeException ("idx");
2554
2555                         fixed (char * pStr = &start_char) 
2556                         {
2557                                 pStr [idx] = val;
2558                         }
2559                 }
2560
2561                 internal unsafe void InternalSetLength (int newLength)
2562                 {
2563                         if (newLength > length)
2564                                 throw new ArgumentOutOfRangeException ("newLength", "newLength as to be <= length");
2565
2566                         // zero terminate, we can pass string objects directly via pinvoke
2567                         // we also zero the rest of the string, since the new GC needs to be
2568                         // able to handle the changing size (it will skip the 0 bytes).
2569                         fixed (char * pStr = &start_char) {
2570                                 char *p = pStr + newLength;
2571                                 char *end = pStr + length;
2572                                 while (p < end) {
2573                                         p [0] = '\0';
2574                                         p++;
2575                                 }
2576                         }
2577                         length = newLength;
2578                 }
2579
2580 #if NET_2_0
2581                 [ReliabilityContractAttribute (Consistency.WillNotCorruptState, Cer.MayFail)]
2582 #endif
2583                 // When modifying it, GetCaseInsensitiveHashCode() should be modified as well.
2584                 public unsafe override int GetHashCode ()
2585                 {
2586                         fixed (char * c = this) {
2587                                 char * cc = c;
2588                                 char * end = cc + length - 1;
2589                                 int h = 0;
2590                                 for (;cc < end; cc += 2) {
2591                                         h = (h << 5) - h + *cc;
2592                                         h = (h << 5) - h + cc [1];
2593                                 }
2594                                 ++end;
2595                                 if (cc < end)
2596                                         h = (h << 5) - h + *cc;
2597                                 return h;
2598                         }
2599                 }
2600
2601                 internal unsafe int GetCaseInsensitiveHashCode ()
2602                 {
2603                         fixed (char * c = this) {
2604                                 char * cc = c;
2605                                 char * end = cc + length - 1;
2606                                 int h = 0;
2607                                 for (;cc < end; cc += 2) {
2608                                         h = (h << 5) - h + Char.ToUpperInvariant (*cc);
2609                                         h = (h << 5) - h + Char.ToUpperInvariant (cc [1]);
2610                                 }
2611                                 ++end;
2612                                 if (cc < end)
2613                                         h = (h << 5) - h + Char.ToUpperInvariant (*cc);
2614                                 return h;
2615                         }
2616                 }
2617
2618                 // Certain constructors are redirected to CreateString methods with
2619                 // matching argument list. The this pointer should not be used.
2620 #pragma warning disable 169
2621                 private unsafe String CreateString (sbyte* value)
2622                 {
2623                         if (value == null)
2624                                 return String.Empty;
2625
2626                         byte* bytes = (byte*) value;
2627                         int length = 0;
2628
2629                         try {
2630                                 while (bytes++ [0] != 0)
2631                                         length++;
2632                         } catch (NullReferenceException) {
2633                                 throw new ArgumentOutOfRangeException ("ptr", "Value does not refer to a valid string.");
2634 #if NET_2_0
2635                         } catch (AccessViolationException) {
2636                                 throw new ArgumentOutOfRangeException ("ptr", "Value does not refer to a valid string.");
2637 #endif
2638                         }
2639
2640                         return CreateString (value, 0, length, null);
2641                 }
2642
2643                 private unsafe String CreateString (sbyte* value, int startIndex, int length)
2644                 {
2645                         return CreateString (value, startIndex, length, null);
2646                 }
2647
2648                 private unsafe String CreateString (sbyte* value, int startIndex, int length, Encoding enc)
2649                 {
2650                         if (length < 0)
2651                                 throw new ArgumentOutOfRangeException ("length", "Non-negative number required.");
2652                         if (startIndex < 0)
2653                                 throw new ArgumentOutOfRangeException ("startIndex", "Non-negative number required.");
2654                         if (value + startIndex < value)
2655                                 throw new ArgumentOutOfRangeException ("startIndex", "Value, startIndex and length do not refer to a valid string.");
2656
2657                         bool isDefaultEncoding;
2658
2659                         if (isDefaultEncoding = (enc == null)) {
2660 #if NET_2_0
2661                                 if (value == null)
2662                                         throw new ArgumentNullException ("value");
2663                                 if (length == 0)
2664 #else
2665                                 if (value == null || length == 0)
2666 #endif
2667                                         return String.Empty;
2668
2669                                 enc = Encoding.Default;
2670                         }
2671
2672                         byte [] bytes = new byte [length];
2673
2674                         if (length != 0)
2675                                 fixed (byte* bytePtr = bytes)
2676                                         try {
2677                                                 memcpy (bytePtr, (byte*) (value + startIndex), length);
2678                                         } catch (NullReferenceException) {
2679 #if !NET_2_0
2680                                                 if (!isDefaultEncoding)
2681                                                         throw;
2682 #endif
2683
2684                                                 throw new ArgumentOutOfRangeException ("ptr", "Value, startIndex and length do not refer to a valid string.");
2685 #if NET_2_0
2686                                         } catch (AccessViolationException) {
2687                                                 if (!isDefaultEncoding)
2688                                                         throw;
2689
2690                                                 throw new ArgumentOutOfRangeException ("value", "Value, startIndex and length do not refer to a valid string.");
2691 #endif
2692                                         }
2693
2694                         // GetString () is called even when length == 0
2695                         return enc.GetString (bytes);
2696                 }
2697
2698                 unsafe string CreateString (char *value)
2699                 {
2700                         if (value == null)
2701                                 return string.Empty;
2702                         char *p = value;
2703                         int i = 0;
2704                         while (*p != 0) {
2705                                 ++i;
2706                                 ++p;
2707                         }
2708                         string result = InternalAllocateStr (i);
2709
2710                         if (i != 0) {
2711                                 fixed (char *dest = result) {
2712                                         CharCopy (dest, value, i);
2713                                 }
2714                         }
2715                         return result;
2716                 }
2717
2718                 unsafe string CreateString (char *value, int startIndex, int length)
2719                 {
2720                         if (length == 0)
2721                                 return string.Empty;
2722                         if (value == null)
2723                                 throw new ArgumentNullException ("value");
2724                         if (startIndex < 0)
2725                                 throw new ArgumentOutOfRangeException ("startIndex");
2726                         if (length < 0)
2727                                 throw new ArgumentOutOfRangeException ("length");
2728
2729                         string result = InternalAllocateStr (length);
2730
2731                         fixed (char *dest = result) {
2732                                 CharCopy (dest, value + startIndex, length);
2733                         }
2734                         return result;
2735                 }
2736
2737                 unsafe string CreateString (char [] val, int startIndex, int length)
2738                 {
2739                         if (val == null)
2740                                 throw new ArgumentNullException ("value");
2741                         if (startIndex < 0)
2742                                 throw new ArgumentOutOfRangeException ("startIndex", "Cannot be negative.");
2743                         if (length < 0)
2744                                 throw new ArgumentOutOfRangeException ("length", "Cannot be negative.");
2745                         if (startIndex > val.Length - length)
2746                                 throw new ArgumentOutOfRangeException ("startIndex", "Cannot be negative, and should be less than length of string.");
2747                         if (length == 0)
2748                                 return string.Empty;
2749
2750                         string result = InternalAllocateStr (length);
2751
2752                         fixed (char *dest = result, src = val) {
2753                                 CharCopy (dest, src + startIndex, length);
2754                         }
2755                         return result;
2756                 }
2757
2758                 unsafe string CreateString (char [] val)
2759                 {
2760                         if (val == null)
2761                                 return string.Empty;
2762                         if (val.Length == 0)
2763                                 return string.Empty;
2764                         string result = InternalAllocateStr (val.Length);
2765
2766                         fixed (char *dest = result, src = val) {
2767                                 CharCopy (dest, src, val.Length);
2768                         }
2769                         return result;
2770                 }
2771
2772                 unsafe string CreateString (char c, int count)
2773                 {
2774                         if (count < 0)
2775                                 throw new ArgumentOutOfRangeException ("count");
2776                         if (count == 0)
2777                                 return string.Empty;
2778                         string result = InternalAllocateStr (count);
2779                         fixed (char *dest = result) {
2780                                 char *p = dest;
2781                                 char *end = p + count;
2782                                 while (p < end) {
2783                                         *p = c;
2784                                         p++;
2785                                 }
2786                         }
2787                         return result;
2788                 }
2789 #pragma warning restore 169
2790
2791                 /* helpers used by the runtime as well as above or eslewhere in corlib */
2792                 internal static unsafe void memset (byte *dest, int val, int len)
2793                 {
2794                         if (len < 8) {
2795                                 while (len != 0) {
2796                                         *dest = (byte)val;
2797                                         ++dest;
2798                                         --len;
2799                                 }
2800                                 return;
2801                         }
2802                         if (val != 0) {
2803                                 val = val | (val << 8);
2804                                 val = val | (val << 16);
2805                         }
2806                         // align to 4
2807                         int rest = (int)dest & 3;
2808                         if (rest != 0) {
2809                                 rest = 4 - rest;
2810                                 len -= rest;
2811                                 do {
2812                                         *dest = (byte)val;
2813                                         ++dest;
2814                                         --rest;
2815                                 } while (rest != 0);
2816                         }
2817                         while (len >= 16) {
2818                                 ((int*)dest) [0] = val;
2819                                 ((int*)dest) [1] = val;
2820                                 ((int*)dest) [2] = val;
2821                                 ((int*)dest) [3] = val;
2822                                 dest += 16;
2823                                 len -= 16;
2824                         }
2825                         while (len >= 4) {
2826                                 ((int*)dest) [0] = val;
2827                                 dest += 4;
2828                                 len -= 4;
2829                         }
2830                         // tail bytes
2831                         while (len > 0) {
2832                                 *dest = (byte)val;
2833                                 dest++;
2834                                 len--;
2835                         }
2836                 }
2837
2838                 static unsafe void memcpy4 (byte *dest, byte *src, int size) {
2839                         /*while (size >= 32) {
2840                                 // using long is better than int and slower than double
2841                                 // FIXME: enable this only on correct alignment or on platforms
2842                                 // that can tolerate unaligned reads/writes of doubles
2843                                 ((double*)dest) [0] = ((double*)src) [0];
2844                                 ((double*)dest) [1] = ((double*)src) [1];
2845                                 ((double*)dest) [2] = ((double*)src) [2];
2846                                 ((double*)dest) [3] = ((double*)src) [3];
2847                                 dest += 32;
2848                                 src += 32;
2849                                 size -= 32;
2850                         }*/
2851                         while (size >= 16) {
2852                                 ((int*)dest) [0] = ((int*)src) [0];
2853                                 ((int*)dest) [1] = ((int*)src) [1];
2854                                 ((int*)dest) [2] = ((int*)src) [2];
2855                                 ((int*)dest) [3] = ((int*)src) [3];
2856                                 dest += 16;
2857                                 src += 16;
2858                                 size -= 16;
2859                         }
2860                         while (size >= 4) {
2861                                 ((int*)dest) [0] = ((int*)src) [0];
2862                                 dest += 4;
2863                                 src += 4;
2864                                 size -= 4;
2865                         }
2866                         while (size > 0) {
2867                                 ((byte*)dest) [0] = ((byte*)src) [0];
2868                                 dest += 1;
2869                                 src += 1;
2870                                 --size;
2871                         }
2872                 }
2873                 static unsafe void memcpy2 (byte *dest, byte *src, int size) {
2874                         while (size >= 8) {
2875                                 ((short*)dest) [0] = ((short*)src) [0];
2876                                 ((short*)dest) [1] = ((short*)src) [1];
2877                                 ((short*)dest) [2] = ((short*)src) [2];
2878                                 ((short*)dest) [3] = ((short*)src) [3];
2879                                 dest += 8;
2880                                 src += 8;
2881                                 size -= 8;
2882                         }
2883                         while (size >= 2) {
2884                                 ((short*)dest) [0] = ((short*)src) [0];
2885                                 dest += 2;
2886                                 src += 2;
2887                                 size -= 2;
2888                         }
2889                         if (size > 0)
2890                                 ((byte*)dest) [0] = ((byte*)src) [0];
2891                 }
2892                 static unsafe void memcpy1 (byte *dest, byte *src, int size) {
2893                         while (size >= 8) {
2894                                 ((byte*)dest) [0] = ((byte*)src) [0];
2895                                 ((byte*)dest) [1] = ((byte*)src) [1];
2896                                 ((byte*)dest) [2] = ((byte*)src) [2];
2897                                 ((byte*)dest) [3] = ((byte*)src) [3];
2898                                 ((byte*)dest) [4] = ((byte*)src) [4];
2899                                 ((byte*)dest) [5] = ((byte*)src) [5];
2900                                 ((byte*)dest) [6] = ((byte*)src) [6];
2901                                 ((byte*)dest) [7] = ((byte*)src) [7];
2902                                 dest += 8;
2903                                 src += 8;
2904                                 size -= 8;
2905                         }
2906                         while (size >= 2) {
2907                                 ((byte*)dest) [0] = ((byte*)src) [0];
2908                                 ((byte*)dest) [1] = ((byte*)src) [1];
2909                                 dest += 2;
2910                                 src += 2;
2911                                 size -= 2;
2912                         }
2913                         if (size > 0)
2914                                 ((byte*)dest) [0] = ((byte*)src) [0];
2915                 }
2916
2917                 internal static unsafe void memcpy (byte *dest, byte *src, int size) {
2918                         // FIXME: if pointers are not aligned, try to align them
2919                         // so a faster routine can be used. Handle the case where
2920                         // the pointers can't be reduced to have the same alignment
2921                         // (just ignore the issue on x86?)
2922                         if ((((int)dest | (int)src) & 3) != 0) {
2923                                 if (((int)dest & 1) != 0 && ((int)src & 1) != 0 && size >= 1) {
2924                                         dest [0] = src [0];
2925                                         ++dest;
2926                                         ++src;
2927                                         --size;
2928                                 }
2929                                 if (((int)dest & 2) != 0 && ((int)src & 2) != 0 && size >= 2) {
2930                                         ((short*)dest) [0] = ((short*)src) [0];
2931                                         dest += 2;
2932                                         src += 2;
2933                                         size -= 2;
2934                                 }
2935                                 if ((((int)dest | (int)src) & 1) != 0) {
2936                                         memcpy1 (dest, src, size);
2937                                         return;
2938                                 }
2939                                 if ((((int)dest | (int)src) & 2) != 0) {
2940                                         memcpy2 (dest, src, size);
2941                                         return;
2942                                 }
2943                         }
2944                         memcpy4 (dest, src, size);
2945                 }
2946
2947                 internal static unsafe void CharCopy (char *dest, char *src, int count) {
2948                         // Same rules as for memcpy, but with the premise that 
2949                         // chars can only be aligned to even addresses if their
2950                         // enclosing types are correctly aligned
2951                         if ((((int)(byte*)dest | (int)(byte*)src) & 3) != 0) {
2952                                 if (((int)(byte*)dest & 2) != 0 && ((int)(byte*)src & 2) != 0 && count > 0) {
2953                                         ((short*)dest) [0] = ((short*)src) [0];
2954                                         dest++;
2955                                         src++;
2956                                         count--;
2957                                 }
2958                                 if ((((int)(byte*)dest | (int)(byte*)src) & 2) != 0) {
2959                                         memcpy2 ((byte*)dest, (byte*)src, count * 2);
2960                                         return;
2961                                 }
2962                         }
2963                         memcpy4 ((byte*)dest, (byte*)src, count * 2);
2964                 }
2965
2966                 internal static unsafe void CharCopyReverse (char *dest, char *src, int count)
2967                 {
2968                         dest += count;
2969                         src += count;
2970                         for (int i = count; i > 0; i--) {
2971                                 dest--;
2972                                 src--;
2973                                 *dest = *src;
2974                         }       
2975                 }
2976
2977                 internal static unsafe void CharCopy (String target, int targetIndex, String source, int sourceIndex, int count)
2978                 {
2979                         fixed (char* dest = target, src = source)
2980                                 CharCopy (dest + targetIndex, src + sourceIndex, count);
2981                 }
2982
2983                 internal static unsafe void CharCopy (String target, int targetIndex, Char[] source, int sourceIndex, int count)
2984                 {
2985                         fixed (char* dest = target, src = source)
2986                                 CharCopy (dest + targetIndex, src + sourceIndex, count);
2987                 }
2988
2989                 // Use this method if you cannot block copy from left to right (e.g. because you are coping within the same string)
2990                 internal static unsafe void CharCopyReverse (String target, int targetIndex, String source, int sourceIndex, int count)
2991                 {
2992                         fixed (char* dest = target, src = source)
2993                                 CharCopyReverse (dest + targetIndex, src + sourceIndex, count);
2994                 }
2995
2996                 [CLSCompliant (false), MethodImplAttribute (MethodImplOptions.InternalCall)]
2997                 unsafe public extern String (char *value);
2998
2999                 [CLSCompliant (false), MethodImplAttribute (MethodImplOptions.InternalCall)]
3000                 unsafe public extern String (char *value, int startIndex, int length);
3001
3002                 [CLSCompliant (false), MethodImplAttribute (MethodImplOptions.InternalCall)]
3003                 unsafe public extern String (sbyte *value);
3004
3005                 [CLSCompliant (false), MethodImplAttribute (MethodImplOptions.InternalCall)]
3006                 unsafe public extern String (sbyte *value, int startIndex, int length);
3007
3008                 [CLSCompliant (false), MethodImplAttribute (MethodImplOptions.InternalCall)]
3009                 unsafe public extern String (sbyte *value, int startIndex, int length, Encoding enc);
3010
3011                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
3012                 public extern String (char [] value, int startIndex, int length);
3013
3014                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
3015                 public extern String (char [] value);
3016
3017                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
3018                 public extern String (char c, int count);
3019
3020 //              [MethodImplAttribute (MethodImplOptions.InternalCall)]
3021 //              private extern static string InternalJoin (string separator, string[] value, int sIndex, int count);
3022
3023 //              [MethodImplAttribute (MethodImplOptions.InternalCall)]
3024 //              private extern String InternalReplace (String oldValue, string newValue, CompareInfo comp);
3025
3026 //              [MethodImplAttribute (MethodImplOptions.InternalCall)]
3027 //              private extern void InternalCopyTo (int sIndex, char[] dest, int destIndex, int count);
3028
3029                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
3030                 private extern String[] InternalSplit (char[] separator, int count, int options);
3031
3032 //              [MethodImplAttribute (MethodImplOptions.InternalCall)]
3033 //              private extern String InternalTrim (char[] chars, int typ);
3034
3035 //              [MethodImplAttribute (MethodImplOptions.InternalCall)]
3036 //              private extern int InternalLastIndexOfAny (char [] anyOf, int sIndex, int count);
3037
3038 //              [MethodImplAttribute (MethodImplOptions.InternalCall)]
3039 //              private extern String InternalPad (int width, char chr, bool right);
3040
3041                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
3042                 internal extern static String InternalAllocateStr (int length);
3043
3044                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
3045                 internal extern static void InternalStrcpy (String dest, int destPos, String src);
3046
3047                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
3048                 internal extern static void InternalStrcpy (String dest, int destPos, char[] chars);
3049
3050                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
3051                 internal extern static void InternalStrcpy (String dest, int destPos, String src, int sPos, int count);
3052
3053                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
3054                 internal extern static void InternalStrcpy (String dest, int destPos, char[] chars, int sPos, int count);
3055
3056                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
3057                 private extern static string InternalIntern (string str);
3058
3059                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
3060                 private extern static string InternalIsInterned (string str);
3061         }
3062 }