2009-06-12 Bill Holmes <billholmes54@gmail.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                         if (this.Length == 0)
984                                 return value == String.Empty ? 0 : -1;
985                         else
986                                 return LastIndexOf (value, this.Length - 1, this.Length, comparisonType);
987                 }
988
989                 public int LastIndexOf (string value, int startIndex, StringComparison comparisonType)
990                 {
991                         return LastIndexOf (value, startIndex, startIndex + 1, comparisonType);
992                 }
993
994                 public int LastIndexOf (string value, int startIndex, int count, StringComparison comparisonType)
995                 {
996                         switch (comparisonType) {
997                         case StringComparison.CurrentCulture:
998                                 return CultureInfo.CurrentCulture.CompareInfo.LastIndexOf (this, value, startIndex, count, CompareOptions.None);
999                         case StringComparison.CurrentCultureIgnoreCase:
1000                                 return CultureInfo.CurrentCulture.CompareInfo.LastIndexOf (this, value, startIndex, count, CompareOptions.IgnoreCase);
1001                         case StringComparison.InvariantCulture:
1002                                 return CultureInfo.InvariantCulture.CompareInfo.LastIndexOf (this, value, startIndex, count, CompareOptions.None);
1003                         case StringComparison.InvariantCultureIgnoreCase:
1004                                 return CultureInfo.InvariantCulture.CompareInfo.LastIndexOf (this, value, startIndex, count, CompareOptions.IgnoreCase);
1005                         case StringComparison.Ordinal:
1006                                 return LastIndexOfOrdinal (value, startIndex, count, CompareOptions.Ordinal);
1007                         case StringComparison.OrdinalIgnoreCase:
1008                                 return LastIndexOfOrdinal (value, startIndex, count, CompareOptions.OrdinalIgnoreCase);
1009                         default:
1010                                 string msg = Locale.GetText ("Invalid value '{0}' for StringComparison", comparisonType);
1011                                 throw new ArgumentException (msg, "comparisonType");
1012                         }
1013                 }
1014 #endif
1015
1016                 internal int LastIndexOfOrdinal (string value, int startIndex, int count, CompareOptions options)
1017                 {
1018                         if (value == null)
1019                                 throw new ArgumentNullException ("value");
1020                         if (startIndex < 0 || startIndex > length)
1021                                 throw new ArgumentOutOfRangeException ("startIndex");
1022                         if (count < 0 || (startIndex < count - 1))
1023                                 throw new ArgumentOutOfRangeException ("count");
1024
1025                         if (options == CompareOptions.Ordinal)
1026                                 return LastIndexOfOrdinalUnchecked (value, startIndex, count);
1027                         return LastIndexOfOrdinalIgnoreCaseUnchecked (value, startIndex, count);
1028                 }
1029
1030                 internal unsafe int LastIndexOfOrdinalUnchecked (string value, int startIndex, int count)
1031                 {
1032                         int valueLen = value.Length;
1033                         if (count < valueLen)
1034                                 return -1;
1035
1036                         if (valueLen <= 1) {
1037                                 if (valueLen == 1)
1038                                         return LastIndexOfUnchecked (value[0], startIndex, count);
1039                                 return 0;
1040                         }
1041
1042                         fixed (char* thisptr = this, valueptr = value) {
1043                                 char* ap = thisptr + startIndex - valueLen + 1;
1044                                 char* thisEnd = ap - count + valueLen - 1;
1045                                 while (ap != thisEnd) {
1046                                         if (*ap == *valueptr) {
1047                                                 for (int i = 1; i < valueLen; i++) {
1048                                                         if (ap[i] != valueptr[i])
1049                                                                 goto NextVal;
1050                                                 }
1051                                                 return (int)(ap - thisptr);
1052                                         }
1053                                         NextVal:
1054                                         ap--;
1055                                 }
1056                         }
1057                         return -1;
1058                 }
1059
1060                 internal unsafe int LastIndexOfOrdinalIgnoreCaseUnchecked (string value, int startIndex, int count)
1061                 {
1062                         int valueLen = value.Length;
1063                         if (count < valueLen)
1064                                 return -1;
1065
1066                         if (valueLen == 0)
1067                                 return 0;
1068
1069                         fixed (char* thisptr = this, valueptr = value) {
1070                                 char* ap = thisptr + startIndex - valueLen + 1;
1071                                 char* thisEnd = ap - count + valueLen - 1;
1072                                 while (ap != thisEnd) {
1073                                         for (int i = 0; i < valueLen; i++) {
1074                                                 if (Char.ToUpperInvariant (ap[i]) != Char.ToUpperInvariant (valueptr[i]))
1075                                                         goto NextVal;
1076                                         }
1077                                         return (int)(ap - thisptr);
1078                                         NextVal:
1079                                         ap--;
1080                                 }
1081                         }
1082                         return -1;
1083                 }
1084
1085                 // Following methods are culture-insensitive
1086                 public int IndexOf (char value)
1087                 {
1088                         if (this.length == 0)
1089                                 return -1;
1090
1091                         return IndexOfUnchecked (value, 0, this.length);
1092                 }
1093
1094                 public int IndexOf (char value, int startIndex)
1095                 {
1096                         if (startIndex < 0)
1097                                 throw new ArgumentOutOfRangeException ("startIndex", "< 0");
1098                         if (startIndex > this.length)
1099                                 throw new ArgumentOutOfRangeException ("startIndex", "startIndex > this.length");
1100
1101                         if ((startIndex == 0 && this.length == 0) || (startIndex == this.length))
1102                                 return -1;
1103
1104                         return IndexOfUnchecked (value, startIndex, this.length - startIndex);
1105                 }
1106
1107                 public int IndexOf (char value, int startIndex, int count)
1108                 {
1109                         if (startIndex < 0 || startIndex > this.length)
1110                                 throw new ArgumentOutOfRangeException ("startIndex", "Cannot be negative and must be< 0");
1111                         if (count < 0)
1112                                 throw new ArgumentOutOfRangeException ("count", "< 0");
1113                         if (startIndex > this.length - count)
1114                                 throw new ArgumentOutOfRangeException ("count", "startIndex + count > this.length");
1115
1116                         if ((startIndex == 0 && this.length == 0) || (startIndex == this.length) || (count == 0))
1117                                 return -1;
1118
1119                         return IndexOfUnchecked (value, startIndex, count);
1120                 }
1121
1122                 internal unsafe int IndexOfUnchecked (char value, int startIndex, int count)
1123                 {
1124                         // It helps JIT compiler to optimize comparison
1125                         int value_32 = (int)value;
1126
1127                         fixed (char* start = &start_char) {
1128                                 char* ptr = start + startIndex;
1129                                 char* end_ptr = ptr + (count >> 3 << 3);
1130
1131                                 while (ptr != end_ptr) {
1132                                         if (*ptr == value_32)
1133                                                 return (int)(ptr - start);
1134                                         if (ptr[1] == value_32)
1135                                                 return (int)(ptr - start + 1);
1136                                         if (ptr[2] == value_32)
1137                                                 return (int)(ptr - start + 2);
1138                                         if (ptr[3] == value_32)
1139                                                 return (int)(ptr - start + 3);
1140                                         if (ptr[4] == value_32)
1141                                                 return (int)(ptr - start + 4);
1142                                         if (ptr[5] == value_32)
1143                                                 return (int)(ptr - start + 5);
1144                                         if (ptr[6] == value_32)
1145                                                 return (int)(ptr - start + 6);
1146                                         if (ptr[7] == value_32)
1147                                                 return (int)(ptr - start + 7);
1148
1149                                         ptr += 8;
1150                                 }
1151
1152                                 end_ptr += count & 0x07;
1153                                 while (ptr != end_ptr) {
1154                                         if (*ptr == value_32)
1155                                                 return (int)(ptr - start);
1156
1157                                         ptr++;
1158                                 }
1159                                 return -1;
1160                         }
1161                 }
1162
1163                 internal unsafe int IndexOfOrdinalIgnoreCase (char value, int startIndex, int count)
1164                 {
1165                         if (length == 0)
1166                                 return -1;
1167                         int end = startIndex + count;
1168                         char c = Char.ToUpperInvariant (value);
1169                         fixed (char* s = &start_char) {
1170                                 for (int i = startIndex; i < end; i++)
1171                                         if (Char.ToUpperInvariant (s [i]) == c)
1172                                                 return i;
1173                         }
1174                         return -1;
1175                 }
1176
1177                 // Following methods are culture-sensitive
1178                 public int IndexOf (String value)
1179                 {
1180                         if (value == null)
1181                                 throw new ArgumentNullException ("value");
1182                         if (value.length == 0)
1183                                 return 0;
1184                         if (this.length == 0)
1185                                 return -1;
1186                         return CultureInfo.CurrentCulture.CompareInfo.IndexOf (this, value, 0, length);
1187                 }
1188
1189                 public int IndexOf (String value, int startIndex)
1190                 {
1191                         return IndexOf (value, startIndex, this.length - startIndex);
1192                 }
1193
1194                 public int IndexOf (String value, int startIndex, int count)
1195                 {
1196                         if (value == null)
1197 #if NET_2_0
1198                                 throw new ArgumentNullException ("value");
1199 #else
1200                                 throw new ArgumentNullException ("string2");
1201 #endif
1202                         if (startIndex < 0 || startIndex > this.length)
1203                                 throw new ArgumentOutOfRangeException ("startIndex", "Cannot be negative, and should not exceed length of string.");
1204                         if (count < 0 || startIndex > this.length - count)
1205                                 throw new ArgumentOutOfRangeException ("count", "Cannot be negative, and should point to location in string.");
1206
1207                         if (value.length == 0)
1208                                 return startIndex;
1209
1210                         if (startIndex == 0 && this.length == 0)
1211                                 return -1;
1212
1213                         if (count == 0)
1214                                 return -1;
1215
1216                         return CultureInfo.CurrentCulture.CompareInfo.IndexOf (this, value, startIndex, count);
1217                 }
1218
1219                 // Following methods are culture-insensitive
1220                 public int LastIndexOfAny (char [] anyOf)
1221                 {
1222                         if (anyOf == null)
1223                                 throw new ArgumentNullException ();
1224
1225                         return LastIndexOfAnyUnchecked (anyOf, this.length - 1, this.length);
1226                 }
1227
1228                 public int LastIndexOfAny (char [] anyOf, int startIndex)
1229                 {
1230                         if (anyOf == null)
1231                                 throw new ArgumentNullException ();
1232
1233                         if (startIndex < 0 || startIndex >= this.length)
1234                                 throw new ArgumentOutOfRangeException ("startIndex", "Cannot be negative, and should be less than length of string.");
1235
1236                         if (this.length == 0)
1237                                 return -1;
1238
1239                         return LastIndexOfAnyUnchecked (anyOf, startIndex, startIndex + 1);
1240                 }
1241
1242                 public int LastIndexOfAny (char [] anyOf, int startIndex, int count)
1243                 {
1244                         if (anyOf == null) 
1245                                 throw new ArgumentNullException ();
1246
1247                         if ((startIndex < 0) || (startIndex >= this.Length))
1248                                 throw new ArgumentOutOfRangeException ("startIndex", "< 0 || > this.Length");
1249                         if ((count < 0) || (count > this.Length))
1250                                 throw new ArgumentOutOfRangeException ("count", "< 0 || > this.Length");
1251                         if (startIndex - count + 1 < 0)
1252                                 throw new ArgumentOutOfRangeException ("startIndex - count + 1 < 0");
1253
1254                         if (this.length == 0)
1255                                 return -1;
1256
1257                         return LastIndexOfAnyUnchecked (anyOf, startIndex, count);
1258                 }
1259
1260                 private unsafe int LastIndexOfAnyUnchecked (char [] anyOf, int startIndex, int count)
1261                 {
1262                         if (anyOf.Length == 1)
1263                                 return LastIndexOfUnchecked (anyOf[0], startIndex, count);
1264
1265                         fixed (char* start = this, testStart = anyOf) {
1266                                 char* ptr = start + startIndex;
1267                                 char* ptrEnd = ptr - count;
1268                                 char* test;
1269                                 char* testEnd = testStart + anyOf.Length;
1270
1271                                 while (ptr != ptrEnd) {
1272                                         test = testStart;
1273                                         while (test != testEnd) {
1274                                                 if (*test == *ptr)
1275                                                         return (int)(ptr - start);
1276                                                 test++;
1277                                         }
1278                                         ptr--;
1279                                 }
1280                                 return -1;
1281                         }
1282                 }
1283
1284                 // Following methods are culture-insensitive
1285                 public int LastIndexOf (char value)
1286                 {
1287                         if (this.length == 0)
1288                                 return -1;
1289                         
1290                         return LastIndexOfUnchecked (value, this.length - 1, this.length);
1291                 }
1292
1293                 public int LastIndexOf (char value, int startIndex)
1294                 {
1295                         return LastIndexOf (value, startIndex, startIndex + 1);
1296                 }
1297
1298                 public int LastIndexOf (char value, int startIndex, int count)
1299                 {
1300                         if (startIndex == 0 && this.length == 0)
1301                                 return -1;
1302
1303                         // >= for char (> for string)
1304                         if ((startIndex < 0) || (startIndex >= this.Length))
1305                                 throw new ArgumentOutOfRangeException ("startIndex", "< 0 || >= this.Length");
1306                         if ((count < 0) || (count > this.Length))
1307                                 throw new ArgumentOutOfRangeException ("count", "< 0 || > this.Length");
1308                         if (startIndex - count + 1 < 0)
1309                                 throw new ArgumentOutOfRangeException ("startIndex - count + 1 < 0");
1310
1311                         return LastIndexOfUnchecked (value, startIndex, count);
1312                 }
1313
1314                 internal unsafe int LastIndexOfUnchecked (char value, int startIndex, int count)
1315                 {
1316                         // It helps JIT compiler to optimize comparison
1317                         int value_32 = (int)value;
1318
1319                         fixed (char* start = &start_char) {
1320                                 char* ptr = start + startIndex;
1321                                 char* end_ptr = ptr - (count >> 3 << 3);
1322
1323                                 while (ptr != end_ptr) {
1324                                         if (*ptr == value_32)
1325                                                 return (int)(ptr - start);
1326                                         if (ptr[-1] == value_32)
1327                                                 return (int)(ptr - start) - 1;
1328                                         if (ptr[-2] == value_32)
1329                                                 return (int)(ptr - start) - 2;
1330                                         if (ptr[-3] == value_32)
1331                                                 return (int)(ptr - start) - 3;
1332                                         if (ptr[-4] == value_32)
1333                                                 return (int)(ptr - start) - 4;
1334                                         if (ptr[-5] == value_32)
1335                                                 return (int)(ptr - start) - 5;
1336                                         if (ptr[-6] == value_32)
1337                                                 return (int)(ptr - start) - 6;
1338                                         if (ptr[-7] == value_32)
1339                                                 return (int)(ptr - start) - 7;
1340
1341                                         ptr -= 8;
1342                                 }
1343
1344                                 end_ptr -= count & 0x07;
1345                                 while (ptr != end_ptr) {
1346                                         if (*ptr == value_32)
1347                                                 return (int)(ptr - start);
1348
1349                                         ptr--;
1350                                 }
1351                                 return -1;
1352                         }
1353                 }
1354
1355                 internal unsafe int LastIndexOfOrdinalIgnoreCase (char value, int startIndex, int count)
1356                 {
1357                         if (length == 0)
1358                                 return -1;
1359                         int end = startIndex - count;
1360                         char c = Char.ToUpperInvariant (value);
1361                         fixed (char* s = &start_char) {
1362                                 for (int i = startIndex; i > end; i--)
1363                                         if (Char.ToUpperInvariant (s [i]) == c)
1364                                                 return i;
1365                         }
1366                         return -1;
1367                 }
1368
1369                 // Following methods are culture-sensitive
1370                 public int LastIndexOf (String value)
1371                 {
1372                         if (this.length == 0)
1373                                 // This overload does additional checking
1374                                 return LastIndexOf (value, 0, 0);
1375                         else
1376                                 return LastIndexOf (value, this.length - 1, this.length);
1377                 }
1378
1379                 public int LastIndexOf (String value, int startIndex)
1380                 {
1381                         int max = startIndex;
1382                         if (max < this.Length)
1383                                 max++;
1384                         return LastIndexOf (value, startIndex, max);
1385                 }
1386
1387                 public int LastIndexOf (String value, int startIndex, int count)
1388                 {
1389                         if (value == null)
1390 #if NET_2_0
1391                                 throw new ArgumentNullException ("value");
1392 #else
1393                                 throw new ArgumentNullException ("string2");
1394 #endif
1395
1396                         // -1 > startIndex > for string (0 > startIndex >= for char)
1397                         if ((startIndex < -1) || (startIndex > this.Length))
1398                                 throw new ArgumentOutOfRangeException ("startIndex", "< 0 || > this.Length");
1399                         if ((count < 0) || (count > this.Length))
1400                                 throw new ArgumentOutOfRangeException ("count", "< 0 || > this.Length");
1401                         if (startIndex - count + 1 < 0)
1402                                 throw new ArgumentOutOfRangeException ("startIndex - count + 1 < 0");
1403
1404                         if (value.Length == 0)
1405                                 return startIndex;
1406
1407                         if (startIndex == 0 && this.length == 0)
1408                                 return -1;
1409
1410                         // This check is needed to match undocumented MS behaviour
1411                         if (this.length == 0 && value.length > 0)
1412                                 return -1;
1413
1414                         if (count == 0)
1415                                 return -1;
1416
1417                         if (startIndex == this.Length)
1418                                 startIndex--;
1419                         return CultureInfo.CurrentCulture.CompareInfo.LastIndexOf (this, value, startIndex, count);
1420                 }
1421
1422 #if NET_2_0
1423                 public bool Contains (String value)
1424                 {
1425                         return IndexOf (value) != -1;
1426                 }
1427
1428                 public static bool IsNullOrEmpty (String value)
1429                 {
1430                         return (value == null) || (value.Length == 0);
1431                 }
1432
1433                 public string Normalize ()
1434                 {
1435                         return Normalization.Normalize (this, 0);
1436                 }
1437
1438                 public string Normalize (NormalizationForm normalizationForm)
1439                 {
1440                         switch (normalizationForm) {
1441                         default:
1442                                 return Normalization.Normalize (this, 0);
1443                         case NormalizationForm.FormD:
1444                                 return Normalization.Normalize (this, 1);
1445                         case NormalizationForm.FormKC:
1446                                 return Normalization.Normalize (this, 2);
1447                         case NormalizationForm.FormKD:
1448                                 return Normalization.Normalize (this, 3);
1449                         }
1450                 }
1451
1452                 public bool IsNormalized ()
1453                 {
1454                         return Normalization.IsNormalized (this, 0);
1455                 }
1456
1457                 public bool IsNormalized (NormalizationForm normalizationForm)
1458                 {
1459                         switch (normalizationForm) {
1460                         default:
1461                                 return Normalization.IsNormalized (this, 0);
1462                         case NormalizationForm.FormD:
1463                                 return Normalization.IsNormalized (this, 1);
1464                         case NormalizationForm.FormKC:
1465                                 return Normalization.IsNormalized (this, 2);
1466                         case NormalizationForm.FormKD:
1467                                 return Normalization.IsNormalized (this, 3);
1468                         }
1469                 }
1470
1471                 public string Remove (int startIndex)
1472                 {
1473                         if (startIndex < 0)
1474                                 throw new ArgumentOutOfRangeException ("startIndex", "StartIndex can not be less than zero");
1475                         if (startIndex >= this.length)
1476                                 throw new ArgumentOutOfRangeException ("startIndex", "StartIndex must be less than the length of the string");
1477
1478                         return Remove (startIndex, this.length - startIndex);
1479                 }
1480 #endif
1481
1482                 public String PadLeft (int totalWidth)
1483                 {
1484                         return PadLeft (totalWidth, ' ');
1485                 }
1486
1487                 public unsafe String PadLeft (int totalWidth, char paddingChar)
1488                 {
1489                         //LAMESPEC: MSDN Doc says this is reversed for RtL languages, but this seems to be untrue
1490
1491                         if (totalWidth < 0)
1492                                 throw new ArgumentOutOfRangeException ("totalWidth", "< 0");
1493
1494                         if (totalWidth < this.length)
1495                                 return this;
1496
1497                         String tmp = InternalAllocateStr (totalWidth);
1498
1499                         fixed (char* dest = tmp, src = this) {
1500                                 char* padPos = dest;
1501                                 char* padTo = dest + (totalWidth - length);
1502                                 while (padPos != padTo)
1503                                         *padPos++ = paddingChar;
1504
1505                                 CharCopy (padTo, src, length);
1506                         }
1507                         return tmp;
1508                 }
1509
1510                 public String PadRight (int totalWidth)
1511                 {
1512                         return PadRight (totalWidth, ' ');
1513                 }
1514
1515                 public unsafe String PadRight (int totalWidth, char paddingChar)
1516                 {
1517                         //LAMESPEC: MSDN Doc says this is reversed for RtL languages, but this seems to be untrue
1518
1519                         if (totalWidth < 0)
1520                                 throw new ArgumentOutOfRangeException ("totalWidth", "< 0");
1521
1522                         if (totalWidth < this.length)
1523                                 return this;
1524
1525                         String tmp = InternalAllocateStr (totalWidth);
1526
1527                         fixed (char* dest = tmp, src = this) {
1528                                 CharCopy (dest, src, length);
1529
1530                                 char* padPos = dest + length;
1531                                 char* padTo = dest + totalWidth;
1532                                 while (padPos != padTo)
1533                                         *padPos++ = paddingChar;
1534                         }
1535                         return tmp;
1536                 }
1537
1538                 public bool StartsWith (String value)
1539                 {
1540                         if (value == null)
1541                                 throw new ArgumentNullException ("value");
1542
1543                         return CultureInfo.CurrentCulture.CompareInfo.IsPrefix (this, value, CompareOptions.None);
1544                 }
1545
1546 #if NET_2_0
1547                 [ComVisible (false)]
1548                 public bool StartsWith (string value, StringComparison comparisonType)
1549                 {
1550                         if (value == null)
1551                                 throw new ArgumentNullException ("value");
1552
1553                         switch (comparisonType) {
1554                         case StringComparison.CurrentCulture:
1555                                 return CultureInfo.CurrentCulture.CompareInfo.IsPrefix (this, value, CompareOptions.None);
1556                         case StringComparison.CurrentCultureIgnoreCase:
1557                                 return CultureInfo.CurrentCulture.CompareInfo.IsPrefix (this, value, CompareOptions.IgnoreCase);
1558                         case StringComparison.InvariantCulture:
1559                                 return CultureInfo.InvariantCulture.CompareInfo.IsPrefix (this, value, CompareOptions.None);
1560                         case StringComparison.InvariantCultureIgnoreCase:
1561                                 return CultureInfo.InvariantCulture.CompareInfo.IsPrefix (this, value, CompareOptions.IgnoreCase);
1562                         case StringComparison.Ordinal:
1563                                 return CultureInfo.CurrentCulture.CompareInfo.IsPrefix (this, value, CompareOptions.Ordinal);
1564                         case StringComparison.OrdinalIgnoreCase:
1565                                 return CultureInfo.CurrentCulture.CompareInfo.IsPrefix (this, value, CompareOptions.OrdinalIgnoreCase);
1566                         default:
1567                                 string msg = Locale.GetText ("Invalid value '{0}' for StringComparison", comparisonType);
1568                                 throw new ArgumentException (msg, "comparisonType");
1569                         }
1570                 }
1571
1572                 [ComVisible (false)]
1573                 public bool EndsWith (string value, StringComparison comparisonType)
1574                 {
1575                         if (value == null)
1576                                 throw new ArgumentNullException ("value");
1577
1578                         switch (comparisonType) {
1579                         case StringComparison.CurrentCulture:
1580                                 return CultureInfo.CurrentCulture.CompareInfo.IsSuffix (this, value, CompareOptions.None);
1581                         case StringComparison.CurrentCultureIgnoreCase:
1582                                 return CultureInfo.CurrentCulture.CompareInfo.IsSuffix (this, value, CompareOptions.IgnoreCase);
1583                         case StringComparison.InvariantCulture:
1584                                 return CultureInfo.InvariantCulture.CompareInfo.IsSuffix (this, value, CompareOptions.None);
1585                         case StringComparison.InvariantCultureIgnoreCase:
1586                                 return CultureInfo.InvariantCulture.CompareInfo.IsSuffix (this, value, CompareOptions.IgnoreCase);
1587                         case StringComparison.Ordinal:
1588                                 return CultureInfo.CurrentCulture.CompareInfo.IsSuffix (this, value, CompareOptions.Ordinal);
1589                         case StringComparison.OrdinalIgnoreCase:
1590                                 return CultureInfo.CurrentCulture.CompareInfo.IsSuffix (this, value, CompareOptions.OrdinalIgnoreCase);
1591                         default:
1592                                 string msg = Locale.GetText ("Invalid value '{0}' for StringComparison", comparisonType);
1593                                 throw new ArgumentException (msg, "comparisonType");
1594                         }
1595                 }
1596 #endif
1597
1598 #if NET_2_0
1599                 public
1600 #else
1601                 internal
1602 #endif
1603                 bool StartsWith (String value, bool ignoreCase, CultureInfo culture)
1604                 {
1605                         if (culture == null)
1606                                 culture = CultureInfo.CurrentCulture;
1607                         
1608                         return culture.CompareInfo.IsPrefix (this, value, ignoreCase ? CompareOptions.IgnoreCase : CompareOptions.None);
1609                 }
1610
1611                 // Following method is culture-insensitive
1612                 public unsafe String Replace (char oldChar, char newChar)
1613                 {
1614                         if (this.length == 0 || oldChar == newChar)
1615                                 return this;
1616
1617                         int start_pos = IndexOfUnchecked (oldChar, 0, this.length);
1618                         if (start_pos == -1)
1619                                 return this;
1620
1621                         if (start_pos < 4)
1622                                 start_pos = 0;
1623
1624                         string tmp = InternalAllocateStr (length);
1625                         fixed (char* dest = tmp, src = &start_char) {
1626                                 if (start_pos != 0)
1627                                         CharCopy (dest, src, start_pos);
1628
1629                                 char* end_ptr = dest + length;
1630                                 char* dest_ptr = dest + start_pos;
1631                                 char* src_ptr = src + start_pos;
1632
1633                                 while (dest_ptr != end_ptr) {
1634                                         if (*src_ptr == oldChar)
1635                                                 *dest_ptr = newChar;
1636                                         else
1637                                                 *dest_ptr = *src_ptr;
1638
1639                                         ++src_ptr;
1640                                         ++dest_ptr;
1641                                 }
1642                         }
1643                         return tmp;
1644                 }
1645
1646                 // culture-insensitive using ordinal search (See testcase StringTest.ReplaceStringCultureTests)
1647                 public String Replace (String oldValue, String newValue)
1648                 {
1649                         // LAMESPEC: According to MSDN the following method is culture-sensitive but this seems to be incorrect
1650                         // LAMESPEC: Result is undefined if result length is longer than maximum string length
1651
1652                         if (oldValue == null)
1653                                 throw new ArgumentNullException ("oldValue");
1654
1655                         if (oldValue.Length == 0)
1656                                 throw new ArgumentException ("oldValue is the empty string.");
1657
1658                         if (this.Length == 0)
1659                                 return this;
1660                         
1661                         if (newValue == null)
1662                                 newValue = String.Empty;
1663
1664                         return ReplaceUnchecked (oldValue, newValue);
1665                 }
1666
1667                 private unsafe String ReplaceUnchecked (String oldValue, String newValue)
1668                 {
1669                         if (oldValue.length > length)
1670                                 return this;
1671                         if (oldValue.length == 1 && newValue.length == 1) {
1672                                 return Replace (oldValue[0], newValue[0]);
1673                                 // ENHANCE: It would be possible to special case oldValue.length == newValue.length
1674                                 // because the length of the result would be this.length and length calculation unneccesary
1675                         }
1676
1677                         const int maxValue = 200; // Allocate 800 byte maximum
1678                         int* dat = stackalloc int[maxValue];
1679                         fixed (char* source = this, replace = newValue) {
1680                                 int i = 0, count = 0;
1681                                 while (i < length) {
1682                                         int found = IndexOfOrdinalUnchecked (oldValue, i, length - i);
1683                                         if (found < 0)
1684                                                 break;
1685                                         else {
1686                                                 if (count < maxValue)
1687                                                         dat[count++] = found;
1688                                                 else
1689                                                         return ReplaceFallback (oldValue, newValue, maxValue);
1690                                         }
1691                                         i = found + oldValue.length;
1692                                 }
1693                                 if (count == 0)
1694                                         return this;
1695                                 int nlen = this.length + ((newValue.length - oldValue.length) * count);
1696                                 String tmp = InternalAllocateStr (nlen);
1697
1698                                 int curPos = 0, lastReadPos = 0;
1699                                 fixed (char* dest = tmp) {
1700                                         for (int j = 0; j < count; j++) {
1701                                                 int precopy = dat[j] - lastReadPos;
1702                                                 CharCopy (dest + curPos, source + lastReadPos, precopy);
1703                                                 curPos += precopy;
1704                                                 lastReadPos = dat[j] + oldValue.length;
1705                                                 CharCopy (dest + curPos, replace, newValue.length);
1706                                                 curPos += newValue.length;
1707                                         }
1708                                         CharCopy (dest + curPos, source + lastReadPos, length - lastReadPos);
1709                                 }
1710                                 return tmp;
1711                         }
1712                 }
1713
1714                 private String ReplaceFallback (String oldValue, String newValue, int testedCount)
1715                 {
1716                         int lengthEstimate = this.length + ((newValue.length - oldValue.length) * testedCount);
1717                         StringBuilder sb = new StringBuilder (lengthEstimate);
1718                         for (int i = 0; i < length;) {
1719                                 int found = IndexOfOrdinalUnchecked (oldValue, i, length - i);
1720                                 if (found < 0) {
1721                                         sb.Append (SubstringUnchecked (i, length - i));
1722                                         break;
1723                                 }
1724                                 sb.Append (SubstringUnchecked (i, found - i));
1725                                 sb.Append (newValue);
1726                                 i = found + oldValue.Length;
1727                         }
1728                         return sb.ToString ();
1729
1730                 }
1731
1732                 public unsafe String Remove (int startIndex, int count)
1733                 {
1734                         if (startIndex < 0)
1735                                 throw new ArgumentOutOfRangeException ("startIndex", "Cannot be negative.");
1736                         if (count < 0)
1737                                 throw new ArgumentOutOfRangeException ("count", "Cannot be negative.");
1738                         if (startIndex > this.length - count)
1739                                 throw new ArgumentOutOfRangeException ("count", "startIndex + count > this.length");
1740
1741                         String tmp = InternalAllocateStr (this.length - count);
1742
1743                         fixed (char *dest = tmp, src = this) {
1744                                 char *dst = dest;
1745                                 CharCopy (dst, src, startIndex);
1746                                 int skip = startIndex + count;
1747                                 dst += startIndex;
1748                                 CharCopy (dst, src + skip, length - skip);
1749                         }
1750                         return tmp;
1751                 }
1752
1753                 public String ToLower ()
1754                 {
1755                         return ToLower (CultureInfo.CurrentCulture);
1756                 }
1757
1758                 public String ToLower (CultureInfo culture)
1759                 {
1760                         if (culture == null)
1761                                 throw new ArgumentNullException ("culture");
1762
1763                         if (culture.LCID == 0x007F) // Invariant
1764                                 return ToLowerInvariant ();
1765
1766                         return culture.TextInfo.ToLower (this);
1767                 }
1768
1769 #if NET_2_0
1770                 public unsafe String ToLowerInvariant ()
1771 #else
1772                 internal unsafe String ToLowerInvariant ()
1773 #endif
1774                 {
1775                         string tmp = InternalAllocateStr (length);
1776                         fixed (char* source = &start_char, dest = tmp) {
1777
1778                                 char* destPtr = (char*)dest;
1779                                 char* sourcePtr = (char*)source;
1780
1781                                 for (int n = 0; n < length; n++) {
1782                                         *destPtr = Char.ToLowerInvariant (*sourcePtr);
1783                                         sourcePtr++;
1784                                         destPtr++;
1785                                 }
1786                         }
1787                         return tmp;
1788                 }
1789
1790                 public String ToUpper ()
1791                 {
1792                         return ToUpper (CultureInfo.CurrentCulture);
1793                 }
1794
1795                 public String ToUpper (CultureInfo culture)
1796                 {
1797                         if (culture == null)
1798                                 throw new ArgumentNullException ("culture");
1799
1800                         if (culture.LCID == 0x007F) // Invariant
1801                                 return ToUpperInvariant ();
1802
1803                         return culture.TextInfo.ToUpper (this);
1804                 }
1805
1806 #if NET_2_0
1807                 public unsafe String ToUpperInvariant ()
1808 #else
1809                 internal unsafe String ToUpperInvariant ()
1810 #endif
1811                 {
1812                         string tmp = InternalAllocateStr (length);
1813                         fixed (char* source = &start_char, dest = tmp) {
1814
1815                                 char* destPtr = (char*)dest;
1816                                 char* sourcePtr = (char*)source;
1817
1818                                 for (int n = 0; n < length; n++) {
1819                                         *destPtr = Char.ToUpperInvariant (*sourcePtr);
1820                                         sourcePtr++;
1821                                         destPtr++;
1822                                 }
1823                         }
1824                         return tmp;
1825                 }
1826
1827                 public override String ToString ()
1828                 {
1829                         return this;
1830                 }
1831
1832                 public String ToString (IFormatProvider provider)
1833                 {
1834                         return this;
1835                 }
1836
1837                 public static String Format (String format, Object arg0)
1838                 {
1839                         return Format (null, format, new Object[] {arg0});
1840                 }
1841
1842                 public static String Format (String format, Object arg0, Object arg1)
1843                 {
1844                         return Format (null, format, new Object[] {arg0, arg1});
1845                 }
1846
1847                 public static String Format (String format, Object arg0, Object arg1, Object arg2)
1848                 {
1849                         return Format (null, format, new Object[] {arg0, arg1, arg2});
1850                 }
1851
1852                 public static string Format (string format, params object[] args)
1853                 {
1854                         return Format (null, format, args);
1855                 }
1856         
1857                 public static string Format (IFormatProvider provider, string format, params object[] args)
1858                 {
1859                         StringBuilder b = FormatHelper (null, provider, format, args);
1860                         return b.ToString ();
1861                 }
1862                 
1863                 internal static StringBuilder FormatHelper (StringBuilder result, IFormatProvider provider, string format, params object[] args)
1864                 {
1865                         if (format == null)
1866                                 throw new ArgumentNullException ("format");
1867                         if (args == null)
1868                                 throw new ArgumentNullException ("args");
1869
1870                         if (result == null) {
1871                                 /* Try to approximate the size of result to avoid reallocations */
1872                                 int i, len;
1873
1874                                 len = 0;
1875                                 for (i = 0; i < args.Length; ++i) {
1876                                         string s = args [i] as string;
1877                                         if (s != null)
1878                                                 len += s.length;
1879                                         else
1880                                                 break;
1881                                 }
1882                                 if (i == args.Length)
1883                                         result = new StringBuilder (len + format.length);
1884                                 else
1885                                         result = new StringBuilder ();
1886                         }
1887
1888                         int ptr = 0;
1889                         int start = ptr;
1890                         while (ptr < format.length) {
1891                                 char c = format[ptr ++];
1892
1893                                 if (c == '{') {
1894                                         result.Append (format, start, ptr - start - 1);
1895
1896                                         // check for escaped open bracket
1897
1898                                         if (format[ptr] == '{') {
1899                                                 start = ptr ++;
1900                                                 continue;
1901                                         }
1902
1903                                         // parse specifier
1904                                 
1905                                         int n, width;
1906                                         bool left_align;
1907                                         string arg_format;
1908
1909                                         ParseFormatSpecifier (format, ref ptr, out n, out width, out left_align, out arg_format);
1910                                         if (n >= args.Length)
1911                                                 throw new FormatException ("Index (zero based) must be greater than or equal to zero and less than the size of the argument list.");
1912
1913                                         // format argument
1914
1915                                         object arg = args[n];
1916
1917                                         string str;
1918                                         ICustomFormatter formatter = null;
1919                                         if (provider != null)
1920                                                 formatter = provider.GetFormat (typeof (ICustomFormatter))
1921                                                         as ICustomFormatter;
1922                                         if (arg == null)
1923                                                 str = String.Empty;
1924                                         else if (formatter != null)
1925                                                 str = formatter.Format (arg_format, arg, provider);
1926                                         else if (arg is IFormattable)
1927                                                 str = ((IFormattable)arg).ToString (arg_format, provider);
1928                                         else
1929                                                 str = arg.ToString ();
1930
1931                                         // pad formatted string and append to result
1932
1933                                         if (width > str.length) {
1934                                                 const char padchar = ' ';
1935                                                 int padlen = width - str.length;
1936
1937                                                 if (left_align) {
1938                                                         result.Append (str);
1939                                                         result.Append (padchar, padlen);
1940                                                 }
1941                                                 else {
1942                                                         result.Append (padchar, padlen);
1943                                                         result.Append (str);
1944                                                 }
1945                                         }
1946                                         else
1947                                                 result.Append (str);
1948
1949                                         start = ptr;
1950                                 }
1951                                 else if (c == '}' && ptr < format.length && format[ptr] == '}') {
1952                                         result.Append (format, start, ptr - start - 1);
1953                                         start = ptr ++;
1954                                 }
1955                                 else if (c == '}') {
1956                                         throw new FormatException ("Input string was not in a correct format.");
1957                                 }
1958                         }
1959
1960                         if (start < format.length)
1961                                 result.Append (format, start, format.Length - start);
1962
1963                         return result;
1964                 }
1965
1966                 public unsafe static String Copy (String str)
1967                 {
1968                         if (str == null)
1969                                 throw new ArgumentNullException ("str");
1970
1971                         int length = str.length;
1972
1973                         String tmp = InternalAllocateStr (length);
1974                         if (length != 0) {
1975                                 fixed (char *dest = tmp, src = str) {
1976                                         CharCopy (dest, src, length);
1977                                 }
1978                         }
1979                         return tmp;
1980                 }
1981
1982                 public static String Concat (Object arg0)
1983                 {
1984                         if (arg0 == null)
1985                                 return String.Empty;
1986
1987                         return arg0.ToString ();
1988                 }
1989
1990                 public unsafe static String Concat (Object arg0, Object arg1)
1991                 {
1992                         string s1, s2;
1993
1994                         s1 = (arg0 != null) ? arg0.ToString () : null;
1995                         s2 = (arg1 != null) ? arg1.ToString () : null;
1996                         
1997                         if (s1 == null) {
1998                                 if (s2 == null)
1999                                         return String.Empty;
2000                                 else
2001                                         return s2;
2002                         } else if (s2 == null)
2003                                 return s1;
2004
2005                         String tmp = InternalAllocateStr (s1.Length + s2.Length);
2006                         if (s1.Length != 0) {
2007                                 fixed (char *dest = tmp, src = s1) {
2008                                         CharCopy (dest, src, s1.length);
2009                                 }
2010                         }
2011                         if (s2.Length != 0) {
2012                                 fixed (char *dest = tmp, src = s2) {
2013                                         CharCopy (dest + s1.Length, src, s2.length);
2014                                 }
2015                         }
2016
2017                         return tmp;
2018                 }
2019
2020                 public static String Concat (Object arg0, Object arg1, Object arg2)
2021                 {
2022                         string s1, s2, s3;
2023                         if (arg0 == null)
2024                                 s1 = String.Empty;
2025                         else
2026                                 s1 = arg0.ToString ();
2027
2028                         if (arg1 == null)
2029                                 s2 = String.Empty;
2030                         else
2031                                 s2 = arg1.ToString ();
2032
2033                         if (arg2 == null)
2034                                 s3 = String.Empty;
2035                         else
2036                                 s3 = arg2.ToString ();
2037
2038                         return Concat (s1, s2, s3);
2039                 }
2040
2041 #if ! BOOTSTRAP_WITH_OLDLIB
2042                 [CLSCompliant(false)]
2043                 public static String Concat (Object arg0, Object arg1, Object arg2,
2044                                              Object arg3, __arglist)
2045                 {
2046                         string s1, s2, s3, s4;
2047
2048                         if (arg0 == null)
2049                                 s1 = String.Empty;
2050                         else
2051                                 s1 = arg0.ToString ();
2052
2053                         if (arg1 == null)
2054                                 s2 = String.Empty;
2055                         else
2056                                 s2 = arg1.ToString ();
2057
2058                         if (arg2 == null)
2059                                 s3 = String.Empty;
2060                         else
2061                                 s3 = arg2.ToString ();
2062
2063                         ArgIterator iter = new ArgIterator (__arglist);
2064                         int argCount = iter.GetRemainingCount();
2065
2066                         StringBuilder sb = new StringBuilder ();
2067                         if (arg3 != null)
2068                                 sb.Append (arg3.ToString ());
2069
2070                         for (int i = 0; i < argCount; i++) {
2071                                 TypedReference typedRef = iter.GetNextArg ();
2072                                 sb.Append (TypedReference.ToObject (typedRef));
2073                         }
2074
2075                         s4 = sb.ToString ();
2076
2077                         return Concat (s1, s2, s3, s4);                 
2078                 }
2079 #endif
2080
2081                 public unsafe static String Concat (String str0, String str1)
2082                 {
2083                         if (str0 == null || str0.Length == 0) {
2084                                 if (str1 == null || str1.Length == 0)
2085                                         return String.Empty;
2086                                 return str1;
2087                         }
2088
2089                         if (str1 == null || str1.Length == 0)
2090                                 return str0; 
2091
2092                         String tmp = InternalAllocateStr (str0.length + str1.length);
2093
2094                         fixed (char *dest = tmp, src = str0)
2095                                 CharCopy (dest, src, str0.length);
2096                         fixed (char *dest = tmp, src = str1)
2097                                 CharCopy (dest + str0.Length, src, str1.length);
2098
2099                         return tmp;
2100                 }
2101
2102                 public unsafe static String Concat (String str0, String str1, String str2)
2103                 {
2104                         if (str0 == null || str0.Length == 0){
2105                                 if (str1 == null || str1.Length == 0){
2106                                         if (str2 == null || str2.Length == 0)
2107                                                 return String.Empty;
2108                                         return str2;
2109                                 } else {
2110                                         if (str2 == null || str2.Length == 0)
2111                                                 return str1;
2112                                 }
2113                                 str0 = String.Empty;
2114                         } else {
2115                                 if (str1 == null || str1.Length == 0){
2116                                         if (str2 == null || str2.Length == 0)
2117                                                 return str0;
2118                                         else
2119                                                 str1 = String.Empty;
2120                                 } else {
2121                                         if (str2 == null || str2.Length == 0)
2122                                                 str2 = String.Empty;
2123                                 }
2124                         }
2125
2126                         String tmp = InternalAllocateStr (str0.length + str1.length + str2.length);
2127
2128                         if (str0.Length != 0) {
2129                                 fixed (char *dest = tmp, src = str0) {
2130                                         CharCopy (dest, src, str0.length);
2131                                 }
2132                         }
2133                         if (str1.Length != 0) {
2134                                 fixed (char *dest = tmp, src = str1) {
2135                                         CharCopy (dest + str0.Length, src, str1.length);
2136                                 }
2137                         }
2138                         if (str2.Length != 0) {
2139                                 fixed (char *dest = tmp, src = str2) {
2140                                         CharCopy (dest + str0.Length + str1.Length, src, str2.length);
2141                                 }
2142                         }
2143
2144                         return tmp;
2145                 }
2146
2147                 public unsafe static String Concat (String str0, String str1, String str2, String str3)
2148                 {
2149                         if (str0 == null && str1 == null && str2 == null && str3 == null)
2150                                 return String.Empty;
2151
2152                         if (str0 == null)
2153                                 str0 = String.Empty;
2154                         if (str1 == null)
2155                                 str1 = String.Empty;
2156                         if (str2 == null)
2157                                 str2 = String.Empty;
2158                         if (str3 == null)
2159                                 str3 = String.Empty;
2160
2161                         String tmp = InternalAllocateStr (str0.length + str1.length + str2.length + str3.length);
2162
2163                         if (str0.Length != 0) {
2164                                 fixed (char *dest = tmp, src = str0) {
2165                                         CharCopy (dest, src, str0.length);
2166                                 }
2167                         }
2168                         if (str1.Length != 0) {
2169                                 fixed (char *dest = tmp, src = str1) {
2170                                         CharCopy (dest + str0.Length, src, str1.length);
2171                                 }
2172                         }
2173                         if (str2.Length != 0) {
2174                                 fixed (char *dest = tmp, src = str2) {
2175                                         CharCopy (dest + str0.Length + str1.Length, src, str2.length);
2176                                 }
2177                         }
2178                         if (str3.Length != 0) {
2179                                 fixed (char *dest = tmp, src = str3) {
2180                                         CharCopy (dest + str0.Length + str1.Length + str2.Length, src, str3.length);
2181                                 }
2182                         }
2183
2184                         return tmp;
2185                 }
2186
2187                 public static String Concat (params Object[] args)
2188                 {
2189                         if (args == null)
2190                                 throw new ArgumentNullException ("args");
2191
2192                         int argLen = args.Length;
2193                         if (argLen == 0)
2194                                 return String.Empty;
2195
2196                         string [] strings = new string [argLen];
2197                         int len = 0;
2198                         for (int i = 0; i < argLen; i++) {
2199                                 if (args[i] != null) {
2200                                         strings[i] = args[i].ToString ();
2201                                         len += strings[i].length;
2202                                 }
2203                         }
2204                         if (len == 0)
2205                                 return String.Empty;
2206
2207                         return ConcatInternal (strings, len);
2208                 }
2209
2210                 public static String Concat (params String[] values)
2211                 {
2212                         if (values == null)
2213                                 throw new ArgumentNullException ("values");
2214
2215                         int len = 0;
2216                         for (int i = 0; i < values.Length; i++) {
2217                                 String s = values[i];
2218                                 if (s != null)
2219                                         len += s.length;
2220                         }
2221                         if (len == 0)
2222                                 return String.Empty;
2223
2224                         return ConcatInternal (values, len);
2225                 }
2226
2227                 private static unsafe String ConcatInternal (String[] values, int length)
2228                 {
2229                         String tmp = InternalAllocateStr (length);
2230
2231                         fixed (char* dest = tmp) {
2232                                 int pos = 0;
2233                                 for (int i = 0; i < values.Length; i++) {
2234                                         String source = values[i];
2235                                         if (source != null) {
2236                                                 fixed (char* src = source) {
2237                                                         CharCopy (dest + pos, src, source.length);
2238                                                 }
2239                                                 pos += source.Length;
2240                                         }
2241                                 }
2242                         }
2243                         return tmp;
2244                 }
2245
2246                 public unsafe String Insert (int startIndex, String value)
2247                 {
2248                         if (value == null)
2249                                 throw new ArgumentNullException ("value");
2250
2251                         if (startIndex < 0 || startIndex > this.length)
2252                                 throw new ArgumentOutOfRangeException ("startIndex", "Cannot be negative and must be less than or equal to length of string.");
2253
2254                         if (value.Length == 0)
2255                                 return this;
2256                         if (this.Length == 0)
2257                                 return value;
2258                         String tmp = InternalAllocateStr (this.length + value.length);
2259
2260                         fixed (char *dest = tmp, src = this, val = value) {
2261                                 char *dst = dest;
2262                                 CharCopy (dst, src, startIndex);
2263                                 dst += startIndex;
2264                                 CharCopy (dst, val, value.length);
2265                                 dst += value.length;
2266                                 CharCopy (dst, src + startIndex, length - startIndex);
2267                         }
2268                         return tmp;
2269                 }
2270
2271                 public static string Intern (string str)
2272                 {
2273                         if (str == null)
2274                                 throw new ArgumentNullException ("str");
2275
2276                         return InternalIntern (str);
2277                 }
2278
2279                 public static string IsInterned (string str)
2280                 {
2281                         if (str == null)
2282                                 throw new ArgumentNullException ("str");
2283
2284                         return InternalIsInterned (str);
2285                 }
2286         
2287                 public static string Join (string separator, string [] value)
2288                 {
2289                         if (value == null)
2290                                 throw new ArgumentNullException ("value");
2291                         if (separator == null)
2292                                 separator = String.Empty;
2293
2294                         return JoinUnchecked (separator, value, 0, value.Length);
2295                 }
2296
2297                 public static string Join (string separator, string[] value, int startIndex, int count)
2298                 {
2299                         if (value == null)
2300                                 throw new ArgumentNullException ("value");
2301                         if (startIndex < 0)
2302                                 throw new ArgumentOutOfRangeException ("startIndex", "< 0");
2303                         if (count < 0)
2304                                 throw new ArgumentOutOfRangeException ("count", "< 0");
2305                         if (startIndex > value.Length - count)
2306                                 throw new ArgumentOutOfRangeException ("startIndex", "startIndex + count > value.length");
2307
2308                         if (startIndex == value.Length)
2309                                 return String.Empty;
2310                         if (separator == null)
2311                                 separator = String.Empty;
2312
2313                         return JoinUnchecked (separator, value, startIndex, count);
2314                 }
2315
2316                 private static unsafe string JoinUnchecked (string separator, string[] value, int startIndex, int count)
2317                 {
2318                         // Unchecked parameters
2319                         // startIndex, count must be >= 0; startIndex + count must be <= value.length
2320                         // separator and value must not be null
2321
2322                         int length = 0;
2323                         int maxIndex = startIndex + count;
2324                         // Precount the number of characters that the resulting string will have
2325                         for (int i = startIndex; i < maxIndex; i++) {
2326                                 String s = value[i];
2327                                 if (s != null)
2328                                         length += s.length;
2329                         }
2330                         length += separator.length * (count - 1);
2331                         if (length <= 0)
2332                                 return String.Empty;
2333
2334                         String tmp = InternalAllocateStr (length);
2335
2336                         maxIndex--;
2337                         fixed (char* dest = tmp, sepsrc = separator) {
2338                                 // Copy each string from value except the last one and add a separator for each
2339                                 int pos = 0;
2340                                 for (int i = startIndex; i < maxIndex; i++) {
2341                                         String source = value[i];
2342                                         if (source != null) {
2343                                                 if (source.Length > 0) {
2344                                                         fixed (char* src = source)
2345                                                                 CharCopy (dest + pos, src, source.Length);
2346                                                         pos += source.Length;
2347                                                 }
2348                                         }
2349                                         if (separator.Length > 0) {
2350                                                 CharCopy (dest + pos, sepsrc, separator.Length);
2351                                                 pos += separator.Length;
2352                                         }
2353                                 }
2354                                 // Append last string that does not get an additional separator
2355                                 String sourceLast = value[maxIndex];
2356                                 if (sourceLast != null) {
2357                                         if (sourceLast.Length > 0) {
2358                                                 fixed (char* src = sourceLast)
2359                                                         CharCopy (dest + pos, src, sourceLast.Length);
2360                                         }
2361                                 }
2362                         }
2363                         return tmp;
2364                 }
2365
2366                 bool IConvertible.ToBoolean (IFormatProvider provider)
2367                 {
2368                         return Convert.ToBoolean (this, provider);
2369                 }
2370
2371                 byte IConvertible.ToByte (IFormatProvider provider)
2372                 {
2373                         return Convert.ToByte (this, provider);
2374                 }
2375
2376                 char IConvertible.ToChar (IFormatProvider provider)
2377                 {
2378                         return Convert.ToChar (this, provider);
2379                 }
2380
2381                 DateTime IConvertible.ToDateTime (IFormatProvider provider)
2382                 {
2383                         return Convert.ToDateTime (this, provider);
2384                 }
2385
2386                 decimal IConvertible.ToDecimal (IFormatProvider provider)
2387                 {
2388                         return Convert.ToDecimal (this, provider);
2389                 }
2390
2391                 double IConvertible.ToDouble (IFormatProvider provider)
2392                 {
2393                         return Convert.ToDouble (this, provider);
2394                 }
2395
2396                 short IConvertible.ToInt16 (IFormatProvider provider)
2397                 {
2398                         return Convert.ToInt16 (this, provider);
2399                 }
2400
2401                 int IConvertible.ToInt32 (IFormatProvider provider)
2402                 {
2403                         return Convert.ToInt32 (this, provider);
2404                 }
2405
2406                 long IConvertible.ToInt64 (IFormatProvider provider)
2407                 {
2408                         return Convert.ToInt64 (this, provider);
2409                 }
2410
2411 #if ONLY_1_1
2412 #pragma warning disable 3019
2413                 [CLSCompliant (false)]
2414 #endif
2415                 sbyte IConvertible.ToSByte (IFormatProvider provider)
2416                 {
2417                         return Convert.ToSByte (this, provider);
2418                 }
2419 #if ONLY_1_1
2420 #pragma warning restore 3019
2421 #endif
2422
2423                 float IConvertible.ToSingle (IFormatProvider provider)
2424                 {
2425                         return Convert.ToSingle (this, provider);
2426                 }
2427
2428                 object IConvertible.ToType (Type type, IFormatProvider provider)
2429                 {
2430                         return Convert.ToType (this, type, provider, false);
2431                 }
2432
2433 #if ONLY_1_1
2434 #pragma warning disable 3019
2435                 [CLSCompliant (false)]
2436 #endif
2437                 ushort IConvertible.ToUInt16 (IFormatProvider provider)
2438                 {
2439                         return Convert.ToUInt16 (this, provider);
2440                 }
2441 #if ONLY_1_1
2442 #pragma warning restore 3019
2443 #endif
2444
2445 #if ONLY_1_1
2446 #pragma warning disable 3019
2447                 [CLSCompliant (false)]
2448 #endif
2449                 uint IConvertible.ToUInt32 (IFormatProvider provider)
2450                 {
2451                         return Convert.ToUInt32 (this, provider);
2452                 }
2453 #if ONLY_1_1
2454 #pragma warning restore 3019
2455 #endif
2456
2457 #if ONLY_1_1
2458 #pragma warning disable 3019
2459                 [CLSCompliant (false)]
2460 #endif
2461                 ulong IConvertible.ToUInt64 (IFormatProvider provider)
2462                 {
2463                         return Convert.ToUInt64 (this, provider);
2464                 }
2465 #if ONLY_1_1
2466 #pragma warning restore 3019
2467 #endif
2468
2469                 public int Length {
2470                         get {
2471                                 return length;
2472                         }
2473                 }
2474
2475                 public CharEnumerator GetEnumerator ()
2476                 {
2477                         return new CharEnumerator (this);
2478                 }
2479
2480 #if NET_2_0
2481                 IEnumerator<char> IEnumerable<char>.GetEnumerator ()
2482                 {
2483                         return new CharEnumerator (this);
2484                 }
2485 #endif
2486
2487                 IEnumerator IEnumerable.GetEnumerator ()
2488                 {
2489                         return new CharEnumerator (this);
2490                 }
2491
2492                 private static void ParseFormatSpecifier (string str, ref int ptr, out int n, out int width,
2493                                                           out bool left_align, out string format)
2494                 {
2495                         // parses format specifier of form:
2496                         //   N,[\ +[-]M][:F]}
2497                         //
2498                         // where:
2499
2500                         try {
2501                                 // N = argument number (non-negative integer)
2502
2503                                 n = ParseDecimal (str, ref ptr);
2504                                 if (n < 0)
2505                                         throw new FormatException ("Input string was not in a correct format.");
2506
2507                                 // M = width (non-negative integer)
2508
2509                                 if (str[ptr] == ',') {
2510                                         // White space between ',' and number or sign.
2511                                         ++ptr;
2512                                         while (Char.IsWhiteSpace (str [ptr]))
2513                                                 ++ptr;
2514                                         int start = ptr;
2515
2516                                         format = str.Substring (start, ptr - start);
2517
2518                                         left_align = (str [ptr] == '-');
2519                                         if (left_align)
2520                                                 ++ ptr;
2521
2522                                         width = ParseDecimal (str, ref ptr);
2523                                         if (width < 0)
2524                                                 throw new FormatException ("Input string was not in a correct format.");
2525                                 }
2526                                 else {
2527                                         width = 0;
2528                                         left_align = false;
2529                                         format = String.Empty;
2530                                 }
2531
2532                                 // F = argument format (string)
2533
2534                                 if (str[ptr] == ':') {
2535                                         int start = ++ ptr;
2536                                         while (str[ptr] != '}')
2537                                                 ++ ptr;
2538
2539                                         format += str.Substring (start, ptr - start);
2540                                 }
2541                                 else
2542                                         format = null;
2543
2544                                 if (str[ptr ++] != '}')
2545                                         throw new FormatException ("Input string was not in a correct format.");
2546                         }
2547                         catch (IndexOutOfRangeException) {
2548                                 throw new FormatException ("Input string was not in a correct format.");
2549                         }
2550                 }
2551
2552                 private static int ParseDecimal (string str, ref int ptr)
2553                 {
2554                         int p = ptr;
2555                         int n = 0;
2556                         while (true) {
2557                                 char c = str[p];
2558                                 if (c < '0' || '9' < c)
2559                                         break;
2560
2561                                 n = n * 10 + c - '0';
2562                                 ++ p;
2563                         }
2564
2565                         if (p == ptr)
2566                                 return -1;
2567
2568                         ptr = p;
2569                         return n;
2570                 }
2571
2572                 internal unsafe void InternalSetChar (int idx, char val)
2573                 {
2574                         if ((uint) idx >= (uint) Length)
2575                                 throw new ArgumentOutOfRangeException ("idx");
2576
2577                         fixed (char * pStr = &start_char) 
2578                         {
2579                                 pStr [idx] = val;
2580                         }
2581                 }
2582
2583                 internal unsafe void InternalSetLength (int newLength)
2584                 {
2585                         if (newLength > length)
2586                                 throw new ArgumentOutOfRangeException ("newLength", "newLength as to be <= length");
2587
2588                         // zero terminate, we can pass string objects directly via pinvoke
2589                         // we also zero the rest of the string, since the new GC needs to be
2590                         // able to handle the changing size (it will skip the 0 bytes).
2591                         fixed (char * pStr = &start_char) {
2592                                 char *p = pStr + newLength;
2593                                 char *end = pStr + length;
2594                                 while (p < end) {
2595                                         p [0] = '\0';
2596                                         p++;
2597                                 }
2598                         }
2599                         length = newLength;
2600                 }
2601
2602 #if NET_2_0
2603                 [ReliabilityContractAttribute (Consistency.WillNotCorruptState, Cer.MayFail)]
2604 #endif
2605                 // When modifying it, GetCaseInsensitiveHashCode() should be modified as well.
2606                 public unsafe override int GetHashCode ()
2607                 {
2608                         fixed (char * c = this) {
2609                                 char * cc = c;
2610                                 char * end = cc + length - 1;
2611                                 int h = 0;
2612                                 for (;cc < end; cc += 2) {
2613                                         h = (h << 5) - h + *cc;
2614                                         h = (h << 5) - h + cc [1];
2615                                 }
2616                                 ++end;
2617                                 if (cc < end)
2618                                         h = (h << 5) - h + *cc;
2619                                 return h;
2620                         }
2621                 }
2622
2623                 internal unsafe int GetCaseInsensitiveHashCode ()
2624                 {
2625                         fixed (char * c = this) {
2626                                 char * cc = c;
2627                                 char * end = cc + length - 1;
2628                                 int h = 0;
2629                                 for (;cc < end; cc += 2) {
2630                                         h = (h << 5) - h + Char.ToUpperInvariant (*cc);
2631                                         h = (h << 5) - h + Char.ToUpperInvariant (cc [1]);
2632                                 }
2633                                 ++end;
2634                                 if (cc < end)
2635                                         h = (h << 5) - h + Char.ToUpperInvariant (*cc);
2636                                 return h;
2637                         }
2638                 }
2639
2640                 // Certain constructors are redirected to CreateString methods with
2641                 // matching argument list. The this pointer should not be used.
2642 #pragma warning disable 169
2643                 private unsafe String CreateString (sbyte* value)
2644                 {
2645                         if (value == null)
2646                                 return String.Empty;
2647
2648                         byte* bytes = (byte*) value;
2649                         int length = 0;
2650
2651                         try {
2652                                 while (bytes++ [0] != 0)
2653                                         length++;
2654                         } catch (NullReferenceException) {
2655                                 throw new ArgumentOutOfRangeException ("ptr", "Value does not refer to a valid string.");
2656 #if NET_2_0
2657                         } catch (AccessViolationException) {
2658                                 throw new ArgumentOutOfRangeException ("ptr", "Value does not refer to a valid string.");
2659 #endif
2660                         }
2661
2662                         return CreateString (value, 0, length, null);
2663                 }
2664
2665                 private unsafe String CreateString (sbyte* value, int startIndex, int length)
2666                 {
2667                         return CreateString (value, startIndex, length, null);
2668                 }
2669
2670                 private unsafe String CreateString (sbyte* value, int startIndex, int length, Encoding enc)
2671                 {
2672                         if (length < 0)
2673                                 throw new ArgumentOutOfRangeException ("length", "Non-negative number required.");
2674                         if (startIndex < 0)
2675                                 throw new ArgumentOutOfRangeException ("startIndex", "Non-negative number required.");
2676                         if (value + startIndex < value)
2677                                 throw new ArgumentOutOfRangeException ("startIndex", "Value, startIndex and length do not refer to a valid string.");
2678
2679                         bool isDefaultEncoding;
2680
2681                         if (isDefaultEncoding = (enc == null)) {
2682 #if NET_2_0
2683                                 if (value == null)
2684                                         throw new ArgumentNullException ("value");
2685                                 if (length == 0)
2686 #else
2687                                 if (value == null || length == 0)
2688 #endif
2689                                         return String.Empty;
2690
2691                                 enc = Encoding.Default;
2692                         }
2693
2694                         byte [] bytes = new byte [length];
2695
2696                         if (length != 0)
2697                                 fixed (byte* bytePtr = bytes)
2698                                         try {
2699                                                 memcpy (bytePtr, (byte*) (value + startIndex), length);
2700                                         } catch (NullReferenceException) {
2701 #if !NET_2_0
2702                                                 if (!isDefaultEncoding)
2703                                                         throw;
2704 #endif
2705
2706                                                 throw new ArgumentOutOfRangeException ("ptr", "Value, startIndex and length do not refer to a valid string.");
2707 #if NET_2_0
2708                                         } catch (AccessViolationException) {
2709                                                 if (!isDefaultEncoding)
2710                                                         throw;
2711
2712                                                 throw new ArgumentOutOfRangeException ("value", "Value, startIndex and length do not refer to a valid string.");
2713 #endif
2714                                         }
2715
2716                         // GetString () is called even when length == 0
2717                         return enc.GetString (bytes);
2718                 }
2719
2720                 unsafe string CreateString (char *value)
2721                 {
2722                         if (value == null)
2723                                 return string.Empty;
2724                         char *p = value;
2725                         int i = 0;
2726                         while (*p != 0) {
2727                                 ++i;
2728                                 ++p;
2729                         }
2730                         string result = InternalAllocateStr (i);
2731
2732                         if (i != 0) {
2733                                 fixed (char *dest = result) {
2734                                         CharCopy (dest, value, i);
2735                                 }
2736                         }
2737                         return result;
2738                 }
2739
2740                 unsafe string CreateString (char *value, int startIndex, int length)
2741                 {
2742                         if (length == 0)
2743                                 return string.Empty;
2744                         if (value == null)
2745                                 throw new ArgumentNullException ("value");
2746                         if (startIndex < 0)
2747                                 throw new ArgumentOutOfRangeException ("startIndex");
2748                         if (length < 0)
2749                                 throw new ArgumentOutOfRangeException ("length");
2750
2751                         string result = InternalAllocateStr (length);
2752
2753                         fixed (char *dest = result) {
2754                                 CharCopy (dest, value + startIndex, length);
2755                         }
2756                         return result;
2757                 }
2758
2759                 unsafe string CreateString (char [] val, int startIndex, int length)
2760                 {
2761                         if (val == null)
2762                                 throw new ArgumentNullException ("value");
2763                         if (startIndex < 0)
2764                                 throw new ArgumentOutOfRangeException ("startIndex", "Cannot be negative.");
2765                         if (length < 0)
2766                                 throw new ArgumentOutOfRangeException ("length", "Cannot be negative.");
2767                         if (startIndex > val.Length - length)
2768                                 throw new ArgumentOutOfRangeException ("startIndex", "Cannot be negative, and should be less than length of string.");
2769                         if (length == 0)
2770                                 return string.Empty;
2771
2772                         string result = InternalAllocateStr (length);
2773
2774                         fixed (char *dest = result, src = val) {
2775                                 CharCopy (dest, src + startIndex, length);
2776                         }
2777                         return result;
2778                 }
2779
2780                 unsafe string CreateString (char [] val)
2781                 {
2782                         if (val == null)
2783                                 return string.Empty;
2784                         if (val.Length == 0)
2785                                 return string.Empty;
2786                         string result = InternalAllocateStr (val.Length);
2787
2788                         fixed (char *dest = result, src = val) {
2789                                 CharCopy (dest, src, val.Length);
2790                         }
2791                         return result;
2792                 }
2793
2794                 unsafe string CreateString (char c, int count)
2795                 {
2796                         if (count < 0)
2797                                 throw new ArgumentOutOfRangeException ("count");
2798                         if (count == 0)
2799                                 return string.Empty;
2800                         string result = InternalAllocateStr (count);
2801                         fixed (char *dest = result) {
2802                                 char *p = dest;
2803                                 char *end = p + count;
2804                                 while (p < end) {
2805                                         *p = c;
2806                                         p++;
2807                                 }
2808                         }
2809                         return result;
2810                 }
2811 #pragma warning restore 169
2812
2813                 /* helpers used by the runtime as well as above or eslewhere in corlib */
2814                 internal static unsafe void memset (byte *dest, int val, int len)
2815                 {
2816                         if (len < 8) {
2817                                 while (len != 0) {
2818                                         *dest = (byte)val;
2819                                         ++dest;
2820                                         --len;
2821                                 }
2822                                 return;
2823                         }
2824                         if (val != 0) {
2825                                 val = val | (val << 8);
2826                                 val = val | (val << 16);
2827                         }
2828                         // align to 4
2829                         int rest = (int)dest & 3;
2830                         if (rest != 0) {
2831                                 rest = 4 - rest;
2832                                 len -= rest;
2833                                 do {
2834                                         *dest = (byte)val;
2835                                         ++dest;
2836                                         --rest;
2837                                 } while (rest != 0);
2838                         }
2839                         while (len >= 16) {
2840                                 ((int*)dest) [0] = val;
2841                                 ((int*)dest) [1] = val;
2842                                 ((int*)dest) [2] = val;
2843                                 ((int*)dest) [3] = val;
2844                                 dest += 16;
2845                                 len -= 16;
2846                         }
2847                         while (len >= 4) {
2848                                 ((int*)dest) [0] = val;
2849                                 dest += 4;
2850                                 len -= 4;
2851                         }
2852                         // tail bytes
2853                         while (len > 0) {
2854                                 *dest = (byte)val;
2855                                 dest++;
2856                                 len--;
2857                         }
2858                 }
2859
2860                 static unsafe void memcpy4 (byte *dest, byte *src, int size) {
2861                         /*while (size >= 32) {
2862                                 // using long is better than int and slower than double
2863                                 // FIXME: enable this only on correct alignment or on platforms
2864                                 // that can tolerate unaligned reads/writes of doubles
2865                                 ((double*)dest) [0] = ((double*)src) [0];
2866                                 ((double*)dest) [1] = ((double*)src) [1];
2867                                 ((double*)dest) [2] = ((double*)src) [2];
2868                                 ((double*)dest) [3] = ((double*)src) [3];
2869                                 dest += 32;
2870                                 src += 32;
2871                                 size -= 32;
2872                         }*/
2873                         while (size >= 16) {
2874                                 ((int*)dest) [0] = ((int*)src) [0];
2875                                 ((int*)dest) [1] = ((int*)src) [1];
2876                                 ((int*)dest) [2] = ((int*)src) [2];
2877                                 ((int*)dest) [3] = ((int*)src) [3];
2878                                 dest += 16;
2879                                 src += 16;
2880                                 size -= 16;
2881                         }
2882                         while (size >= 4) {
2883                                 ((int*)dest) [0] = ((int*)src) [0];
2884                                 dest += 4;
2885                                 src += 4;
2886                                 size -= 4;
2887                         }
2888                         while (size > 0) {
2889                                 ((byte*)dest) [0] = ((byte*)src) [0];
2890                                 dest += 1;
2891                                 src += 1;
2892                                 --size;
2893                         }
2894                 }
2895                 static unsafe void memcpy2 (byte *dest, byte *src, int size) {
2896                         while (size >= 8) {
2897                                 ((short*)dest) [0] = ((short*)src) [0];
2898                                 ((short*)dest) [1] = ((short*)src) [1];
2899                                 ((short*)dest) [2] = ((short*)src) [2];
2900                                 ((short*)dest) [3] = ((short*)src) [3];
2901                                 dest += 8;
2902                                 src += 8;
2903                                 size -= 8;
2904                         }
2905                         while (size >= 2) {
2906                                 ((short*)dest) [0] = ((short*)src) [0];
2907                                 dest += 2;
2908                                 src += 2;
2909                                 size -= 2;
2910                         }
2911                         if (size > 0)
2912                                 ((byte*)dest) [0] = ((byte*)src) [0];
2913                 }
2914                 static unsafe void memcpy1 (byte *dest, byte *src, int size) {
2915                         while (size >= 8) {
2916                                 ((byte*)dest) [0] = ((byte*)src) [0];
2917                                 ((byte*)dest) [1] = ((byte*)src) [1];
2918                                 ((byte*)dest) [2] = ((byte*)src) [2];
2919                                 ((byte*)dest) [3] = ((byte*)src) [3];
2920                                 ((byte*)dest) [4] = ((byte*)src) [4];
2921                                 ((byte*)dest) [5] = ((byte*)src) [5];
2922                                 ((byte*)dest) [6] = ((byte*)src) [6];
2923                                 ((byte*)dest) [7] = ((byte*)src) [7];
2924                                 dest += 8;
2925                                 src += 8;
2926                                 size -= 8;
2927                         }
2928                         while (size >= 2) {
2929                                 ((byte*)dest) [0] = ((byte*)src) [0];
2930                                 ((byte*)dest) [1] = ((byte*)src) [1];
2931                                 dest += 2;
2932                                 src += 2;
2933                                 size -= 2;
2934                         }
2935                         if (size > 0)
2936                                 ((byte*)dest) [0] = ((byte*)src) [0];
2937                 }
2938
2939                 internal static unsafe void memcpy (byte *dest, byte *src, int size) {
2940                         // FIXME: if pointers are not aligned, try to align them
2941                         // so a faster routine can be used. Handle the case where
2942                         // the pointers can't be reduced to have the same alignment
2943                         // (just ignore the issue on x86?)
2944                         if ((((int)dest | (int)src) & 3) != 0) {
2945                                 if (((int)dest & 1) != 0 && ((int)src & 1) != 0 && size >= 1) {
2946                                         dest [0] = src [0];
2947                                         ++dest;
2948                                         ++src;
2949                                         --size;
2950                                 }
2951                                 if (((int)dest & 2) != 0 && ((int)src & 2) != 0 && size >= 2) {
2952                                         ((short*)dest) [0] = ((short*)src) [0];
2953                                         dest += 2;
2954                                         src += 2;
2955                                         size -= 2;
2956                                 }
2957                                 if ((((int)dest | (int)src) & 1) != 0) {
2958                                         memcpy1 (dest, src, size);
2959                                         return;
2960                                 }
2961                                 if ((((int)dest | (int)src) & 2) != 0) {
2962                                         memcpy2 (dest, src, size);
2963                                         return;
2964                                 }
2965                         }
2966                         memcpy4 (dest, src, size);
2967                 }
2968
2969                 internal static unsafe void CharCopy (char *dest, char *src, int count) {
2970                         // Same rules as for memcpy, but with the premise that 
2971                         // chars can only be aligned to even addresses if their
2972                         // enclosing types are correctly aligned
2973                         if ((((int)(byte*)dest | (int)(byte*)src) & 3) != 0) {
2974                                 if (((int)(byte*)dest & 2) != 0 && ((int)(byte*)src & 2) != 0 && count > 0) {
2975                                         ((short*)dest) [0] = ((short*)src) [0];
2976                                         dest++;
2977                                         src++;
2978                                         count--;
2979                                 }
2980                                 if ((((int)(byte*)dest | (int)(byte*)src) & 2) != 0) {
2981                                         memcpy2 ((byte*)dest, (byte*)src, count * 2);
2982                                         return;
2983                                 }
2984                         }
2985                         memcpy4 ((byte*)dest, (byte*)src, count * 2);
2986                 }
2987
2988                 internal static unsafe void CharCopyReverse (char *dest, char *src, int count)
2989                 {
2990                         dest += count;
2991                         src += count;
2992                         for (int i = count; i > 0; i--) {
2993                                 dest--;
2994                                 src--;
2995                                 *dest = *src;
2996                         }       
2997                 }
2998
2999                 internal static unsafe void CharCopy (String target, int targetIndex, String source, int sourceIndex, int count)
3000                 {
3001                         fixed (char* dest = target, src = source)
3002                                 CharCopy (dest + targetIndex, src + sourceIndex, count);
3003                 }
3004
3005                 internal static unsafe void CharCopy (String target, int targetIndex, Char[] source, int sourceIndex, int count)
3006                 {
3007                         fixed (char* dest = target, src = source)
3008                                 CharCopy (dest + targetIndex, src + sourceIndex, count);
3009                 }
3010
3011                 // Use this method if you cannot block copy from left to right (e.g. because you are coping within the same string)
3012                 internal static unsafe void CharCopyReverse (String target, int targetIndex, String source, int sourceIndex, int count)
3013                 {
3014                         fixed (char* dest = target, src = source)
3015                                 CharCopyReverse (dest + targetIndex, src + sourceIndex, count);
3016                 }
3017
3018                 [CLSCompliant (false), MethodImplAttribute (MethodImplOptions.InternalCall)]
3019                 unsafe public extern String (char *value);
3020
3021                 [CLSCompliant (false), MethodImplAttribute (MethodImplOptions.InternalCall)]
3022                 unsafe public extern String (char *value, int startIndex, int length);
3023
3024                 [CLSCompliant (false), MethodImplAttribute (MethodImplOptions.InternalCall)]
3025                 unsafe public extern String (sbyte *value);
3026
3027                 [CLSCompliant (false), MethodImplAttribute (MethodImplOptions.InternalCall)]
3028                 unsafe public extern String (sbyte *value, int startIndex, int length);
3029
3030                 [CLSCompliant (false), MethodImplAttribute (MethodImplOptions.InternalCall)]
3031                 unsafe public extern String (sbyte *value, int startIndex, int length, Encoding enc);
3032
3033                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
3034                 public extern String (char [] value, int startIndex, int length);
3035
3036                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
3037                 public extern String (char [] value);
3038
3039                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
3040                 public extern String (char c, int count);
3041
3042 //              [MethodImplAttribute (MethodImplOptions.InternalCall)]
3043 //              private extern static string InternalJoin (string separator, string[] value, int sIndex, int count);
3044
3045 //              [MethodImplAttribute (MethodImplOptions.InternalCall)]
3046 //              private extern String InternalReplace (String oldValue, string newValue, CompareInfo comp);
3047
3048 //              [MethodImplAttribute (MethodImplOptions.InternalCall)]
3049 //              private extern void InternalCopyTo (int sIndex, char[] dest, int destIndex, int count);
3050
3051                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
3052                 private extern String[] InternalSplit (char[] separator, int count, int options);
3053
3054 //              [MethodImplAttribute (MethodImplOptions.InternalCall)]
3055 //              private extern String InternalTrim (char[] chars, int typ);
3056
3057 //              [MethodImplAttribute (MethodImplOptions.InternalCall)]
3058 //              private extern int InternalLastIndexOfAny (char [] anyOf, int sIndex, int count);
3059
3060 //              [MethodImplAttribute (MethodImplOptions.InternalCall)]
3061 //              private extern String InternalPad (int width, char chr, bool right);
3062
3063                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
3064                 internal extern static String InternalAllocateStr (int length);
3065
3066                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
3067                 internal extern static void InternalStrcpy (String dest, int destPos, String src);
3068
3069                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
3070                 internal extern static void InternalStrcpy (String dest, int destPos, char[] chars);
3071
3072                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
3073                 internal extern static void InternalStrcpy (String dest, int destPos, String src, int sPos, int count);
3074
3075                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
3076                 internal extern static void InternalStrcpy (String dest, int destPos, char[] chars, int sPos, int count);
3077
3078                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
3079                 private extern static string InternalIntern (string str);
3080
3081                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
3082                 private extern static string InternalIsInterned (string str);
3083         }
3084 }