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