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