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