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