2008-07-17 Marek Safar <marek.safar@gmail.com>
[mono.git] / mcs / class / corlib / System / String.cs
1 //
2 // System.String.cs
3 //
4 // Authors:
5 //   Patrik Torstensson
6 //   Jeffrey Stedfast (fejj@ximian.com)
7 //   Dan Lewis (dihlewis@yahoo.co.uk)
8 //   Sebastien Pouliot  <sebastien@ximian.com>
9 //   Marek Safar (marek.safar@seznam.cz)
10 //   Andreas Nahr (Classdevelopment@A-SoftTech.com)
11 //
12 // (C) 2001 Ximian, Inc.  http://www.ximian.com
13 // Copyright (C) 2004-2005 Novell (http://www.novell.com)
14 //
15 // Permission is hereby granted, free of charge, to any person obtaining
16 // a copy of this software and associated documentation files (the
17 // "Software"), to deal in the Software without restriction, including
18 // without limitation the rights to use, copy, modify, merge, publish,
19 // distribute, sublicense, and/or sell copies of the Software, and to
20 // permit persons to whom the Software is furnished to do so, subject to
21 // the following conditions:
22 // 
23 // The above copyright notice and this permission notice shall be
24 // included in all copies or substantial portions of the Software.
25 // 
26 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
27 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
28 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
29 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
30 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
31 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
32 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
33 //
34 //
35 //
36 // This class contains all implementation for culture-insensitive methods.
37 // Culture-sensitive methods are implemented in the System.Globalization or
38 // Mono.Globalization namespace.
39 //
40 // Ensure that argument checks on methods don't overflow
41 //
42
43 using System.Text;
44 using System.Collections;
45 using System.Globalization;
46 using System.Runtime.CompilerServices;
47
48 #if NET_2_0
49 using System.Collections.Generic;
50 using System.Runtime.ConstrainedExecution;
51 using System.Runtime.InteropServices;
52 using Mono.Globalization.Unicode;
53 #endif
54
55 namespace System
56 {
57         [Serializable]
58 #if NET_2_0
59         [ComVisible (true)]
60         public sealed class String : IConvertible, ICloneable, IEnumerable, IComparable, IComparable<String>, IEquatable <String>, IEnumerable<char>
61 #else
62         public sealed class String : IConvertible, ICloneable, IEnumerable, IComparable
63 #endif
64         {
65                 [NonSerialized] private int length;
66                 [NonSerialized] private char start_char;
67
68                 public static readonly String Empty = "";
69
70                 public static unsafe bool Equals (string a, string b)
71                 {
72                         if ((a as object) == (b as object))
73                                 return true;
74
75                         if (a == null || b == null)
76                                 return false;
77
78                         int len = a.length;
79
80                         if (len != b.length)
81                                 return false;
82
83                         fixed (char* s1 = &a.start_char, s2 = &b.start_char) {
84                                 char* s1_ptr = s1;
85                                 char* s2_ptr = s2;
86
87                                 while (len >= 8) {
88                                         if (((int*)s1_ptr)[0] != ((int*)s2_ptr)[0] ||
89                                                 ((int*)s1_ptr)[1] != ((int*)s2_ptr)[1] ||
90                                                 ((int*)s1_ptr)[2] != ((int*)s2_ptr)[2] ||
91                                                 ((int*)s1_ptr)[3] != ((int*)s2_ptr)[3])
92                                                 return false;
93
94                                         s1_ptr += 8;
95                                         s2_ptr += 8;
96                                         len -= 8;
97                                 }
98
99                                 if (len >= 4) {
100                                         if (((int*)s1_ptr)[0] != ((int*)s2_ptr)[0] ||
101                                                 ((int*)s1_ptr)[1] != ((int*)s2_ptr)[1])
102                                                 return false;
103
104                                         s1_ptr += 4;
105                                         s2_ptr += 4;
106                                         len -= 4;
107                                 }
108
109                                 if (len > 1) {
110                                         if (((int*)s1_ptr)[0] != ((int*)s2_ptr)[0])
111                                                 return false;
112
113                                         s1_ptr += 2;
114                                         s2_ptr += 2;
115                                         len -= 2;
116                                 }
117
118                                 return len == 0 || *s1_ptr == *s2_ptr;
119                         }
120                 }
121
122                 public static bool operator == (String a, String b)
123                 {
124                         return Equals (a, b);
125                 }
126
127                 public static bool operator != (String a, String b)
128                 {
129                         return !Equals (a, b);
130                 }
131
132 #if NET_2_0
133                 [ReliabilityContractAttribute (Consistency.WillNotCorruptState, Cer.MayFail)]
134 #endif
135                 public override bool Equals (Object obj)
136                 {
137                         return Equals (this, obj as String);
138                 }
139
140 #if NET_2_0
141                 [ReliabilityContractAttribute (Consistency.WillNotCorruptState, Cer.MayFail)]
142 #endif
143                 public bool Equals (String value)
144                 {
145                         return Equals (this, value);
146                 }
147
148                 [IndexerName ("Chars")]
149                 public unsafe char this [int index] {
150                         get {
151                                 if (index < 0 || index >= length)
152                                         throw new IndexOutOfRangeException ();
153                                 fixed (char* c = &start_char)
154                                         return c[index];
155                         }
156                 }
157
158                 public Object Clone ()
159                 {
160                         return this;
161                 }
162
163                 public TypeCode GetTypeCode ()
164                 {
165                         return TypeCode.String;
166                 }
167
168                 public unsafe void CopyTo (int sourceIndex, char[] destination, int destinationIndex, int count)
169                 {
170                         if (destination == null)
171                                 throw new ArgumentNullException ("destination");
172                         if (sourceIndex < 0)
173                                 throw new ArgumentOutOfRangeException ("sourceIndex", "Cannot be negative");
174                         if (destinationIndex < 0)
175                                 throw new ArgumentOutOfRangeException ("destinationIndex", "Cannot be negative.");
176                         if (count < 0)
177                                 throw new ArgumentOutOfRangeException ("count", "Cannot be negative.");
178                         if (sourceIndex > Length - count)
179                                 throw new ArgumentOutOfRangeException ("sourceIndex", "sourceIndex + count > Length");
180                         if (destinationIndex > destination.Length - count)
181                                 throw new ArgumentOutOfRangeException ("destinationIndex", "destinationIndex + count > destination.Length");
182
183                         fixed (char* dest = destination, src = this)
184                                 CharCopy (dest + destinationIndex, src + sourceIndex, count);
185                 }
186
187                 public unsafe char[] ToCharArray ()
188                 {
189                         char[] tmp = new char [length];
190                         fixed (char* dest = tmp, src = this)
191                                 CharCopy (dest, src, length);
192                         return tmp;
193                 }
194
195                 public unsafe char[] ToCharArray (int startIndex, int length)
196                 {
197                         if (startIndex < 0)
198                                 throw new ArgumentOutOfRangeException ("startIndex", "< 0"); 
199                         if (length < 0)
200                                 throw new ArgumentOutOfRangeException ("length", "< 0"); 
201                         if (startIndex > this.length - length)
202                                 throw new ArgumentOutOfRangeException ("startIndex", "Must be greater than the length of the string.");
203
204                         char[] tmp = new char [length];
205                         fixed (char* dest = tmp, src = this)
206                                 CharCopy (dest, src + startIndex, length);
207                         return tmp;
208                 }
209
210                 public String [] Split (params char [] separator)
211                 {
212                         return Split (separator, Int32.MaxValue);
213                 }
214
215                 public String[] Split (char[] separator, int count)
216                 {
217                         if (separator == null || separator.Length == 0)
218                                 separator = WhiteChars;
219
220                         if (count < 0)
221                                 throw new ArgumentOutOfRangeException ("count");
222
223                         if (count == 0) 
224                                 return new String[0];
225
226                         if (count == 1) 
227                                 return new String[1] { this };
228
229                         return InternalSplit (separator, count, 0);
230                 }
231
232 #if NET_2_0
233                 [ComVisible (false)]
234                 [MonoDocumentationNote ("code should be moved to managed")]
235                 public String[] Split (char[] separator, int count, StringSplitOptions options)
236                 {
237                         if (separator == null || separator.Length == 0)
238                                 return Split (WhiteChars, count, options);
239
240                         if (count < 0)
241                                 throw new ArgumentOutOfRangeException ("count", "Count cannot be less than zero.");
242                         if ((options != StringSplitOptions.None) && (options != StringSplitOptions.RemoveEmptyEntries))
243                                 throw new ArgumentException ("Illegal enum value: " + options + ".");
244
245                         if (count == 0)
246                                 return new string [0];
247
248                         return InternalSplit (separator, count, (int)options);
249                 }
250
251                 [ComVisible (false)]
252                 public String[] Split (string[] separator, int count, StringSplitOptions options)
253                 {
254                         if (separator == null || separator.Length == 0)
255                                 return Split (WhiteChars, count, options);
256
257                         if (count < 0)
258                                 throw new ArgumentOutOfRangeException ("count", "Count cannot be less than zero.");
259                         if ((options != StringSplitOptions.None) && (options != StringSplitOptions.RemoveEmptyEntries))
260                                 throw new ArgumentException ("Illegal enum value: " + options + ".");
261
262                         bool removeEmpty = (options & StringSplitOptions.RemoveEmptyEntries) == StringSplitOptions.RemoveEmptyEntries;
263
264                         if (count == 0 || (this == String.Empty && removeEmpty))
265                                 return new String [0];
266
267                         ArrayList arr = new ArrayList ();
268
269                         int pos = 0;
270                         int matchCount = 0;
271                         while (pos < this.Length) {
272                                 int matchIndex = -1;
273                                 int matchPos = Int32.MaxValue;
274
275                                 // Find the first position where any of the separators matches
276                                 for (int i = 0; i < separator.Length; ++i) {
277                                         string sep = separator [i];
278                                         if (sep == null || sep == String.Empty)
279                                                 continue;
280
281                                         int match = IndexOf (sep, pos);
282                                         if (match > -1 && match < matchPos) {
283                                                 matchIndex = i;
284                                                 matchPos = match;
285                                         }
286                                 }
287
288                                 if (matchIndex == -1)
289                                         break;
290
291                                 if (!(matchPos == pos && removeEmpty))
292                                         arr.Add (this.Substring (pos, matchPos - pos));
293
294                                 pos = matchPos + separator [matchIndex].Length;
295
296                                 matchCount ++;
297
298                                 if (matchCount == count - 1)
299                                         break;
300                         }
301
302                         if (matchCount == 0)
303                                 return new String [] { this };
304                         else {
305                                 if (removeEmpty && pos == this.Length) {
306                                         String[] res = new String [arr.Count];
307                                         arr.CopyTo (0, res, 0, arr.Count);
308
309                                         return res;
310                                 }
311                                 else {
312                                         String[] res = new String [arr.Count + 1];
313                                         arr.CopyTo (0, res, 0, arr.Count);
314                                         res [arr.Count] = this.Substring (pos);
315
316                                         return res;
317                                 }
318                         }
319                 }
320
321                 [ComVisible (false)]
322                 public String[] Split (char[] separator, StringSplitOptions options)
323                 {
324                         return Split (separator, Int32.MaxValue, options);
325                 }
326
327                 [ComVisible (false)]
328                 public String[] Split (String[] separator, StringSplitOptions options)
329                 {
330                         return Split (separator, Int32.MaxValue, options);
331                 }
332 #endif
333
334                 public String Substring (int startIndex)
335                 {
336 #if NET_2_0
337                         if (startIndex == 0)
338                                 return this;
339                         if (startIndex < 0 || startIndex > this.length)
340                                 throw new ArgumentOutOfRangeException ("startIndex");
341 #else
342                         if (startIndex < 0)
343                                 throw new ArgumentOutOfRangeException ("startIndex", "Cannot be negative.");
344
345                         if (startIndex > this.length)
346                                 throw new ArgumentOutOfRangeException ("length", "Cannot exceed length of string.");
347 #endif
348
349                         return SubstringUnchecked (startIndex, this.length - startIndex);
350                 }
351
352                 public String Substring (int startIndex, int length)
353                 {
354                         if (length < 0)
355                                 throw new ArgumentOutOfRangeException ("length", "Cannot be negative.");
356                         if (startIndex < 0)
357                                 throw new ArgumentOutOfRangeException ("startIndex", "Cannot be negative.");
358 #if NET_2_0
359                         if (startIndex > this.length)
360                                 throw new ArgumentOutOfRangeException ("startIndex", "Cannot exceed length of string.");
361 #endif
362                         if (startIndex > this.length - length)
363                                 throw new ArgumentOutOfRangeException ("length", "startIndex + length > this.length");
364 #if NET_2_0
365                         if (startIndex == 0 && length == this.length)
366                                 return this;
367 #endif
368
369                         return SubstringUnchecked (startIndex, length);
370                 }
371
372                 // This method is used by StringBuilder.ToString() and is expected to
373                 // always create a new string object (or return String.Empty). 
374                 internal unsafe String SubstringUnchecked (int startIndex, int length)
375                 {
376                         if (length == 0)
377                                 return String.Empty;
378
379                         string tmp = InternalAllocateStr (length);
380                         fixed (char* dest = tmp, src = this) {
381                                 CharCopy (dest, src + startIndex, length);
382                         }
383                         return tmp;
384                 }
385
386                 private static readonly char[] WhiteChars = { (char) 0x9, (char) 0xA, (char) 0xB, (char) 0xC, (char) 0xD,
387 #if NET_2_0
388                         (char) 0x85, (char) 0x1680, (char) 0x2028, (char) 0x2029,
389 #endif
390                         (char) 0x20, (char) 0xA0, (char) 0x2000, (char) 0x2001, (char) 0x2002, (char) 0x2003, (char) 0x2004,
391                         (char) 0x2005, (char) 0x2006, (char) 0x2007, (char) 0x2008, (char) 0x2009, (char) 0x200A, (char) 0x200B,
392                         (char) 0x3000, (char) 0xFEFF };
393
394                 public String Trim ()
395                 {
396                         if (length == 0) 
397                                 return String.Empty;
398                         int start = FindNotWhiteSpace (0, length, 1);
399
400                         if (start == length)
401                                 return String.Empty;
402
403                         int end = FindNotWhiteSpace (length - 1, start, -1);
404
405                         int newLength = end - start + 1;
406                         if (newLength == length)
407                                 return this;
408
409                         return SubstringUnchecked (start, newLength);
410                 }
411
412                 public String Trim (params char[] trimChars)
413                 {
414                         if (trimChars == null || trimChars.Length == 0)
415                                 return Trim ();
416
417                         if (length == 0) 
418                                 return String.Empty;
419                         int start = FindNotInTable (0, length, 1, trimChars);
420
421                         if (start == length)
422                                 return String.Empty;
423
424                         int end = FindNotInTable (length - 1, start, -1, trimChars);
425
426                         int newLength = end - start + 1;
427                         if (newLength == length)
428                                 return this;
429
430                         return SubstringUnchecked (start, newLength);
431                 }
432
433                 public String TrimStart (params char[] trimChars)
434                 {
435                         if (length == 0) 
436                                 return String.Empty;
437                         int start;
438                         if (trimChars == null || trimChars.Length == 0)
439                                 start = FindNotWhiteSpace (0, length, 1);
440                         else
441                                 start = FindNotInTable (0, length, 1, trimChars);
442
443                         if (start == 0)
444                                 return this;
445
446                         return SubstringUnchecked (start, length - start);
447                 }
448
449                 public String TrimEnd (params char[] trimChars)
450                 {
451                         if (length == 0) 
452                                 return String.Empty;
453                         int end;
454                         if (trimChars == null || trimChars.Length == 0)
455                                 end = FindNotWhiteSpace (length - 1, -1, -1);
456                         else
457                                 end = FindNotInTable (length - 1, -1, -1, trimChars);
458
459                         end++;
460                         if (end == length)
461                                 return this;
462
463                         return SubstringUnchecked (0, end);
464                 }
465
466                 private int FindNotWhiteSpace (int pos, int target, int change)
467                 {
468                         while (pos != target) {
469                                 char c = this[pos];
470                                 if (c < 0x85) {
471                                         if (c != 0x20) {
472                                                 if (c < 0x9 || c > 0xD)
473                                                         return pos;
474                                         }
475                                 }
476                                 else {
477                                         if (c != 0xA0 && c != 0xFEFF && c != 0x3000) {
478 #if NET_2_0
479                                                 if (c != 0x85 && c != 0x1680 && c != 0x2028 && c != 0x2029)
480 #endif
481                                                         if (c < 0x2000 || c > 0x200B)
482                                                                 return pos;
483                                         }
484                                 }
485                                 pos += change;
486                         }
487                         return pos;
488                 }
489
490                 private unsafe int FindNotInTable (int pos, int target, int change, char[] table)
491                 {
492                         fixed (char* tablePtr = table, thisPtr = this) {
493                                 while (pos != target) {
494                                         char c = thisPtr[pos];
495                                         int x = 0;
496                                         while (x < table.Length) {
497                                                 if (c == tablePtr[x])
498                                                         break;
499                                                 x++;
500                                         }
501                                         if (x == table.Length)
502                                                 return pos;
503                                         pos += change;
504                                 }
505                         }
506                         return pos;
507                 }
508
509                 public static int Compare (String strA, String strB)
510                 {
511                         return CultureInfo.CurrentCulture.CompareInfo.Compare (strA, strB, CompareOptions.None);
512                 }
513
514                 public static int Compare (String strA, String strB, bool ignoreCase)
515                 {
516                         return CultureInfo.CurrentCulture.CompareInfo.Compare (strA, strB, ignoreCase ? CompareOptions.IgnoreCase : CompareOptions.None);
517                 }
518
519                 public static int Compare (String strA, String strB, bool ignoreCase, CultureInfo culture)
520                 {
521                         if (culture == null)
522                                 throw new ArgumentNullException ("culture");
523
524                         return culture.CompareInfo.Compare (strA, strB, ignoreCase ? CompareOptions.IgnoreCase : CompareOptions.None);
525                 }
526
527                 public static int Compare (String strA, int indexA, String strB, int indexB, int length)
528                 {
529                         return Compare (strA, indexA, strB, indexB, length, false, CultureInfo.CurrentCulture);
530                 }
531
532                 public static int Compare (String strA, int indexA, String strB, int indexB, int length, bool ignoreCase)
533                 {
534                         return Compare (strA, indexA, strB, indexB, length, ignoreCase, CultureInfo.CurrentCulture);
535                 }
536                 
537                 public static int Compare (String strA, int indexA, String strB, int indexB, int length, bool ignoreCase, CultureInfo culture)
538                 {
539                         if (culture == null)
540                                 throw new ArgumentNullException ("culture");
541
542                         if ((indexA > strA.Length) || (indexB > strB.Length) || (indexA < 0) || (indexB < 0) || (length < 0))
543                                 throw new ArgumentOutOfRangeException ();
544
545                         if (length == 0)
546                                 return 0;
547                         
548                         if (strA == null) {
549                                 if (strB == null) {
550                                         return 0;
551                                 } else {
552                                         return -1;
553                                 }
554                         }
555                         else if (strB == null) {
556                                 return 1;
557                         }
558
559                         CompareOptions compopts;
560
561                         if (ignoreCase)
562                                 compopts = CompareOptions.IgnoreCase;
563                         else
564                                 compopts = CompareOptions.None;
565
566                         // Need to cap the requested length to the
567                         // length of the string, because
568                         // CompareInfo.Compare will insist that length
569                         // <= (string.Length - offset)
570
571                         int len1 = length;
572                         int len2 = length;
573                         
574                         if (length > (strA.Length - indexA)) {
575                                 len1 = strA.Length - indexA;
576                         }
577
578                         if (length > (strB.Length - indexB)) {
579                                 len2 = strB.Length - indexB;
580                         }
581
582                         // ENHANCE: Might call internal_compare_switch directly instead of doing all checks twice
583                         return culture.CompareInfo.Compare (strA, indexA, len1, strB, indexB, len2, compopts);
584                 }
585 #if NET_2_0
586                 public static int Compare (string strA, string strB, StringComparison comparisonType)
587                 {
588                         switch (comparisonType) {
589                         case StringComparison.CurrentCulture:
590                                 return Compare (strA, strB, false, CultureInfo.CurrentCulture);
591                         case StringComparison.CurrentCultureIgnoreCase:
592                                 return Compare (strA, strB, true, CultureInfo.CurrentCulture);
593                         case StringComparison.InvariantCulture:
594                                 return Compare (strA, strB, false, CultureInfo.InvariantCulture);
595                         case StringComparison.InvariantCultureIgnoreCase:
596                                 return Compare (strA, strB, true, CultureInfo.InvariantCulture);
597                         case StringComparison.Ordinal:
598                                 return CompareOrdinalUnchecked (strA, 0, Int32.MaxValue, strB, 0, Int32.MaxValue);
599                         case StringComparison.OrdinalIgnoreCase:
600                                 return CompareOrdinalCaseInsensitiveUnchecked (strA, 0, Int32.MaxValue, strB, 0, Int32.MaxValue);
601                         default:
602                                 string msg = Locale.GetText ("Invalid value '{0}' for StringComparison", comparisonType);
603                                 throw new ArgumentException (msg, "comparisonType");
604                         }
605                 }
606
607                 public static int Compare (string strA, int indexA, string strB, int indexB, int length, StringComparison comparisonType)
608                 {
609                         switch (comparisonType) {
610                         case StringComparison.CurrentCulture:
611                                 return Compare (strA, indexA, strB, indexB, length, false, CultureInfo.CurrentCulture);
612                         case StringComparison.CurrentCultureIgnoreCase:
613                                 return Compare (strA, indexA, strB, indexB, length, true, CultureInfo.CurrentCulture);
614                         case StringComparison.InvariantCulture:
615                                 return Compare (strA, indexA, strB, indexB, length, false, CultureInfo.InvariantCulture);
616                         case StringComparison.InvariantCultureIgnoreCase:
617                                 return Compare (strA, indexA, strB, indexB, length, true, CultureInfo.InvariantCulture);
618                         case StringComparison.Ordinal:
619                                 return CompareOrdinal (strA, indexA, strB, indexB, length);
620                         case StringComparison.OrdinalIgnoreCase:
621                                 return CompareOrdinalCaseInsensitive (strA, indexA, strB, indexB, length);
622                         default:
623                                 string msg = Locale.GetText ("Invalid value '{0}' for StringComparison", comparisonType);
624                                 throw new ArgumentException (msg, "comparisonType");
625                         }
626                 }
627
628                 public static bool Equals (string a, string b, StringComparison comparisonType)
629                 {
630                         return String.Compare (a, b, comparisonType) == 0;
631                 }
632
633                 public bool Equals (string value, StringComparison comparisonType)
634                 {
635                         return String.Compare (value, this, comparisonType) == 0;
636                 }
637 #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                 // Following 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                                         CharCopy (dest, src, start_pos);
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                 // culture-insensitive using ordinal search (See testcase StringTest.ReplaceStringCultureTests)
1618                 public String Replace (String oldValue, String newValue)
1619                 {
1620                         // LAMESPEC: According to MSDN the following method is culture-sensitive but this seems to be incorrect
1621                         // LAMESPEC: Result is undefined if result length is longer than maximum string length
1622
1623                         if (oldValue == null)
1624                                 throw new ArgumentNullException ("oldValue");
1625
1626                         if (oldValue.Length == 0)
1627                                 throw new ArgumentException ("oldValue is the empty string.");
1628
1629                         if (this.Length == 0)
1630                                 return this;
1631                         
1632                         if (newValue == null)
1633                                 newValue = String.Empty;
1634
1635                         return ReplaceUnchecked (oldValue, newValue);
1636                 }
1637
1638                 private unsafe String ReplaceUnchecked (String oldValue, String newValue)
1639                 {
1640                         if (oldValue.length > length)
1641                                 return this;
1642                         if (oldValue.length == 1 && newValue.length == 1) {
1643                                 return Replace (oldValue[0], newValue[0]);
1644                                 // ENHANCE: It would be possible to special case oldValue.length == newValue.length
1645                                 // because the length of the result would be this.length and length calculation unneccesary
1646                         }
1647
1648                         const int maxValue = 200; // Allocate 800 byte maximum
1649                         int* dat = stackalloc int[maxValue];
1650                         fixed (char* source = this, replace = newValue) {
1651                                 int i = 0, count = 0;
1652                                 while (i < length) {
1653                                         int found = IndexOfOrdinalUnchecked (oldValue, i, length - i);
1654                                         if (found < 0)
1655                                                 break;
1656                                         else {
1657                                                 if (count < maxValue)
1658                                                         dat[count++] = found;
1659                                                 else
1660                                                         return ReplaceFallback (oldValue, newValue, maxValue);
1661                                         }
1662                                         i = found + oldValue.length;
1663                                 }
1664                                 int nlen = this.length + ((newValue.length - oldValue.length) * count);
1665                                 String tmp = InternalAllocateStr (nlen);
1666
1667                                 int curPos = 0, lastReadPos = 0;
1668                                 fixed (char* dest = tmp) {
1669                                         for (int j = 0; j < count; j++) {
1670                                                 int precopy = dat[j] - lastReadPos;
1671                                                 CharCopy (dest + curPos, source + lastReadPos, precopy);
1672                                                 curPos += precopy;
1673                                                 lastReadPos = dat[j] + oldValue.length;
1674                                                 CharCopy (dest + curPos, replace, newValue.length);
1675                                                 curPos += newValue.length;
1676                                         }
1677                                         CharCopy (dest + curPos, source + lastReadPos, length - lastReadPos);
1678                                 }
1679                                 return tmp;
1680                         }
1681                 }
1682
1683                 private String ReplaceFallback (String oldValue, String newValue, int testedCount)
1684                 {
1685                         int lengthEstimate = this.length + ((newValue.length - oldValue.length) * testedCount);
1686                         StringBuilder sb = new StringBuilder (lengthEstimate);
1687                         for (int i = 0; i < length;) {
1688                                 int found = IndexOfOrdinalUnchecked (oldValue, i, length - i);
1689                                 if (found < 0) {
1690                                         sb.Append (SubstringUnchecked (i, length - i));
1691                                         break;
1692                                 }
1693                                 sb.Append (SubstringUnchecked (i, found - i));
1694                                 sb.Append (newValue);
1695                                 i = found + oldValue.Length;
1696                         }
1697                         return sb.ToString ();
1698
1699                 }
1700
1701                 public unsafe String Remove (int startIndex, int count)
1702                 {
1703                         if (startIndex < 0)
1704                                 throw new ArgumentOutOfRangeException ("startIndex", "Cannot be negative.");
1705                         if (count < 0)
1706                                 throw new ArgumentOutOfRangeException ("count", "Cannot be negative.");
1707                         if (startIndex > this.length - count)
1708                                 throw new ArgumentOutOfRangeException ("count", "startIndex + count > this.length");
1709
1710                         String tmp = InternalAllocateStr (this.length - count);
1711
1712                         fixed (char *dest = tmp, src = this) {
1713                                 char *dst = dest;
1714                                 CharCopy (dst, src, startIndex);
1715                                 int skip = startIndex + count;
1716                                 dst += startIndex;
1717                                 CharCopy (dst, src + skip, length - skip);
1718                         }
1719                         return tmp;
1720                 }
1721
1722                 public String ToLower ()
1723                 {
1724                         return ToLower (CultureInfo.CurrentCulture);
1725                 }
1726
1727                 public String ToLower (CultureInfo culture)
1728                 {
1729                         if (culture == null)
1730                                 throw new ArgumentNullException ("culture");
1731
1732                         if (culture.LCID == 0x007F) // Invariant
1733                                 return ToLowerInvariant ();
1734
1735                         return culture.TextInfo.ToLower (this);
1736                 }
1737
1738 #if NET_2_0
1739                 public unsafe String ToLowerInvariant ()
1740 #else
1741                 internal unsafe String ToLowerInvariant ()
1742 #endif
1743                 {
1744                         string tmp = InternalAllocateStr (length);
1745                         fixed (char* source = &start_char, dest = tmp) {
1746
1747                                 char* destPtr = (char*)dest;
1748                                 char* sourcePtr = (char*)source;
1749
1750                                 for (int n = 0; n < length; n++) {
1751                                         *destPtr = Char.ToLowerInvariant (*sourcePtr);
1752                                         sourcePtr++;
1753                                         destPtr++;
1754                                 }
1755                         }
1756                         return tmp;
1757                 }
1758
1759                 public String ToUpper ()
1760                 {
1761                         return ToUpper (CultureInfo.CurrentCulture);
1762                 }
1763
1764                 public String ToUpper (CultureInfo culture)
1765                 {
1766                         if (culture == null)
1767                                 throw new ArgumentNullException ("culture");
1768
1769                         if (culture.LCID == 0x007F) // Invariant
1770                                 return ToUpperInvariant ();
1771
1772                         return culture.TextInfo.ToUpper (this);
1773                 }
1774
1775 #if NET_2_0
1776                 public unsafe String ToUpperInvariant ()
1777 #else
1778                 internal unsafe String ToUpperInvariant ()
1779 #endif
1780                 {
1781                         string tmp = InternalAllocateStr (length);
1782                         fixed (char* source = &start_char, dest = tmp) {
1783
1784                                 char* destPtr = (char*)dest;
1785                                 char* sourcePtr = (char*)source;
1786
1787                                 for (int n = 0; n < length; n++) {
1788                                         *destPtr = Char.ToUpperInvariant (*sourcePtr);
1789                                         sourcePtr++;
1790                                         destPtr++;
1791                                 }
1792                         }
1793                         return tmp;
1794                 }
1795
1796                 public override String ToString ()
1797                 {
1798                         return this;
1799                 }
1800
1801                 public String ToString (IFormatProvider provider)
1802                 {
1803                         return this;
1804                 }
1805
1806                 public static String Format (String format, Object arg0)
1807                 {
1808                         return Format (null, format, new Object[] {arg0});
1809                 }
1810
1811                 public static String Format (String format, Object arg0, Object arg1)
1812                 {
1813                         return Format (null, format, new Object[] {arg0, arg1});
1814                 }
1815
1816                 public static String Format (String format, Object arg0, Object arg1, Object arg2)
1817                 {
1818                         return Format (null, format, new Object[] {arg0, arg1, arg2});
1819                 }
1820
1821                 public static string Format (string format, params object[] args)
1822                 {
1823                         return Format (null, format, args);
1824                 }
1825         
1826                 public static string Format (IFormatProvider provider, string format, params object[] args)
1827                 {
1828                         StringBuilder b = new StringBuilder ();
1829                         FormatHelper (b, provider, format, args);
1830                         return b.ToString ();
1831                 }
1832                 
1833                 internal static void FormatHelper (StringBuilder result, IFormatProvider provider, string format, params object[] args)
1834                 {
1835                         if (format == null)
1836                                 throw new ArgumentNullException ("format");
1837                         if (args == null)
1838                                 throw new ArgumentNullException ("args");
1839
1840                         int ptr = 0;
1841                         int start = ptr;
1842                         while (ptr < format.length) {
1843                                 char c = format[ptr ++];
1844
1845                                 if (c == '{') {
1846                                         result.Append (format, start, ptr - start - 1);
1847
1848                                         // check for escaped open bracket
1849
1850                                         if (format[ptr] == '{') {
1851                                                 start = ptr ++;
1852                                                 continue;
1853                                         }
1854
1855                                         // parse specifier
1856                                 
1857                                         int n, width;
1858                                         bool left_align;
1859                                         string arg_format;
1860
1861                                         ParseFormatSpecifier (format, ref ptr, out n, out width, out left_align, out arg_format);
1862                                         if (n >= args.Length)
1863                                                 throw new FormatException ("Index (zero based) must be greater than or equal to zero and less than the size of the argument list.");
1864
1865                                         // format argument
1866
1867                                         object arg = args[n];
1868
1869                                         string str;
1870                                         ICustomFormatter formatter = null;
1871                                         if (provider != null)
1872                                                 formatter = provider.GetFormat (typeof (ICustomFormatter))
1873                                                         as ICustomFormatter;
1874                                         if (arg == null)
1875                                                 str = String.Empty;
1876                                         else if (formatter != null)
1877                                                 str = formatter.Format (arg_format, arg, provider);
1878                                         else if (arg is IFormattable)
1879                                                 str = ((IFormattable)arg).ToString (arg_format, provider);
1880                                         else
1881                                                 str = arg.ToString ();
1882
1883                                         // pad formatted string and append to result
1884
1885                                         if (width > str.length) {
1886                                                 const char padchar = ' ';
1887                                                 int padlen = width - str.length;
1888
1889                                                 if (left_align) {
1890                                                         result.Append (str);
1891                                                         result.Append (padchar, padlen);
1892                                                 }
1893                                                 else {
1894                                                         result.Append (padchar, padlen);
1895                                                         result.Append (str);
1896                                                 }
1897                                         }
1898                                         else
1899                                                 result.Append (str);
1900
1901                                         start = ptr;
1902                                 }
1903                                 else if (c == '}' && ptr < format.length && format[ptr] == '}') {
1904                                         result.Append (format, start, ptr - start - 1);
1905                                         start = ptr ++;
1906                                 }
1907                                 else if (c == '}') {
1908                                         throw new FormatException ("Input string was not in a correct format.");
1909                                 }
1910                         }
1911
1912                         if (start < format.length)
1913                                 result.Append (format, start, format.Length - start);
1914                 }
1915
1916                 public unsafe static String Copy (String str)
1917                 {
1918                         if (str == null)
1919                                 throw new ArgumentNullException ("str");
1920
1921                         int length = str.length;
1922
1923                         String tmp = InternalAllocateStr (length);
1924                         if (length != 0) {
1925                                 fixed (char *dest = tmp, src = str) {
1926                                         CharCopy (dest, src, length);
1927                                 }
1928                         }
1929                         return tmp;
1930                 }
1931
1932                 public static String Concat (Object obj)
1933                 {
1934                         if (obj == null)
1935                                 return String.Empty;
1936
1937                         return obj.ToString ();
1938                 }
1939
1940                 public unsafe static String Concat (Object obj1, Object obj2)
1941                 {
1942                         string s1, s2;
1943
1944                         s1 = (obj1 != null) ? obj1.ToString () : null;
1945                         s2 = (obj2 != null) ? obj2.ToString () : null;
1946                         
1947                         if (s1 == null) {
1948                                 if (s2 == null)
1949                                         return String.Empty;
1950                                 else
1951                                         return s2;
1952                         } else if (s2 == null)
1953                                 return s1;
1954
1955                         String tmp = InternalAllocateStr (s1.Length + s2.Length);
1956                         if (s1.Length != 0) {
1957                                 fixed (char *dest = tmp, src = s1) {
1958                                         CharCopy (dest, src, s1.length);
1959                                 }
1960                         }
1961                         if (s2.Length != 0) {
1962                                 fixed (char *dest = tmp, src = s2) {
1963                                         CharCopy (dest + s1.Length, src, s2.length);
1964                                 }
1965                         }
1966
1967                         return tmp;
1968                 }
1969
1970                 public static String Concat (Object obj1, Object obj2, Object obj3)
1971                 {
1972                         string s1, s2, s3;
1973                         if (obj1 == null)
1974                                 s1 = String.Empty;
1975                         else
1976                                 s1 = obj1.ToString ();
1977
1978                         if (obj2 == null)
1979                                 s2 = String.Empty;
1980                         else
1981                                 s2 = obj2.ToString ();
1982
1983                         if (obj3 == null)
1984                                 s3 = String.Empty;
1985                         else
1986                                 s3 = obj3.ToString ();
1987
1988                         return Concat (s1, s2, s3);
1989                 }
1990
1991 #if ! BOOTSTRAP_WITH_OLDLIB
1992                 [CLSCompliant(false)]
1993                 public static String Concat (Object obj1, Object obj2, Object obj3,
1994                                              Object obj4, __arglist)
1995                 {
1996                         string s1, s2, s3, s4;
1997
1998                         if (obj1 == null)
1999                                 s1 = String.Empty;
2000                         else
2001                                 s1 = obj1.ToString ();
2002
2003                         if (obj2 == null)
2004                                 s2 = String.Empty;
2005                         else
2006                                 s2 = obj2.ToString ();
2007
2008                         if (obj3 == null)
2009                                 s3 = String.Empty;
2010                         else
2011                                 s3 = obj3.ToString ();
2012
2013                         ArgIterator iter = new ArgIterator (__arglist);
2014                         int argCount = iter.GetRemainingCount();
2015
2016                         StringBuilder sb = new StringBuilder ();
2017                         if (obj4 != null)
2018                                 sb.Append (obj4.ToString ());
2019
2020                         for (int i = 0; i < argCount; i++) {
2021                                 TypedReference typedRef = iter.GetNextArg ();
2022                                 sb.Append (TypedReference.ToObject (typedRef));
2023                         }
2024
2025                         s4 = sb.ToString ();
2026
2027                         return Concat (s1, s2, s3, s4);                 
2028                 }
2029 #endif
2030
2031                 public unsafe static String Concat (String s1, String s2)
2032                 {
2033                         if (s1 == null || s1.Length == 0) {
2034                                 if (s2 == null || s2.Length == 0)
2035                                         return String.Empty;
2036                                 return s2;
2037                         }
2038
2039                         if (s2 == null || s2.Length == 0)
2040                                 return s1; 
2041
2042                         String tmp = InternalAllocateStr (s1.length + s2.length);
2043
2044                         fixed (char *dest = tmp, src = s1)
2045                                 CharCopy (dest, src, s1.length);
2046                         fixed (char *dest = tmp, src = s2)
2047                                 CharCopy (dest + s1.Length, src, s2.length);
2048
2049                         return tmp;
2050                 }
2051
2052                 public unsafe static String Concat (String s1, String s2, String s3)
2053                 {
2054                         if (s1 == null || s1.Length == 0){
2055                                 if (s2 == null || s2.Length == 0){
2056                                         if (s3 == null || s3.Length == 0)
2057                                                 return String.Empty;
2058                                         return s3;
2059                                 } else {
2060                                         if (s3 == null || s3.Length == 0)
2061                                                 return s2;
2062                                 }
2063                                 s1 = String.Empty;
2064                         } else {
2065                                 if (s2 == null || s2.Length == 0){
2066                                         if (s3 == null || s3.Length == 0)
2067                                                 return s1;
2068                                         else
2069                                                 s2 = String.Empty;
2070                                 } else {
2071                                         if (s3 == null || s3.Length == 0)
2072                                                 s3 = String.Empty;
2073                                 }
2074                         }
2075
2076                         String tmp = InternalAllocateStr (s1.length + s2.length + s3.length);
2077
2078                         if (s1.Length != 0) {
2079                                 fixed (char *dest = tmp, src = s1) {
2080                                         CharCopy (dest, src, s1.length);
2081                                 }
2082                         }
2083                         if (s2.Length != 0) {
2084                                 fixed (char *dest = tmp, src = s2) {
2085                                         CharCopy (dest + s1.Length, src, s2.length);
2086                                 }
2087                         }
2088                         if (s3.Length != 0) {
2089                                 fixed (char *dest = tmp, src = s3) {
2090                                         CharCopy (dest + s1.Length + s2.Length, src, s3.length);
2091                                 }
2092                         }
2093
2094                         return tmp;
2095                 }
2096
2097                 public unsafe static String Concat (String s1, String s2, String s3, String s4)
2098                 {
2099                         if (s1 == null && s2 == null && s3 == null && s4 == null)
2100                                 return String.Empty;
2101
2102                         if (s1 == null)
2103                                 s1 = String.Empty;
2104                         if (s2 == null)
2105                                 s2 = String.Empty;
2106                         if (s3 == null)
2107                                 s3 = String.Empty;
2108                         if (s4 == null)
2109                                 s4 = String.Empty;
2110
2111                         String tmp = InternalAllocateStr (s1.length + s2.length + s3.length + s4.length);
2112
2113                         if (s1.Length != 0) {
2114                                 fixed (char *dest = tmp, src = s1) {
2115                                         CharCopy (dest, src, s1.length);
2116                                 }
2117                         }
2118                         if (s2.Length != 0) {
2119                                 fixed (char *dest = tmp, src = s2) {
2120                                         CharCopy (dest + s1.Length, src, s2.length);
2121                                 }
2122                         }
2123                         if (s3.Length != 0) {
2124                                 fixed (char *dest = tmp, src = s3) {
2125                                         CharCopy (dest + s1.Length + s2.Length, src, s3.length);
2126                                 }
2127                         }
2128                         if (s4.Length != 0) {
2129                                 fixed (char *dest = tmp, src = s4) {
2130                                         CharCopy (dest + s1.Length + s2.Length + s3.Length, src, s4.length);
2131                                 }
2132                         }
2133
2134                         return tmp;
2135                 }
2136
2137                 public static String Concat (params Object[] args)
2138                 {
2139                         if (args == null)
2140                                 throw new ArgumentNullException ("args");
2141
2142                         int argLen = args.Length;
2143                         if (argLen == 0)
2144                                 return String.Empty;
2145
2146                         string [] strings = new string [argLen];
2147                         int len = 0;
2148                         for (int i = 0; i < argLen; i++) {
2149                                 if (args[i] != null) {
2150                                         strings[i] = args[i].ToString ();
2151                                         len += strings[i].length;
2152                                 }
2153                         }
2154                         if (len == 0)
2155                                 return String.Empty;
2156
2157                         return ConcatInternal (strings, len);
2158                 }
2159
2160                 public static String Concat (params String[] values)
2161                 {
2162                         if (values == null)
2163                                 throw new ArgumentNullException ("values");
2164
2165                         int len = 0;
2166                         for (int i = 0; i < values.Length; i++) {
2167                                 String s = values[i];
2168                                 if (s != null)
2169                                         len += s.length;
2170                         }
2171                         if (len == 0)
2172                                 return String.Empty;
2173
2174                         return ConcatInternal (values, len);
2175                 }
2176
2177                 private static unsafe String ConcatInternal (String[] values, int length)
2178                 {
2179                         String tmp = InternalAllocateStr (length);
2180
2181                         fixed (char* dest = tmp) {
2182                                 int pos = 0;
2183                                 for (int i = 0; i < values.Length; i++) {
2184                                         String source = values[i];
2185                                         if (source != null) {
2186                                                 fixed (char* src = source) {
2187                                                         CharCopy (dest + pos, src, source.length);
2188                                                 }
2189                                                 pos += source.Length;
2190                                         }
2191                                 }
2192                         }
2193                         return tmp;
2194                 }
2195
2196                 public unsafe String Insert (int startIndex, String value)
2197                 {
2198                         if (value == null)
2199                                 throw new ArgumentNullException ("value");
2200
2201                         if (startIndex < 0 || startIndex > this.length)
2202                                 throw new ArgumentOutOfRangeException ("startIndex", "Cannot be negative and must be less than or equal to length of string.");
2203
2204                         if (value.Length == 0)
2205                                 return this;
2206                         if (this.Length == 0)
2207                                 return value;
2208                         String tmp = InternalAllocateStr (this.length + value.length);
2209
2210                         fixed (char *dest = tmp, src = this, val = value) {
2211                                 char *dst = dest;
2212                                 CharCopy (dst, src, startIndex);
2213                                 dst += startIndex;
2214                                 CharCopy (dst, val, value.length);
2215                                 dst += value.length;
2216                                 CharCopy (dst, src + startIndex, length - startIndex);
2217                         }
2218                         return tmp;
2219                 }
2220
2221                 public static string Intern (string str)
2222                 {
2223                         if (str == null)
2224                                 throw new ArgumentNullException ("str");
2225
2226                         return InternalIntern (str);
2227                 }
2228
2229                 public static string IsInterned (string str)
2230                 {
2231                         if (str == null)
2232                                 throw new ArgumentNullException ("str");
2233
2234                         return InternalIsInterned (str);
2235                 }
2236         
2237                 public static string Join (string separator, string [] value)
2238                 {
2239                         if (value == null)
2240                                 throw new ArgumentNullException ("value");
2241                         if (separator == null)
2242                                 separator = String.Empty;
2243
2244                         return JoinUnchecked (separator, value, 0, value.Length);
2245                 }
2246
2247                 public static string Join (string separator, string[] value, int startIndex, int count)
2248                 {
2249                         if (value == null)
2250                                 throw new ArgumentNullException ("value");
2251                         if (startIndex < 0)
2252                                 throw new ArgumentOutOfRangeException ("startIndex", "< 0");
2253                         if (count < 0)
2254                                 throw new ArgumentOutOfRangeException ("count", "< 0");
2255                         if (startIndex > value.Length - count)
2256                                 throw new ArgumentOutOfRangeException ("startIndex", "startIndex + count > value.length");
2257
2258                         if (startIndex == value.Length)
2259                                 return String.Empty;
2260                         if (separator == null)
2261                                 separator = String.Empty;
2262
2263                         return JoinUnchecked (separator, value, startIndex, count);
2264                 }
2265
2266                 private static unsafe string JoinUnchecked (string separator, string[] value, int startIndex, int count)
2267                 {
2268                         // Unchecked parameters
2269                         // startIndex, count must be >= 0; startIndex + count must be <= value.length
2270                         // separator and value must not be null
2271
2272                         int length = 0;
2273                         int maxIndex = startIndex + count;
2274                         // Precount the number of characters that the resulting string will have
2275                         for (int i = startIndex; i < maxIndex; i++) {
2276                                 String s = value[i];
2277                                 if (s != null)
2278                                         length += s.length;
2279                         }
2280                         length += separator.length * (count - 1);
2281                         if (length <= 0)
2282                                 return String.Empty;
2283
2284                         String tmp = InternalAllocateStr (length);
2285
2286                         maxIndex--;
2287                         fixed (char* dest = tmp, sepsrc = separator) {
2288                                 // Copy each string from value except the last one and add a separator for each
2289                                 int pos = 0;
2290                                 for (int i = startIndex; i < maxIndex; i++) {
2291                                         String source = value[i];
2292                                         if (source != null) {
2293                                                 if (source.Length > 0) {
2294                                                         fixed (char* src = source)
2295                                                                 CharCopy (dest + pos, src, source.Length);
2296                                                         pos += source.Length;
2297                                                 }
2298                                         }
2299                                         if (separator.Length > 0) {
2300                                                 CharCopy (dest + pos, sepsrc, separator.Length);
2301                                                 pos += separator.Length;
2302                                         }
2303                                 }
2304                                 // Append last string that does not get an additional separator
2305                                 String sourceLast = value[maxIndex];
2306                                 if (sourceLast != null) {
2307                                         if (sourceLast.Length > 0) {
2308                                                 fixed (char* src = sourceLast)
2309                                                         CharCopy (dest + pos, src, sourceLast.Length);
2310                                         }
2311                                 }
2312                         }
2313                         return tmp;
2314                 }
2315
2316                 bool IConvertible.ToBoolean (IFormatProvider provider)
2317                 {
2318                         return Convert.ToBoolean (this, provider);
2319                 }
2320
2321                 byte IConvertible.ToByte (IFormatProvider provider)
2322                 {
2323                         return Convert.ToByte (this, provider);
2324                 }
2325
2326                 char IConvertible.ToChar (IFormatProvider provider)
2327                 {
2328                         return Convert.ToChar (this, provider);
2329                 }
2330
2331                 DateTime IConvertible.ToDateTime (IFormatProvider provider)
2332                 {
2333                         return Convert.ToDateTime (this, provider);
2334                 }
2335
2336                 decimal IConvertible.ToDecimal (IFormatProvider provider)
2337                 {
2338                         return Convert.ToDecimal (this, provider);
2339                 }
2340
2341                 double IConvertible.ToDouble (IFormatProvider provider)
2342                 {
2343                         return Convert.ToDouble (this, provider);
2344                 }
2345
2346                 short IConvertible.ToInt16 (IFormatProvider provider)
2347                 {
2348                         return Convert.ToInt16 (this, provider);
2349                 }
2350
2351                 int IConvertible.ToInt32 (IFormatProvider provider)
2352                 {
2353                         return Convert.ToInt32 (this, provider);
2354                 }
2355
2356                 long IConvertible.ToInt64 (IFormatProvider provider)
2357                 {
2358                         return Convert.ToInt64 (this, provider);
2359                 }
2360         
2361                 sbyte IConvertible.ToSByte (IFormatProvider provider)
2362                 {
2363                         return Convert.ToSByte (this, provider);
2364                 }
2365
2366                 float IConvertible.ToSingle (IFormatProvider provider)
2367                 {
2368                         return Convert.ToSingle (this, provider);
2369                 }
2370
2371                 object IConvertible.ToType (Type conversionType, IFormatProvider provider)
2372                 {
2373                         return Convert.ToType (this, conversionType,  provider);
2374                 }
2375
2376                 ushort IConvertible.ToUInt16 (IFormatProvider provider)
2377                 {
2378                         return Convert.ToUInt16 (this, provider);
2379                 }
2380
2381                 uint IConvertible.ToUInt32 (IFormatProvider provider)
2382                 {
2383                         return Convert.ToUInt32 (this, provider);
2384                 }
2385
2386                 ulong IConvertible.ToUInt64 (IFormatProvider provider)
2387                 {
2388                         return Convert.ToUInt64 (this, provider);
2389                 }
2390
2391                 public int Length {
2392                         get {
2393                                 return length;
2394                         }
2395                 }
2396
2397                 public CharEnumerator GetEnumerator ()
2398                 {
2399                         return new CharEnumerator (this);
2400                 }
2401
2402 #if NET_2_0
2403                 IEnumerator<char> IEnumerable<char>.GetEnumerator ()
2404                 {
2405                         return new CharEnumerator (this);
2406                 }
2407 #endif
2408
2409                 IEnumerator IEnumerable.GetEnumerator ()
2410                 {
2411                         return new CharEnumerator (this);
2412                 }
2413
2414                 private static void ParseFormatSpecifier (string str, ref int ptr, out int n, out int width,
2415                                                           out bool left_align, out string format)
2416                 {
2417                         // parses format specifier of form:
2418                         //   N,[\ +[-]M][:F]}
2419                         //
2420                         // where:
2421
2422                         try {
2423                                 // N = argument number (non-negative integer)
2424
2425                                 n = ParseDecimal (str, ref ptr);
2426                                 if (n < 0)
2427                                         throw new FormatException ("Input string was not in a correct format.");
2428
2429                                 // M = width (non-negative integer)
2430
2431                                 if (str[ptr] == ',') {
2432                                         // White space between ',' and number or sign.
2433                                         ++ptr;
2434                                         while (Char.IsWhiteSpace (str [ptr]))
2435                                                 ++ptr;
2436                                         int start = ptr;
2437
2438                                         format = str.Substring (start, ptr - start);
2439
2440                                         left_align = (str [ptr] == '-');
2441                                         if (left_align)
2442                                                 ++ ptr;
2443
2444                                         width = ParseDecimal (str, ref ptr);
2445                                         if (width < 0)
2446                                                 throw new FormatException ("Input string was not in a correct format.");
2447                                 }
2448                                 else {
2449                                         width = 0;
2450                                         left_align = false;
2451                                         format = String.Empty;
2452                                 }
2453
2454                                 // F = argument format (string)
2455
2456                                 if (str[ptr] == ':') {
2457                                         int start = ++ ptr;
2458                                         while (str[ptr] != '}')
2459                                                 ++ ptr;
2460
2461                                         format += str.Substring (start, ptr - start);
2462                                 }
2463                                 else
2464                                         format = null;
2465
2466                                 if (str[ptr ++] != '}')
2467                                         throw new FormatException ("Input string was not in a correct format.");
2468                         }
2469                         catch (IndexOutOfRangeException) {
2470                                 throw new FormatException ("Input string was not in a correct format.");
2471                         }
2472                 }
2473
2474                 private static int ParseDecimal (string str, ref int ptr)
2475                 {
2476                         int p = ptr;
2477                         int n = 0;
2478                         while (true) {
2479                                 char c = str[p];
2480                                 if (c < '0' || '9' < c)
2481                                         break;
2482
2483                                 n = n * 10 + c - '0';
2484                                 ++ p;
2485                         }
2486
2487                         if (p == ptr)
2488                                 return -1;
2489
2490                         ptr = p;
2491                         return n;
2492                 }
2493
2494                 internal unsafe void InternalSetChar (int idx, char val)
2495                 {
2496                         if ((uint) idx >= (uint) Length)
2497                                 throw new ArgumentOutOfRangeException ("idx");
2498
2499                         fixed (char * pStr = &start_char) 
2500                         {
2501                                 pStr [idx] = val;
2502                         }
2503                 }
2504
2505                 internal unsafe void InternalSetLength (int newLength)
2506                 {
2507                         if (newLength > length)
2508                                 throw new ArgumentOutOfRangeException ("newLength", "newLength as to be <= length");
2509
2510                         // zero terminate, we can pass string objects directly via pinvoke
2511                         // we also zero the rest of the string, since the new GC needs to be
2512                         // able to handle the changing size (it will skip the 0 bytes).
2513                         fixed (char * pStr = &start_char) {
2514                                 char *p = pStr + newLength;
2515                                 char *end = pStr + length;
2516                                 while (p < end) {
2517                                         p [0] = '\0';
2518                                         p++;
2519                                 }
2520                         }
2521                         length = newLength;
2522                 }
2523
2524 #if NET_2_0
2525                 [ReliabilityContractAttribute (Consistency.WillNotCorruptState, Cer.MayFail)]
2526 #endif
2527                 // When modifying it, GetCaseInsensitiveHashCode() should be modified as well.
2528                 public unsafe override int GetHashCode ()
2529                 {
2530                         fixed (char * c = this) {
2531                                 char * cc = c;
2532                                 char * end = cc + length - 1;
2533                                 int h = 0;
2534                                 for (;cc < end; cc += 2) {
2535                                         h = (h << 5) - h + *cc;
2536                                         h = (h << 5) - h + cc [1];
2537                                 }
2538                                 ++end;
2539                                 if (cc < end)
2540                                         h = (h << 5) - h + *cc;
2541                                 return h;
2542                         }
2543                 }
2544
2545                 internal unsafe int GetCaseInsensitiveHashCode ()
2546                 {
2547                         fixed (char * c = this) {
2548                                 char * cc = c;
2549                                 char * end = cc + length - 1;
2550                                 int h = 0;
2551                                 for (;cc < end; cc += 2) {
2552                                         h = (h << 5) - h + Char.ToUpperInvariant (*cc);
2553                                         h = (h << 5) - h + Char.ToUpperInvariant (cc [1]);
2554                                 }
2555                                 ++end;
2556                                 if (cc < end)
2557                                         h = (h << 5) - h + Char.ToUpperInvariant (*cc);
2558                                 return h;
2559                         }
2560                 }
2561
2562                 // Certain constructors are redirected to CreateString methods with
2563                 // matching argument list. The this pointer should not be used.
2564
2565                 private unsafe String CreateString (sbyte* value)
2566                 {
2567                         if (value == null)
2568                                 return String.Empty;
2569
2570                         byte* bytes = (byte*) value;
2571                         int length = 0;
2572
2573                         try {
2574                                 while (bytes++ [0] != 0)
2575                                         length++;
2576                         } catch (NullReferenceException) {
2577                                 throw new ArgumentOutOfRangeException ("ptr", "Value does not refer to a valid string.");
2578 #if NET_2_0
2579                         } catch (AccessViolationException) {
2580                                 throw new ArgumentOutOfRangeException ("ptr", "Value does not refer to a valid string.");
2581 #endif
2582                         }
2583
2584                         return CreateString (value, 0, length, null);
2585                 }
2586
2587                 private unsafe String CreateString (sbyte* value, int startIndex, int length)
2588                 {
2589                         return CreateString (value, startIndex, length, null);
2590                 }
2591
2592                 private unsafe String CreateString (sbyte* value, int startIndex, int length, Encoding enc)
2593                 {
2594                         if (length < 0)
2595                                 throw new ArgumentOutOfRangeException ("length", "Non-negative number required.");
2596                         if (startIndex < 0)
2597                                 throw new ArgumentOutOfRangeException ("startIndex", "Non-negative number required.");
2598                         if (value + startIndex < value)
2599                                 throw new ArgumentOutOfRangeException ("startIndex", "Value, startIndex and length do not refer to a valid string.");
2600
2601                         bool isDefaultEncoding;
2602
2603                         if (isDefaultEncoding = (enc == null)) {
2604 #if NET_2_0
2605                                 if (value == null)
2606                                         throw new ArgumentNullException ("value");
2607                                 if (length == 0)
2608 #else
2609                                 if (value == null || length == 0)
2610 #endif
2611                                         return String.Empty;
2612
2613                                 enc = Encoding.Default;
2614                         }
2615
2616                         byte [] bytes = new byte [length];
2617
2618                         if (length != 0)
2619                                 fixed (byte* bytePtr = bytes)
2620                                         try {
2621                                                 memcpy (bytePtr, (byte*) (value + startIndex), length);
2622                                         } catch (NullReferenceException) {
2623 #if !NET_2_0
2624                                                 if (!isDefaultEncoding)
2625                                                         throw;
2626 #endif
2627
2628                                                 throw new ArgumentOutOfRangeException ("ptr", "Value, startIndex and length do not refer to a valid string.");
2629 #if NET_2_0
2630                                         } catch (AccessViolationException) {
2631                                                 if (!isDefaultEncoding)
2632                                                         throw;
2633
2634                                                 throw new ArgumentOutOfRangeException ("value", "Value, startIndex and length do not refer to a valid string.");
2635 #endif
2636                                         }
2637
2638                         // GetString () is called even when length == 0
2639                         return enc.GetString (bytes);
2640                 }
2641
2642                 unsafe string CreateString (char *value)
2643                 {
2644                         if (value == null)
2645                                 return string.Empty;
2646                         char *p = value;
2647                         int i = 0;
2648                         while (*p != 0) {
2649                                 ++i;
2650                                 ++p;
2651                         }
2652                         string result = InternalAllocateStr (i);
2653
2654                         if (i != 0) {
2655                                 fixed (char *dest = result) {
2656                                         CharCopy (dest, value, i);
2657                                 }
2658                         }
2659                         return result;
2660                 }
2661
2662                 unsafe string CreateString (char *value, int startIndex, int length)
2663                 {
2664                         if (length == 0)
2665                                 return string.Empty;
2666                         if (value == null)
2667                                 throw new ArgumentNullException ("value");
2668                         if (startIndex < 0)
2669                                 throw new ArgumentOutOfRangeException ("startIndex");
2670                         if (length < 0)
2671                                 throw new ArgumentOutOfRangeException ("length");
2672
2673                         string result = InternalAllocateStr (length);
2674
2675                         fixed (char *dest = result) {
2676                                 CharCopy (dest, value + startIndex, length);
2677                         }
2678                         return result;
2679                 }
2680
2681                 unsafe string CreateString (char [] val, int startIndex, int length)
2682                 {
2683                         if (val == null)
2684                                 throw new ArgumentNullException ("value");
2685                         if (startIndex < 0)
2686                                 throw new ArgumentOutOfRangeException ("startIndex", "Cannot be negative.");
2687                         if (length < 0)
2688                                 throw new ArgumentOutOfRangeException ("length", "Cannot be negative.");
2689                         if (startIndex > val.Length - length)
2690                                 throw new ArgumentOutOfRangeException ("startIndex", "Cannot be negative, and should be less than length of string.");
2691                         if (length == 0)
2692                                 return string.Empty;
2693
2694                         string result = InternalAllocateStr (length);
2695
2696                         fixed (char *dest = result, src = val) {
2697                                 CharCopy (dest, src + startIndex, length);
2698                         }
2699                         return result;
2700                 }
2701
2702                 unsafe string CreateString (char [] val)
2703                 {
2704                         if (val == null)
2705                                 return string.Empty;
2706                         if (val.Length == 0)
2707                                 return string.Empty;
2708                         string result = InternalAllocateStr (val.Length);
2709
2710                         fixed (char *dest = result, src = val) {
2711                                 CharCopy (dest, src, val.Length);
2712                         }
2713                         return result;
2714                 }
2715
2716                 unsafe string CreateString (char c, int count)
2717                 {
2718                         if (count < 0)
2719                                 throw new ArgumentOutOfRangeException ("count");
2720                         if (count == 0)
2721                                 return string.Empty;
2722                         string result = InternalAllocateStr (count);
2723                         fixed (char *dest = result) {
2724                                 char *p = dest;
2725                                 char *end = p + count;
2726                                 while (p < end) {
2727                                         *p = c;
2728                                         p++;
2729                                 }
2730                         }
2731                         return result;
2732                 }
2733
2734                 /* helpers used by the runtime as well as above or eslewhere in corlib */
2735                 internal static unsafe void memset (byte *dest, int val, int len)
2736                 {
2737                         if (len < 8) {
2738                                 while (len != 0) {
2739                                         *dest = (byte)val;
2740                                         ++dest;
2741                                         --len;
2742                                 }
2743                                 return;
2744                         }
2745                         if (val != 0) {
2746                                 val = val | (val << 8);
2747                                 val = val | (val << 16);
2748                         }
2749                         // align to 4
2750                         int rest = (int)dest & 3;
2751                         if (rest != 0) {
2752                                 rest = 4 - rest;
2753                                 len -= rest;
2754                                 do {
2755                                         *dest = (byte)val;
2756                                         ++dest;
2757                                         --rest;
2758                                 } while (rest != 0);
2759                         }
2760                         while (len >= 16) {
2761                                 ((int*)dest) [0] = val;
2762                                 ((int*)dest) [1] = val;
2763                                 ((int*)dest) [2] = val;
2764                                 ((int*)dest) [3] = val;
2765                                 dest += 16;
2766                                 len -= 16;
2767                         }
2768                         while (len >= 4) {
2769                                 ((int*)dest) [0] = val;
2770                                 dest += 4;
2771                                 len -= 4;
2772                         }
2773                         // tail bytes
2774                         while (len > 0) {
2775                                 *dest = (byte)val;
2776                                 dest++;
2777                                 len--;
2778                         }
2779                 }
2780
2781                 static unsafe void memcpy4 (byte *dest, byte *src, int size) {
2782                         /*while (size >= 32) {
2783                                 // using long is better than int and slower than double
2784                                 // FIXME: enable this only on correct alignment or on platforms
2785                                 // that can tolerate unaligned reads/writes of doubles
2786                                 ((double*)dest) [0] = ((double*)src) [0];
2787                                 ((double*)dest) [1] = ((double*)src) [1];
2788                                 ((double*)dest) [2] = ((double*)src) [2];
2789                                 ((double*)dest) [3] = ((double*)src) [3];
2790                                 dest += 32;
2791                                 src += 32;
2792                                 size -= 32;
2793                         }*/
2794                         while (size >= 16) {
2795                                 ((int*)dest) [0] = ((int*)src) [0];
2796                                 ((int*)dest) [1] = ((int*)src) [1];
2797                                 ((int*)dest) [2] = ((int*)src) [2];
2798                                 ((int*)dest) [3] = ((int*)src) [3];
2799                                 dest += 16;
2800                                 src += 16;
2801                                 size -= 16;
2802                         }
2803                         while (size >= 4) {
2804                                 ((int*)dest) [0] = ((int*)src) [0];
2805                                 dest += 4;
2806                                 src += 4;
2807                                 size -= 4;
2808                         }
2809                         while (size > 0) {
2810                                 ((byte*)dest) [0] = ((byte*)src) [0];
2811                                 dest += 1;
2812                                 src += 1;
2813                                 --size;
2814                         }
2815                 }
2816                 static unsafe void memcpy2 (byte *dest, byte *src, int size) {
2817                         while (size >= 8) {
2818                                 ((short*)dest) [0] = ((short*)src) [0];
2819                                 ((short*)dest) [1] = ((short*)src) [1];
2820                                 ((short*)dest) [2] = ((short*)src) [2];
2821                                 ((short*)dest) [3] = ((short*)src) [3];
2822                                 dest += 8;
2823                                 src += 8;
2824                                 size -= 8;
2825                         }
2826                         while (size >= 2) {
2827                                 ((short*)dest) [0] = ((short*)src) [0];
2828                                 dest += 2;
2829                                 src += 2;
2830                                 size -= 2;
2831                         }
2832                         if (size > 0)
2833                                 ((byte*)dest) [0] = ((byte*)src) [0];
2834                 }
2835                 static unsafe void memcpy1 (byte *dest, byte *src, int size) {
2836                         while (size >= 8) {
2837                                 ((byte*)dest) [0] = ((byte*)src) [0];
2838                                 ((byte*)dest) [1] = ((byte*)src) [1];
2839                                 ((byte*)dest) [2] = ((byte*)src) [2];
2840                                 ((byte*)dest) [3] = ((byte*)src) [3];
2841                                 ((byte*)dest) [4] = ((byte*)src) [4];
2842                                 ((byte*)dest) [5] = ((byte*)src) [5];
2843                                 ((byte*)dest) [6] = ((byte*)src) [6];
2844                                 ((byte*)dest) [7] = ((byte*)src) [7];
2845                                 dest += 8;
2846                                 src += 8;
2847                                 size -= 8;
2848                         }
2849                         while (size >= 2) {
2850                                 ((byte*)dest) [0] = ((byte*)src) [0];
2851                                 ((byte*)dest) [1] = ((byte*)src) [1];
2852                                 dest += 2;
2853                                 src += 2;
2854                                 size -= 2;
2855                         }
2856                         if (size > 0)
2857                                 ((byte*)dest) [0] = ((byte*)src) [0];
2858                 }
2859
2860                 internal static unsafe void memcpy (byte *dest, byte *src, int size) {
2861                         // FIXME: if pointers are not aligned, try to align them
2862                         // so a faster routine can be used. Handle the case where
2863                         // the pointers can't be reduced to have the same alignment
2864                         // (just ignore the issue on x86?)
2865                         if ((((int)dest | (int)src) & 3) != 0) {
2866                                 if (((int)dest & 1) != 0 && ((int)src & 1) != 0 && size >= 1) {
2867                                         dest [0] = src [0];
2868                                         ++dest;
2869                                         ++src;
2870                                         --size;
2871                                 }
2872                                 if (((int)dest & 2) != 0 && ((int)src & 2) != 0 && size >= 2) {
2873                                         ((short*)dest) [0] = ((short*)src) [0];
2874                                         dest += 2;
2875                                         src += 2;
2876                                         size -= 2;
2877                                 }
2878                                 if ((((int)dest | (int)src) & 1) != 0) {
2879                                         memcpy1 (dest, src, size);
2880                                         return;
2881                                 }
2882                                 if ((((int)dest | (int)src) & 2) != 0) {
2883                                         memcpy2 (dest, src, size);
2884                                         return;
2885                                 }
2886                         }
2887                         memcpy4 (dest, src, size);
2888                 }
2889
2890                 internal static unsafe void CharCopy (char *dest, char *src, int count) {
2891                         // Same rules as for memcpy, but with the premise that 
2892                         // chars can only be aligned to even addresses if their
2893                         // enclosing types are correctly aligned
2894                         if ((((int)(byte*)dest | (int)(byte*)src) & 3) != 0) {
2895                                 if (((int)(byte*)dest & 2) != 0 && ((int)(byte*)src & 2) != 0 && count > 0) {
2896                                         ((short*)dest) [0] = ((short*)src) [0];
2897                                         dest++;
2898                                         src++;
2899                                         count--;
2900                                 }
2901                                 if ((((int)(byte*)dest | (int)(byte*)src) & 2) != 0) {
2902                                         memcpy2 ((byte*)dest, (byte*)src, count * 2);
2903                                         return;
2904                                 }
2905                         }
2906                         memcpy4 ((byte*)dest, (byte*)src, count * 2);
2907                 }
2908
2909                 internal static unsafe void CharCopyReverse (char *dest, char *src, int count)
2910                 {
2911                         dest += count;
2912                         src += count;
2913                         for (int i = count; i > 0; i--) {
2914                                 dest--;
2915                                 src--;
2916                                 *dest = *src;
2917                         }       
2918                 }
2919
2920                 internal static unsafe void CharCopy (String target, int targetIndex, String source, int sourceIndex, int count)
2921                 {
2922                         fixed (char* dest = target, src = source)
2923                                 CharCopy (dest + targetIndex, src + sourceIndex, count);
2924                 }
2925
2926                 internal static unsafe void CharCopy (String target, int targetIndex, Char[] source, int sourceIndex, int count)
2927                 {
2928                         fixed (char* dest = target, src = source)
2929                                 CharCopy (dest + targetIndex, src + sourceIndex, count);
2930                 }
2931
2932                 // Use this method if you cannot block copy from left to right (e.g. because you are coping within the same string)
2933                 internal static unsafe void CharCopyReverse (String target, int targetIndex, String source, int sourceIndex, int count)
2934                 {
2935                         fixed (char* dest = target, src = source)
2936                                 CharCopyReverse (dest + targetIndex, src + sourceIndex, count);
2937                 }
2938
2939                 [CLSCompliant (false), MethodImplAttribute (MethodImplOptions.InternalCall)]
2940                 unsafe public extern String (char *value);
2941
2942                 [CLSCompliant (false), MethodImplAttribute (MethodImplOptions.InternalCall)]
2943                 unsafe public extern String (char *value, int startIndex, int length);
2944
2945                 [CLSCompliant (false), MethodImplAttribute (MethodImplOptions.InternalCall)]
2946                 unsafe public extern String (sbyte *value);
2947
2948                 [CLSCompliant (false), MethodImplAttribute (MethodImplOptions.InternalCall)]
2949                 unsafe public extern String (sbyte *value, int startIndex, int length);
2950
2951                 [CLSCompliant (false), MethodImplAttribute (MethodImplOptions.InternalCall)]
2952                 unsafe public extern String (sbyte *value, int startIndex, int length, Encoding enc);
2953
2954                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
2955                 public extern String (char [] val, int startIndex, int length);
2956
2957                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
2958                 public extern String (char [] val);
2959
2960                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
2961                 public extern String (char c, int count);
2962
2963                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
2964                 private extern static string InternalJoin (string separator, string[] value, int sIndex, int count);
2965
2966                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
2967                 private extern String InternalReplace (String oldValue, string newValue, CompareInfo comp);
2968
2969                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
2970                 private extern void InternalCopyTo (int sIndex, char[] dest, int destIndex, int count);
2971
2972                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
2973                 private extern String[] InternalSplit (char[] separator, int count, int options);
2974
2975                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
2976                 private extern String InternalTrim (char[] chars, int typ);
2977
2978                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
2979                 private extern int InternalLastIndexOfAny (char [] anyOf, int sIndex, int count);
2980
2981                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
2982                 private extern String InternalPad (int width, char chr, bool right);
2983
2984                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
2985                 internal extern static String InternalAllocateStr (int length);
2986
2987                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
2988                 internal extern static void InternalStrcpy (String dest, int destPos, String src);
2989
2990                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
2991                 internal extern static void InternalStrcpy (String dest, int destPos, char[] chars);
2992
2993                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
2994                 internal extern static void InternalStrcpy (String dest, int destPos, String src, int sPos, int count);
2995
2996                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
2997                 internal extern static void InternalStrcpy (String dest, int destPos, char[] chars, int sPos, int count);
2998
2999                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
3000                 private extern static string InternalIntern (string str);
3001
3002                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
3003                 private extern static string InternalIsInterned (string str);
3004         }
3005 }