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