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