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