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