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