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