2005-03-24 Zoltan Varga <vargaz@freemail.hu>
[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                         // CurrentCulture can never be invariant or null
783                         return InternalToLower (CultureInfo.CurrentCulture);
784                 }
785
786                 public String ToLower (CultureInfo culture)
787                 {
788                         if (culture == null)
789                                 throw new ArgumentNullException ("culture");
790
791                         if (culture.LCID == 0x007F) { // Invariant
792                                 return ToLowerInvariant ();
793                         }
794                         return InternalToLower (culture);
795                 }
796
797                 internal unsafe String ToLowerInvariant ()
798                 {
799                         string tmp = InternalAllocateStr (length);
800                         fixed (char* source = &start_char, dest = tmp) {
801
802                                 char* destPtr = (char*)dest;
803                                 char* sourcePtr = (char*)source;
804
805                                 for (int n = 0; n < length; n++) {
806                                         *destPtr = Char.ToLowerInvariant (*sourcePtr);
807                                         sourcePtr++;
808                                         destPtr++;
809                                 }
810                         }
811                         return tmp;
812                 }
813
814                 public String ToUpper ()
815                 {
816                         // CurrentCulture can never be invariant or null
817                         return InternalToUpper (CultureInfo.CurrentCulture);
818                 }
819
820                 public String ToUpper (CultureInfo culture)
821                 {
822                         if (culture == null)
823                                 throw new ArgumentNullException ("culture");
824
825                         if (culture.LCID == 0x007F) { // Invariant
826                                 return ToUpperInvariant ();
827                         }
828                         return InternalToUpper (culture);
829                 }
830
831                 internal unsafe String ToUpperInvariant ()
832                 {
833                         string tmp = InternalAllocateStr (length);
834                         fixed (char* source = &start_char, dest = tmp) {
835
836                                 char* destPtr = (char*)dest;
837                                 char* sourcePtr = (char*)source;
838
839                                 for (int n = 0; n < length; n++) {
840                                         *destPtr = Char.ToUpperInvariant (*sourcePtr);
841                                         sourcePtr++;
842                                         destPtr++;
843                                 }
844                         }
845                         return tmp;
846                 }
847
848                 public override String ToString ()
849                 {
850                         return this;
851                 }
852
853                 public String ToString (IFormatProvider provider)
854                 {
855                         return this;
856                 }
857
858                 public String Trim ()
859                 {
860                         return Trim (null);
861                 }
862
863                 public static String Format (String format, Object arg0)
864                 {
865                         return Format (null, format, new Object[] {arg0});
866                 }
867
868                 public static String Format (String format, Object arg0, Object arg1)
869                 {
870                         return Format (null, format, new Object[] {arg0, arg1});
871                 }
872
873                 public static String Format (String format, Object arg0, Object arg1, Object arg2)
874                 {
875                         return Format (null, format, new Object[] {arg0, arg1, arg2});
876                 }
877
878                 public static string Format (string format, params object[] args)
879                 {
880                         return Format (null, format, args);
881                 }
882         
883                 public static string Format (IFormatProvider provider, string format, params object[] args)
884                 {
885                         StringBuilder b = new StringBuilder ();
886                         FormatHelper (b, provider, format, args);
887                         return b.ToString ();
888                 }
889                 
890                 internal static void FormatHelper (StringBuilder result, IFormatProvider provider, string format, params object[] args)
891                 {
892                         if (format == null || args == null)
893                                 throw new ArgumentNullException ();
894
895                         int ptr = 0;
896                         int start = ptr;
897                         while (ptr < format.length) {
898                                 char c = format[ptr ++];
899
900                                 if (c == '{') {
901                                         result.Append (format, start, ptr - start - 1);
902
903                                         // check for escaped open bracket
904
905                                         if (format[ptr] == '{') {
906                                                 start = ptr ++;
907                                                 continue;
908                                         }
909
910                                         // parse specifier
911                                 
912                                         int n, width;
913                                         bool left_align;
914                                         string arg_format;
915
916                                         ParseFormatSpecifier (format, ref ptr, out n, out width, out left_align, out arg_format);
917                                         if (n >= args.Length)
918                                                 throw new FormatException ("Index (zero based) must be greater than or equal to zero and less than the size of the argument list.");
919
920                                         // format argument
921
922                                         object arg = args[n];
923
924                                         string str;
925                                         if (arg == null)
926                                                 str = "";
927                                         else if (arg is IFormattable)
928                                                 str = ((IFormattable)arg).ToString (arg_format, provider);
929                                         else
930                                                 str = arg.ToString ();
931
932                                         // pad formatted string and append to result
933
934                                         if (width > str.length) {
935                                                 string pad = new String (' ', width - str.length);
936
937                                                 if (left_align) {
938                                                         result.Append (str);
939                                                         result.Append (pad);
940                                                 }
941                                                 else {
942                                                         result.Append (pad);
943                                                         result.Append (str);
944                                                 }
945                                         }
946                                         else
947                                                 result.Append (str);
948
949                                         start = ptr;
950                                 }
951                                 else if (c == '}' && ptr < format.length && format[ptr] == '}') {
952                                         result.Append (format, start, ptr - start - 1);
953                                         start = ptr ++;
954                                 }
955                                 else if (c == '}') {
956                                         throw new FormatException ("Input string was not in a correct format.");
957                                 }
958                         }
959
960                         if (start < format.length)
961                                 result.Append (format.Substring (start));
962                 }
963
964                 public unsafe static String Copy (String str)
965                 {
966                         if (str == null)
967                                 throw new ArgumentNullException ("str");
968
969                         int length = str.length;
970
971                         String tmp = InternalAllocateStr (length);
972                         if (length != 0) {
973                                 fixed (char *dest = tmp, src = str) {
974                                         memcpy ((byte*)dest, (byte*)src, length * 2);
975                                 }
976                         }
977                         return tmp;
978                 }
979
980                 public static String Concat (Object obj)
981                 {
982                         if (obj == null)
983                                 return String.Empty;
984
985                         return obj.ToString ();
986                 }
987
988                 public unsafe static String Concat (Object obj1, Object obj2)
989                 {
990                         string s1, s2;
991
992                         s1 = (obj1 != null) ? obj1.ToString () : null;
993                         s2 = (obj2 != null) ? obj2.ToString () : null;
994                         
995                         if (s1 == null) {
996                                 if (s2 == null)
997                                         return String.Empty;
998                                 else
999                                         return s2;
1000                         } else if (s2 == null)
1001                                 return s1;
1002
1003                         String tmp = InternalAllocateStr (s1.Length + s2.Length);
1004                         if (s1.Length != 0) {
1005                                 fixed (char *dest = tmp, src = s1) {
1006                                         memcpy ((byte*)dest, (byte*)src, s1.length * 2);
1007                                 }
1008                         }
1009                         if (s2.Length != 0) {
1010                                 fixed (char *dest = tmp, src = s2) {
1011                                         memcpy ((byte*)(dest + s1.Length), (byte*)src, s2.length * 2);
1012                                 }
1013                         }
1014
1015                         return tmp;
1016                 }
1017
1018                 public static String Concat (Object obj1, Object obj2, Object obj3)
1019                 {
1020                         string s1, s2, s3;
1021                         if (obj1 == null)
1022                                 s1 = String.Empty;
1023                         else
1024                                 s1 = obj1.ToString ();
1025
1026                         if (obj2 == null)
1027                                 s2 = String.Empty;
1028                         else
1029                                 s2 = obj2.ToString ();
1030
1031                         if (obj3 == null)
1032                                 s3 = String.Empty;
1033                         else
1034                                 s3 = obj3.ToString ();
1035
1036                         return Concat (s1, s2, s3);
1037                 }
1038
1039 #if ! BOOTSTRAP_WITH_OLDLIB
1040                 [CLSCompliant(false)]
1041                 public static String Concat (Object obj1, Object obj2, Object obj3,
1042                                              Object obj4, __arglist)
1043                 {
1044                         string s1, s2, s3, s4;
1045
1046                         if (obj1 == null)
1047                                 s1 = String.Empty;
1048                         else
1049                                 s1 = obj1.ToString ();
1050
1051                         if (obj2 == null)
1052                                 s2 = String.Empty;
1053                         else
1054                                 s2 = obj2.ToString ();
1055
1056                         if (obj3 == null)
1057                                 s3 = String.Empty;
1058                         else
1059                                 s3 = obj3.ToString ();
1060
1061                         ArgIterator iter = new ArgIterator (__arglist);
1062                         int argCount = iter.GetRemainingCount();
1063
1064                         StringBuilder sb = new StringBuilder ();
1065                         if (obj4 != null)
1066                                 sb.Append (obj4.ToString ());
1067
1068                         for (int i = 0; i < argCount; i++) {
1069                                 TypedReference typedRef = iter.GetNextArg ();
1070                                 sb.Append (TypedReference.ToObject (typedRef));
1071                         }
1072
1073                         s4 = sb.ToString ();
1074
1075                         return Concat (s1, s2, s3, s4);                 
1076                 }
1077 #endif
1078
1079                 public unsafe static String Concat (String s1, String s2)
1080                 {
1081                         if (s1 == null) {
1082                                 if (s2 == null)
1083                                         return String.Empty;
1084                                 return s2;
1085                         }
1086
1087                         if (s2 == null)
1088                                 return s1; 
1089
1090                         String tmp = InternalAllocateStr (s1.length + s2.length);
1091
1092                         if (s1.Length != 0) {
1093                                 fixed (char *dest = tmp, src = s1) {
1094                                         memcpy ((byte*)dest, (byte*)src, s1.length * 2);
1095                                 }
1096                         }
1097                         if (s2.Length != 0) {
1098                                 fixed (char *dest = tmp, src = s2) {
1099                                         memcpy ((byte*)(dest + s1.Length), (byte*)src, s2.length * 2);
1100                                 }
1101                         }
1102
1103                         return tmp;
1104                 }
1105
1106                 public unsafe static String Concat (String s1, String s2, String s3)
1107                 {
1108                         if (s1 == null){
1109                                 if (s2 == null){
1110                                         if (s3 == null)
1111                                                 return String.Empty;
1112                                         return s3;
1113                                 } else {
1114                                         if (s3 == null)
1115                                                 return s2;
1116                                 }
1117                                 s1 = String.Empty;
1118                         } else {
1119                                 if (s2 == null){
1120                                         if (s3 == null)
1121                                                 return s1;
1122                                         else
1123                                                 s2 = String.Empty;
1124                                 } else {
1125                                         if (s3 == null)
1126                                                 s3 = String.Empty;
1127                                 }
1128                         }
1129
1130                         //return InternalConcat (s1, s2, s3);
1131                         String tmp = InternalAllocateStr (s1.length + s2.length + s3.length);
1132
1133                         if (s1.Length != 0) {
1134                                 fixed (char *dest = tmp, src = s1) {
1135                                         memcpy ((byte*)dest, (byte*)src, s1.length * 2);
1136                                 }
1137                         }
1138                         if (s2.Length != 0) {
1139                                 fixed (char *dest = tmp, src = s2) {
1140                                         memcpy ((byte*)(dest + s1.Length), (byte*)src, s2.length * 2);
1141                                 }
1142                         }
1143                         if (s3.Length != 0) {
1144                                 fixed (char *dest = tmp, src = s3) {
1145                                         memcpy ((byte*)(dest + s1.Length + s2.Length), (byte*)src, s3.length * 2);
1146                                 }
1147                         }
1148
1149                         return tmp;
1150                 }
1151
1152                 public unsafe static String Concat (String s1, String s2, String s3, String s4)
1153                 {
1154                         if (s1 == null && s2 == null && s3 == null && s4 == null)
1155                                 return String.Empty;
1156
1157                         if (s1 == null)
1158                                 s1 = String.Empty;
1159                         if (s2 == null)
1160                                 s2 = String.Empty;
1161                         if (s3 == null)
1162                                 s3 = String.Empty;
1163                         if (s4 == null)
1164                                 s4 = String.Empty;
1165
1166                         String tmp = InternalAllocateStr (s1.length + s2.length + s3.length + s4.length);
1167
1168                         if (s1.Length != 0) {
1169                                 fixed (char *dest = tmp, src = s1) {
1170                                         memcpy ((byte*)dest, (byte*)src, s1.length * 2);
1171                                 }
1172                         }
1173                         if (s2.Length != 0) {
1174                                 fixed (char *dest = tmp, src = s2) {
1175                                         memcpy ((byte*)(dest + s1.Length), (byte*)src, s2.length * 2);
1176                                 }
1177                         }
1178                         if (s3.Length != 0) {
1179                                 fixed (char *dest = tmp, src = s3) {
1180                                         memcpy ((byte*)(dest + s1.Length + s2.Length), (byte*)src, s3.length * 2);
1181                                 }
1182                         }
1183                         if (s4.Length != 0) {
1184                                 fixed (char *dest = tmp, src = s4) {
1185                                         memcpy ((byte*)(dest + s1.Length + s2.Length + s3.Length), (byte*)src, s4.length * 2);
1186                                 }
1187                         }
1188
1189                         return tmp;
1190                 }
1191
1192                 public static String Concat (params Object[] args)
1193                 {
1194                         if (args == null)
1195                                 throw new ArgumentNullException ("args");
1196
1197                         int i = args.Length;
1198                         if (i == 0)
1199                                 return String.Empty;
1200
1201                         string [] strings = new string [i];
1202                         i = 0;
1203                         int len = 0;
1204                         foreach (object arg in args) {
1205                                 if (arg == null) {
1206                                         strings[i] = String.Empty;
1207                                 } else {
1208                                         strings[i] = arg.ToString ();
1209                                         len += strings[i].length;
1210                                 }
1211                                 i++;
1212                         }
1213
1214                         if (len == 0)
1215                                 return String.Empty;
1216
1217                         return InternalJoin (String.Empty, strings, 0, strings.Length);
1218                 }
1219
1220                 public static String Concat (params String[] values)
1221                 {
1222                         if (values == null)
1223                                 throw new ArgumentNullException ("values");
1224
1225                         return InternalJoin (String.Empty, values, 0, values.Length);
1226                 }
1227
1228                 public unsafe String Insert (int startIndex, String value)
1229                 {
1230                         if (value == null)
1231                                 throw new ArgumentNullException ("value");
1232
1233                         if (startIndex < 0 || startIndex > this.length)
1234                                 throw new ArgumentOutOfRangeException ();
1235
1236                         if (value.Length == 0)
1237                                 return this;
1238                         if (this.Length == 0)
1239                                 return value;
1240                         String tmp = InternalAllocateStr (this.length + value.length);
1241
1242                         fixed (char *dest = tmp, src = this, val = value) {
1243                                 char *dst = dest;
1244                                 memcpy ((byte*)dst, (byte*)src, startIndex * 2);
1245                                 dst += startIndex;
1246                                 memcpy ((byte*)dst, (byte*)val, value.length * 2);
1247                                 dst += value.length;
1248                                 memcpy ((byte*)dst, (byte*)(src + startIndex), (length - startIndex) * 2);
1249                         }
1250                         return tmp;
1251                 }
1252
1253
1254                 public static string Intern (string str)
1255                 {
1256                         if (str == null)
1257                                 throw new ArgumentNullException ("str");
1258
1259                         return InternalIntern (str);
1260                 }
1261
1262                 public static string IsInterned (string str)
1263                 {
1264                         if (str == null)
1265                                 throw new ArgumentNullException ("str");
1266
1267                         return InternalIsInterned (str);
1268                 }
1269         
1270                 public static string Join (string separator, string [] value)
1271                 {
1272                         if (value == null)
1273                                 throw new ArgumentNullException ("value");
1274
1275                         return Join (separator, value, 0, value.Length);
1276                 }
1277
1278                 public static string Join (string separator, string[] value, int startIndex, int count)
1279                 {
1280                         if (value == null)
1281                                 throw new ArgumentNullException ("value");
1282                         if (startIndex < 0)
1283                                 throw new ArgumentOutOfRangeException ("startIndex", "< 0");
1284                         if (count < 0)
1285                                 throw new ArgumentOutOfRangeException ("count", "< 0");
1286                         // re-ordered to avoid possible integer overflow
1287                         if (startIndex > value.Length - count)
1288                                 throw new ArgumentOutOfRangeException ("startIndex + count > value.length");
1289
1290                         if (startIndex == value.Length)
1291                                 return String.Empty;
1292                         if (separator == null)
1293                                 separator = String.Empty;
1294
1295                         return InternalJoin (separator, value, startIndex, count);
1296                 }
1297
1298                 bool IConvertible.ToBoolean (IFormatProvider provider)
1299                 {
1300                         return Convert.ToBoolean (this, provider);
1301                 }
1302
1303                 byte IConvertible.ToByte (IFormatProvider provider)
1304                 {
1305                         return Convert.ToByte (this, provider);
1306                 }
1307
1308                 char IConvertible.ToChar (IFormatProvider provider)
1309                 {
1310                         return Convert.ToChar (this, provider);
1311                 }
1312
1313                 DateTime IConvertible.ToDateTime (IFormatProvider provider)
1314                 {
1315                         return Convert.ToDateTime (this, provider);
1316                 }
1317
1318                 decimal IConvertible.ToDecimal (IFormatProvider provider)
1319                 {
1320                         return Convert.ToDecimal (this, provider);
1321                 }
1322
1323                 double IConvertible.ToDouble (IFormatProvider provider)
1324                 {
1325                         return Convert.ToDouble (this, provider);
1326                 }
1327
1328                 short IConvertible.ToInt16 (IFormatProvider provider)
1329                 {
1330                         return Convert.ToInt16 (this, provider);
1331                 }
1332
1333                 int IConvertible.ToInt32 (IFormatProvider provider)
1334                 {
1335                         return Convert.ToInt32 (this, provider);
1336                 }
1337
1338                 long IConvertible.ToInt64 (IFormatProvider provider)
1339                 {
1340                         return Convert.ToInt64 (this, provider);
1341                 }
1342         
1343                 sbyte IConvertible.ToSByte (IFormatProvider provider)
1344                 {
1345                         return Convert.ToSByte (this, provider);
1346                 }
1347
1348                 float IConvertible.ToSingle (IFormatProvider provider)
1349                 {
1350                         return Convert.ToSingle (this, provider);
1351                 }
1352
1353                 string IConvertible.ToString (IFormatProvider format)
1354                 {
1355                         return this;
1356                 }
1357
1358                 object IConvertible.ToType (Type conversionType, IFormatProvider provider)
1359                 {
1360                         return Convert.ToType (this, conversionType,  provider);
1361                 }
1362
1363                 ushort IConvertible.ToUInt16 (IFormatProvider provider)
1364                 {
1365                         return Convert.ToUInt16 (this, provider);
1366                 }
1367
1368                 uint IConvertible.ToUInt32 (IFormatProvider provider)
1369                 {
1370                         return Convert.ToUInt32 (this, provider);
1371                 }
1372
1373                 ulong IConvertible.ToUInt64 (IFormatProvider provider)
1374                 {
1375                         return Convert.ToUInt64 (this, provider);
1376                 }
1377
1378                 TypeCode IConvertible.GetTypeCode ()
1379                 {
1380                         return TypeCode.String;
1381                 }
1382
1383                 public int Length {
1384                         get {
1385                                 return length;
1386                         }
1387                 }
1388
1389                 public CharEnumerator GetEnumerator ()
1390                 {
1391                         return new CharEnumerator (this);
1392                 }
1393
1394                 IEnumerator IEnumerable.GetEnumerator ()
1395                 {
1396                         return new CharEnumerator (this);
1397                 }
1398
1399                 private static void ParseFormatSpecifier (string str, ref int ptr, out int n, out int width,
1400                                                           out bool left_align, out string format)
1401                 {
1402                         // parses format specifier of form:
1403                         //   N,[\ +[-]M][:F]}
1404                         //
1405                         // where:
1406
1407                         try {
1408                                 // N = argument number (non-negative integer)
1409
1410                                 n = ParseDecimal (str, ref ptr);
1411                                 if (n < 0)
1412                                         throw new FormatException ("Input string was not in a correct format.");
1413
1414                                 // M = width (non-negative integer)
1415
1416                                 if (str[ptr] == ',') {
1417                                         // White space between ',' and number or sign.
1418                                         int start = ++ptr;
1419                                         while (Char.IsWhiteSpace (str [ptr]))
1420                                                 ++ptr;
1421
1422                                         format = str.Substring (start, ptr - start);
1423
1424                                         left_align = (str [ptr] == '-');
1425                                         if (left_align)
1426                                                 ++ ptr;
1427
1428                                         width = ParseDecimal (str, ref ptr);
1429                                         if (width < 0)
1430                                                 throw new FormatException ("Input string was not in a correct format.");
1431                                 }
1432                                 else {
1433                                         width = 0;
1434                                         left_align = false;
1435                                         format = "";
1436                                 }
1437
1438                                 // F = argument format (string)
1439
1440                                 if (str[ptr] == ':') {
1441                                         int start = ++ ptr;
1442                                         while (str[ptr] != '}')
1443                                                 ++ ptr;
1444
1445                                         format += str.Substring (start, ptr - start);
1446                                 }
1447                                 else
1448                                         format = null;
1449
1450                                 if (str[ptr ++] != '}')
1451                                         throw new FormatException ("Input string was not in a correct format.");
1452                         }
1453                         catch (IndexOutOfRangeException) {
1454                                 throw new FormatException ("Input string was not in a correct format.");
1455                         }
1456                 }
1457
1458                 private static int ParseDecimal (string str, ref int ptr)
1459                 {
1460                         int p = ptr;
1461                         int n = 0;
1462                         while (true) {
1463                                 char c = str[p];
1464                                 if (c < '0' || '9' < c)
1465                                         break;
1466
1467                                 n = n * 10 + c - '0';
1468                                 ++ p;
1469                         }
1470
1471                         if (p == ptr)
1472                                 return -1;
1473
1474                         ptr = p;
1475                         return n;
1476                 }
1477
1478                 internal unsafe void InternalSetChar (int idx, char val)
1479                 {
1480                         if ((uint) idx >= (uint) Length)
1481                                 throw new ArgumentOutOfRangeException ("idx");
1482
1483                         fixed (char * pStr = &start_char) 
1484                         {
1485                                 pStr [idx] = val;
1486                         }
1487                 }
1488
1489                 internal unsafe void InternalSetLength (int newLength)
1490                 {
1491                         if (newLength > length)
1492                                 throw new ArgumentOutOfRangeException ("newLength", "newLength as to be <= length");
1493
1494                         length = newLength;
1495
1496                         // zero terminate, we can pass string objects directly via pinvoke
1497                         fixed (char * pStr = &start_char) {
1498                                 pStr [length] = '\0';
1499                         }
1500                 }
1501
1502                 public unsafe override int GetHashCode ()
1503                 {
1504                         fixed (char * c = this) {
1505                                 char * cc = c;
1506                                 char * end = cc + length - 1;
1507                                 int h = 0;
1508                                 for (;cc < end; cc += 2) {
1509                                         h = (h << 5) - h + *cc;
1510                                         h = (h << 5) - h + cc [1];
1511                                 }
1512                                 ++end;
1513                                 if (cc < end)
1514                                         h = (h << 5) - h + *cc;
1515                                 return h;
1516                         }
1517                 }
1518
1519                 /* helpers used by the runtime as well as above or eslewhere in corlib */
1520                 internal static unsafe void memset (byte *dest, int val, int len)
1521                 {
1522                         if (len < 8) {
1523                                 while (len != 0) {
1524                                         *dest = (byte)val;
1525                                         ++dest;
1526                                         --len;
1527                                 }
1528                                 return;
1529                         }
1530                         if (val != 0) {
1531                                 val = val | (val << 8);
1532                                 val = val | (val << 16);
1533                         }
1534                         // align to 4
1535                         int rest = (int)dest & 3;
1536                         if (rest != 0) {
1537                                 rest = 4 - rest;
1538                                 len -= rest;
1539                                 do {
1540                                         *dest = (byte)val;
1541                                         ++dest;
1542                                         --rest;
1543                                 } while (rest != 0);
1544                         }
1545                         while (len >= 16) {
1546                                 ((int*)dest) [0] = val;
1547                                 ((int*)dest) [1] = val;
1548                                 ((int*)dest) [2] = val;
1549                                 ((int*)dest) [3] = val;
1550                                 dest += 16;
1551                                 len -= 16;
1552                         }
1553                         while (len >= 4) {
1554                                 ((int*)dest) [0] = val;
1555                                 dest += 4;
1556                                 len -= 4;
1557                         }
1558                         // tail bytes
1559                         while (len > 0) {
1560                                 *dest = (byte)val;
1561                                 dest++;
1562                                 len--;
1563                         }
1564                 }
1565
1566                 internal static unsafe void memcpy4 (byte *dest, byte *src, int size) {
1567                         /*while (size >= 32) {
1568                                 // using long is better than int and slower than double
1569                                 // FIXME: enable this only on correct alignment or on platforms
1570                                 // that can tolerate unaligned reads/writes of doubles
1571                                 ((double*)dest) [0] = ((double*)src) [0];
1572                                 ((double*)dest) [1] = ((double*)src) [1];
1573                                 ((double*)dest) [2] = ((double*)src) [2];
1574                                 ((double*)dest) [3] = ((double*)src) [3];
1575                                 dest += 32;
1576                                 src += 32;
1577                                 size -= 32;
1578                         }*/
1579                         while (size >= 16) {
1580                                 ((int*)dest) [0] = ((int*)src) [0];
1581                                 ((int*)dest) [1] = ((int*)src) [1];
1582                                 ((int*)dest) [2] = ((int*)src) [2];
1583                                 ((int*)dest) [3] = ((int*)src) [3];
1584                                 dest += 16;
1585                                 src += 16;
1586                                 size -= 16;
1587                         }
1588                         while (size >= 4) {
1589                                 ((int*)dest) [0] = ((int*)src) [0];
1590                                 dest += 4;
1591                                 src += 4;
1592                                 size -= 4;
1593                         }
1594                         while (size > 0) {
1595                                 ((byte*)dest) [0] = ((byte*)src) [0];
1596                                 dest += 1;
1597                                 src += 1;
1598                                 --size;
1599                         }
1600                 }
1601                 static unsafe void memcpy2 (byte *dest, byte *src, int size) {
1602                         while (size >= 8) {
1603                                 ((short*)dest) [0] = ((short*)src) [0];
1604                                 ((short*)dest) [1] = ((short*)src) [1];
1605                                 ((short*)dest) [2] = ((short*)src) [2];
1606                                 ((short*)dest) [3] = ((short*)src) [3];
1607                                 dest += 8;
1608                                 src += 8;
1609                                 size -= 8;
1610                         }
1611                         while (size >= 2) {
1612                                 ((short*)dest) [0] = ((short*)src) [0];
1613                                 dest += 2;
1614                                 src += 2;
1615                                 size -= 2;
1616                         }
1617                         if (size > 0)
1618                                 ((byte*)dest) [0] = ((byte*)src) [0];
1619                 }
1620                 static unsafe void memcpy1 (byte *dest, byte *src, int size) {
1621                         while (size >= 8) {
1622                                 ((byte*)dest) [0] = ((byte*)src) [0];
1623                                 ((byte*)dest) [1] = ((byte*)src) [1];
1624                                 ((byte*)dest) [2] = ((byte*)src) [2];
1625                                 ((byte*)dest) [3] = ((byte*)src) [3];
1626                                 ((byte*)dest) [4] = ((byte*)src) [4];
1627                                 ((byte*)dest) [5] = ((byte*)src) [5];
1628                                 ((byte*)dest) [6] = ((byte*)src) [6];
1629                                 ((byte*)dest) [7] = ((byte*)src) [7];
1630                                 dest += 8;
1631                                 src += 8;
1632                                 size -= 8;
1633                         }
1634                         while (size >= 2) {
1635                                 ((byte*)dest) [0] = ((byte*)src) [0];
1636                                 ((byte*)dest) [1] = ((byte*)src) [1];
1637                                 dest += 2;
1638                                 src += 2;
1639                                 size -= 2;
1640                         }
1641                         if (size > 0)
1642                                 ((byte*)dest) [0] = ((byte*)src) [0];
1643                 }
1644                 static unsafe void memcpy (byte *dest, byte *src, int size) {
1645                         // FIXME: if pointers are not aligned, try to align them
1646                         // so a faster routine can be used. Handle the case where
1647                         // the pointers can't be reduced to have the same alignment
1648                         // (just ignore the issue on x86?)
1649                         if ((((int)dest | (int)src) & 3) != 0) {
1650                                 if (((int)dest & 1) != 0 && ((int)src & 1) != 0 && size >= 1) {
1651                                         dest [0] = src [0];
1652                                         ++dest;
1653                                         ++src;
1654                                         --size;
1655                                 }
1656                                 if (((int)dest & 2) != 0 && ((int)src & 2) != 0 && size >= 2) {
1657                                         ((short*)dest) [0] = ((short*)src) [0];
1658                                         dest += 2;
1659                                         src += 2;
1660                                         size -= 2;
1661                                 }
1662                                 if ((((int)dest | (int)src) & 1) != 0) {
1663                                         memcpy1 (dest, src, size);
1664                                         return;
1665                                 }
1666                                 if ((((int)dest | (int)src) & 2) != 0) {
1667                                         memcpy2 (dest, src, size);
1668                                         return;
1669                                 }
1670                         }
1671                         memcpy4 (dest, src, size);
1672                 }
1673
1674                 [CLSCompliant (false), MethodImplAttribute (MethodImplOptions.InternalCall)]
1675                 unsafe public extern String (char *value);
1676
1677                 [CLSCompliant (false), MethodImplAttribute (MethodImplOptions.InternalCall)]
1678                 unsafe public extern String (char *value, int startIndex, int length);
1679
1680                 [CLSCompliant (false), MethodImplAttribute (MethodImplOptions.InternalCall)]
1681                 unsafe public extern String (sbyte *value);
1682
1683                 [CLSCompliant (false), MethodImplAttribute (MethodImplOptions.InternalCall)]
1684                 unsafe public extern String (sbyte *value, int startIndex, int length);
1685
1686                 [CLSCompliant (false), MethodImplAttribute (MethodImplOptions.InternalCall)]
1687                 unsafe public extern String (sbyte *value, int startIndex, int length, Encoding enc);
1688
1689                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
1690                 public extern String (char [] val, int startIndex, int length);
1691
1692                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
1693                 public extern String (char [] val);
1694
1695                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
1696                 public extern String (char c, int count);
1697
1698                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
1699                 private extern static string InternalJoin (string separator, string[] value, int sIndex, int count);
1700
1701                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
1702                 private extern String InternalReplace (char oldChar, char newChar);
1703
1704                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
1705                 private extern String InternalReplace (String oldValue, string newValue, CompareInfo comp);
1706
1707                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
1708                 private extern void InternalCopyTo (int sIndex, char[] dest, int destIndex, int count);
1709
1710                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
1711                 private extern String[] InternalSplit (char[] separator, int count);
1712
1713                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
1714                 private extern String InternalTrim (char[] chars, int typ);
1715
1716                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
1717                 private extern int InternalIndexOfAny (char [] arr, int sIndex, int count);
1718
1719                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
1720                 private extern int InternalLastIndexOfAny (char [] anyOf, int sIndex, int count);
1721
1722                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
1723                 private extern String InternalPad (int width, char chr, bool right);
1724
1725                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
1726                 private extern String InternalToLower (CultureInfo culture);
1727
1728                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
1729                 private extern String InternalToUpper (CultureInfo culture);
1730
1731                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
1732                 internal extern static String InternalAllocateStr (int length);
1733
1734                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
1735                 internal extern static void InternalStrcpy (String dest, int destPos, String src);
1736
1737                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
1738                 internal extern static void InternalStrcpy (String dest, int destPos, char[] chars);
1739
1740                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
1741                 internal extern static void InternalStrcpy (String dest, int destPos, String src, int sPos, int count);
1742
1743                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
1744                 internal extern static void InternalStrcpy (String dest, int destPos, char[] chars, int sPos, int count);
1745
1746                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
1747                 private extern static string InternalIntern (string str);
1748
1749                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
1750                 private extern static string InternalIsInterned (string str);
1751         }
1752 }