6 // Jeffrey Stedfast (fejj@ximian.com)
7 // Dan Lewis (dihlewis@yahoo.co.uk)
8 // Sebastien Pouliot <sebastien@ximian.com>
10 // (C) 2001 Ximian, Inc. http://www.ximian.com
11 // Copyright (C) 2004 Novell (http://www.novell.com)
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:
21 // The above copyright notice and this permission notice shall be
22 // included in all copies or substantial portions of the Software.
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.
35 using System.Collections;
36 using System.Globalization;
37 using System.Runtime.CompilerServices;
42 public sealed class String : IConvertible, ICloneable, IEnumerable,
44 IComparable, IComparable<String>
49 [NonSerialized] private int length;
50 [NonSerialized] private char start_char;
52 private const int COMPARE_CASE = 0;
53 private const int COMPARE_INCASE = 1;
54 private const int COMPARE_ORDINAL = 2;
56 public static readonly String Empty = "";
58 public static unsafe bool Equals (string a, string b)
60 if ((a as object) == (b as object))
63 if (a == null || b == null)
74 fixed (char * s1 = &a.start_char, s2 = &b.start_char) {
75 // it must be one char, because 0 len is done above
80 int * sint1 = (int *) s1, sint2 = (int *) s2;
83 if (*sint1++ != *sint2++)
92 return *(char *) sint1 == *(char *) sint2;
96 public static bool operator == (String a, String b)
101 public static bool operator != (String a, String b)
103 return !Equals (a, b);
106 public override bool Equals (Object obj)
108 return Equals (this, obj as String);
111 public bool Equals (String value)
113 return Equals (this, value);
116 [IndexerName ("Chars")]
117 public extern char this [int index] {
118 [MethodImplAttribute (MethodImplOptions.InternalCall)]
122 public Object Clone ()
127 public TypeCode GetTypeCode ()
129 return TypeCode.String;
132 public void CopyTo (int sourceIndex, char[] destination, int destinationIndex, int count)
134 // LAMESPEC: should I null-terminate?
135 if (destination == null)
136 throw new ArgumentNullException ("destination");
138 if (sourceIndex < 0 || destinationIndex < 0 || count < 0)
139 throw new ArgumentOutOfRangeException ();
141 // re-ordered to avoid possible integer overflow
142 if (sourceIndex > Length - count)
143 throw new ArgumentOutOfRangeException ("sourceIndex + count > Length");
144 // re-ordered to avoid possible integer overflow
145 if (destinationIndex > destination.Length - count)
146 throw new ArgumentOutOfRangeException ("destinationIndex + count > destination.Length");
148 InternalCopyTo (sourceIndex, destination, destinationIndex, count);
151 public char[] ToCharArray ()
153 return ToCharArray (0, length);
156 public char[] ToCharArray (int startIndex, int length)
159 throw new ArgumentOutOfRangeException ("startIndex", "< 0");
161 throw new ArgumentOutOfRangeException ("length", "< 0");
162 // re-ordered to avoid possible integer overflow
163 if (startIndex > this.length - length)
164 throw new ArgumentOutOfRangeException ("startIndex + length > this.length");
166 char[] tmp = new char [length];
168 InternalCopyTo (startIndex, tmp, 0, length);
173 public String [] Split (params char [] separator)
175 return Split (separator, Int32.MaxValue);
178 public String[] Split (char[] separator, int count)
180 if (separator == null || separator.Length == 0)
181 separator = WhiteChars;
184 throw new ArgumentOutOfRangeException ("count");
187 return new String[0];
190 return new String[1] { ToString() };
192 return InternalSplit (separator, count);
195 public String Substring (int startIndex)
197 if (startIndex < 0 || startIndex > this.length)
198 throw new ArgumentOutOfRangeException ("startIndex");
200 string tmp = InternalAllocateStr (this.length - startIndex);
201 InternalStrcpy (tmp, 0, this, startIndex, length - startIndex);
206 public String Substring (int startIndex, int length)
209 throw new ArgumentOutOfRangeException ("length", "< 0");
211 throw new ArgumentOutOfRangeException ("startIndex", "< 0");
212 // re-ordered to avoid possible integer overflow
213 if (startIndex > this.length - length)
214 throw new ArgumentOutOfRangeException ("startIndex + length > this.length");
219 string tmp = InternalAllocateStr (length);
220 InternalStrcpy (tmp, 0, this, startIndex, length);
225 private static readonly char[] WhiteChars = { (char) 0x9, (char) 0xA, (char) 0xB, (char) 0xC, (char) 0xD,
226 (char) 0x20, (char) 0xA0, (char) 0x2000, (char) 0x2001, (char) 0x2002, (char) 0x2003, (char) 0x2004,
227 (char) 0x2005, (char) 0x2006, (char) 0x2007, (char) 0x2008, (char) 0x2009, (char) 0x200A, (char) 0x200B,
228 (char) 0x3000, (char) 0xFEFF };
230 public String Trim (params char[] trimChars)
232 if (trimChars == null || trimChars.Length == 0)
233 trimChars = WhiteChars;
235 return InternalTrim (trimChars, 0);
238 public String TrimStart (params char[] trimChars)
240 if (trimChars == null || trimChars.Length == 0)
241 trimChars = WhiteChars;
243 return InternalTrim (trimChars, 1);
246 public String TrimEnd (params char[] trimChars)
248 if (trimChars == null || trimChars.Length == 0)
249 trimChars = WhiteChars;
251 return InternalTrim (trimChars, 2);
254 public static int Compare (String strA, String strB)
256 return Compare (strA, strB, false, CultureInfo.CurrentCulture);
259 public static int Compare (String strA, String strB, bool ignoreCase)
261 return Compare (strA, strB, ignoreCase, CultureInfo.CurrentCulture);
264 public static int Compare (String strA, String strB, bool ignoreCase, CultureInfo culture)
267 throw new ArgumentNullException ("culture");
276 else if (strB == null) {
280 CompareOptions compopts;
283 compopts = CompareOptions.IgnoreCase;
285 compopts = CompareOptions.None;
287 return culture.CompareInfo.Compare (strA, strB, compopts);
290 public static int Compare (String strA, int indexA, String strB, int indexB, int length)
292 return Compare (strA, indexA, strB, indexB, length, false, CultureInfo.CurrentCulture);
295 public static int Compare (String strA, int indexA, String strB, int indexB, int length, bool ignoreCase)
297 return Compare (strA, indexA, strB, indexB, length, ignoreCase, CultureInfo.CurrentCulture);
300 public static int Compare (String strA, int indexA, String strB, int indexB, int length, bool ignoreCase, CultureInfo culture)
303 throw new ArgumentNullException ("culture");
305 if ((indexA > strA.Length) || (indexB > strB.Length) || (indexA < 0) || (indexB < 0) || (length < 0))
306 throw new ArgumentOutOfRangeException ();
318 else if (strB == null) {
322 CompareOptions compopts;
325 compopts = CompareOptions.IgnoreCase;
327 compopts = CompareOptions.None;
329 /* Need to cap the requested length to the
330 * length of the string, because
331 * CompareInfo.Compare will insist that length
332 * <= (string.Length - offset)
337 if (length > (strA.Length - indexA)) {
338 len1 = strA.Length - indexA;
341 if (length > (strB.Length - indexB)) {
342 len2 = strB.Length - indexB;
345 return culture.CompareInfo.Compare (strA, indexA, len1, strB, indexB, len2, compopts);
348 public int CompareTo (Object value)
353 if (!(value is String))
354 throw new ArgumentException ();
356 return String.Compare (this, (String) value, false);
359 public int CompareTo (String strB)
364 return Compare (this, strB, false);
367 public static int CompareOrdinal (String strA, String strB)
375 else if (strB == null) {
379 /* Invariant, because that is cheaper to
380 * instantiate (and chances are it already has
383 return CultureInfo.InvariantCulture.CompareInfo.Compare (strA, strB, CompareOptions.Ordinal);
386 public static int CompareOrdinal (String strA, int indexA, String strB, int indexB, int length)
388 if ((indexA > strA.Length) || (indexB > strB.Length) || (indexA < 0) || (indexB < 0) || (length < 0))
389 throw new ArgumentOutOfRangeException ();
397 else if (strB == null) {
401 /* Need to cap the requested length to the
402 * length of the string, because
403 * CompareInfo.Compare will insist that length
404 * <= (string.Length - offset)
409 if (length > (strA.Length - indexA)) {
410 len1 = strA.Length - indexA;
413 if (length > (strB.Length - indexB)) {
414 len2 = strB.Length - indexB;
417 return CultureInfo.InvariantCulture.CompareInfo.Compare (strA, indexA, len1, strB, indexB, len2, CompareOptions.Ordinal);
420 public bool EndsWith (String value)
423 throw new ArgumentNullException ("value");
425 if (value == String.Empty)
428 if (value.length > this.length)
431 return (0 == Compare (this, length - value.length, value, 0, value.length));
434 public int IndexOfAny (char [] anyOf)
437 throw new ArgumentNullException ("anyOf");
439 return InternalIndexOfAny (anyOf, 0, this.length);
442 public int IndexOfAny (char [] anyOf, int startIndex)
445 throw new ArgumentNullException ("anyOf");
446 if (startIndex < 0 || startIndex >= this.length)
447 throw new ArgumentOutOfRangeException ("sourceIndex");
449 return InternalIndexOfAny (anyOf, startIndex, this.length - startIndex);
452 public int IndexOfAny (char [] anyOf, int startIndex, int count)
455 throw new ArgumentNullException ("anyOf");
457 throw new ArgumentOutOfRangeException ("startIndex", "< 0");
459 throw new ArgumentOutOfRangeException ("count", "< 0");
460 // re-ordered to avoid possible integer overflow
461 if (startIndex > this.length - count)
462 throw new ArgumentOutOfRangeException ("startIndex + count > this.length");
464 return InternalIndexOfAny (anyOf, startIndex, count);
467 public int IndexOf (char value)
469 return IndexOf (value, 0, this.length);
472 public int IndexOf (String value)
474 return IndexOf (value, 0, this.length);
477 public int IndexOf (char value, int startIndex)
479 return IndexOf (value, startIndex, this.length - startIndex);
482 public int IndexOf (String value, int startIndex)
484 return IndexOf (value, startIndex, this.length - startIndex);
487 /* This method is culture-insensitive */
488 public int IndexOf (char value, int startIndex, int count)
491 throw new ArgumentOutOfRangeException ("startIndex", "< 0");
493 throw new ArgumentOutOfRangeException ("count", "< 0");
494 // re-ordered to avoid possible integer overflow
495 if (startIndex > this.length - count)
496 throw new ArgumentOutOfRangeException ("startIndex + count > this.length");
498 if ((startIndex == 0 && this.length == 0) || (startIndex == this.length) || (count == 0))
501 for (int pos = startIndex; pos < startIndex + count; pos++) {
502 if (this[pos] == value)
508 /* But this one is culture-sensitive */
509 public int IndexOf (String value, int startIndex, int count)
512 throw new ArgumentNullException ("value");
514 throw new ArgumentOutOfRangeException ("startIndex", "< 0");
516 throw new ArgumentOutOfRangeException ("count", "< 0");
517 // re-ordered to avoid possible integer overflow
518 if (startIndex > this.length - count)
519 throw new ArgumentOutOfRangeException ("startIndex + count > this.length");
521 if (value.length == 0)
524 if (startIndex == 0 && this.length == 0)
530 return CultureInfo.CurrentCulture.CompareInfo.IndexOf (this, value, startIndex, count);
533 public int LastIndexOfAny (char [] anyOf)
536 throw new ArgumentNullException ("anyOf");
538 return InternalLastIndexOfAny (anyOf, this.length - 1, this.length);
541 public int LastIndexOfAny (char [] anyOf, int startIndex)
544 throw new ArgumentNullException ("anyOf");
546 if (startIndex < 0 || startIndex > this.length)
547 throw new ArgumentOutOfRangeException ();
549 if (this.length == 0)
552 return InternalLastIndexOfAny (anyOf, startIndex, startIndex + 1);
555 public int LastIndexOfAny (char [] anyOf, int startIndex, int count)
558 throw new ArgumentNullException ("anyOf");
560 if ((startIndex < 0) || (startIndex > this.Length))
561 throw new ArgumentOutOfRangeException ("startIndex", "< 0 || > this.Length");
562 if ((count < 0) || (count > this.Length))
563 throw new ArgumentOutOfRangeException ("count", "< 0 || > this.Length");
564 if (startIndex - count + 1 < 0)
565 throw new ArgumentOutOfRangeException ("startIndex - count + 1 < 0");
567 if (this.length == 0)
570 return InternalLastIndexOfAny (anyOf, startIndex, count);
573 public int LastIndexOf (char value)
575 if (this.length == 0)
578 return LastIndexOf (value, this.length - 1, this.length);
581 public int LastIndexOf (String value)
583 if (this.length == 0)
584 /* This overload does additional checking */
585 return LastIndexOf (value, 0, 0);
587 return LastIndexOf (value, this.length - 1, this.length);
590 public int LastIndexOf (char value, int startIndex)
592 return LastIndexOf (value, startIndex, startIndex + 1);
595 public int LastIndexOf (String value, int startIndex)
598 throw new ArgumentNullException ("value");
599 int max = startIndex;
600 if (max < this.Length)
602 return LastIndexOf (value, startIndex, max);
605 /* This method is culture-insensitive */
606 public int LastIndexOf (char value, int startIndex, int count)
608 if (startIndex == 0 && this.length == 0)
611 // >= for char (> for string)
612 if ((startIndex < 0) || (startIndex >= this.Length))
613 throw new ArgumentOutOfRangeException ("startIndex", "< 0 || >= this.Length");
614 if ((count < 0) || (count > this.Length))
615 throw new ArgumentOutOfRangeException ("count", "< 0 || > this.Length");
616 if (startIndex - count + 1 < 0)
617 throw new ArgumentOutOfRangeException ("startIndex - count + 1 < 0");
619 for(int pos = startIndex; pos > startIndex - count; pos--) {
620 if (this [pos] == value)
626 /* But this one is culture-sensitive */
627 public int LastIndexOf (String value, int startIndex, int count)
630 throw new ArgumentNullException ("value");
631 // -1 > startIndex > for string (0 > startIndex >= for char)
632 if ((startIndex < -1) || (startIndex > this.Length))
633 throw new ArgumentOutOfRangeException ("startIndex", "< 0 || > this.Length");
634 if ((count < 0) || (count > this.Length))
635 throw new ArgumentOutOfRangeException ("count", "< 0 || > this.Length");
636 if (startIndex - count + 1 < 0)
637 throw new ArgumentOutOfRangeException ("startIndex - count + 1 < 0");
639 if (value == String.Empty)
642 if (startIndex == 0 && this.length == 0)
645 // This check is needed to match undocumented MS behaviour
646 if (this.length == 0 && value.length > 0)
649 if (value.length > startIndex)
655 if (startIndex == this.Length)
657 return CultureInfo.CurrentCulture.CompareInfo.LastIndexOf (this, value, startIndex, count);
660 public String PadLeft (int totalWidth)
662 return PadLeft (totalWidth, ' ');
665 public String PadLeft (int totalWidth, char paddingChar)
668 throw new ArgumentOutOfRangeException ("totalWidth", "< 0");
670 if (totalWidth < this.length)
671 return String.Copy (this);
673 return InternalPad (totalWidth, paddingChar, false);
676 public String PadRight (int totalWidth)
678 return PadRight (totalWidth, ' ');
681 public String PadRight (int totalWidth, char paddingChar)
684 throw new ArgumentOutOfRangeException ("totalWidth", "< 0");
686 if (totalWidth < this.length)
687 return String.Copy (this);
689 return InternalPad (totalWidth, paddingChar, true);
692 public bool StartsWith (String value)
695 throw new ArgumentNullException ("value");
697 if (value == String.Empty)
700 if (this.length < value.length)
703 return (0 == Compare (this, 0, value, 0 , value.length));
706 /* This method is culture insensitive */
707 public String Replace (char oldChar, char newChar)
709 return InternalReplace (oldChar, newChar);
712 /* This method is culture sensitive */
713 public String Replace (String oldValue, String newValue)
715 if (oldValue == null)
716 throw new ArgumentNullException ("oldValue");
718 if (oldValue == String.Empty)
719 throw new ArgumentException ("oldValue is the empty string.");
721 if (this == String.Empty)
724 if (oldValue.Length == 0) {
728 if (newValue == null)
729 newValue = String.Empty;
731 return InternalReplace (oldValue, newValue, CultureInfo.CurrentCulture.CompareInfo);
734 public String Remove (int startIndex, int count)
737 throw new ArgumentOutOfRangeException ("startIndex", "< 0");
739 throw new ArgumentOutOfRangeException ("count", "< 0");
740 // re-ordered to avoid possible integer overflow
741 if (startIndex > this.length - count)
742 throw new ArgumentOutOfRangeException ("startIndex + count > this.length");
744 return InternalRemove (startIndex, count);
747 public String ToLower ()
749 // CurrentCulture can never be invariant or null
750 return InternalToLower (CultureInfo.CurrentCulture);
753 public String ToLower (CultureInfo culture)
756 throw new ArgumentNullException ("culture");
758 if (culture.LCID == 0x007F) { // Invariant
759 return ToLowerInvariant ();
761 return InternalToLower (culture);
764 internal unsafe String ToLowerInvariant ()
766 string tmp = InternalAllocateStr (length);
767 fixed (char* source = &start_char, dest = tmp) {
769 char* destPtr = (char*)dest;
770 char* sourcePtr = (char*)source;
772 for (int n = 0; n < length; n++) {
773 *destPtr = Char.ToLowerInvariant (*sourcePtr);
781 public String ToUpper ()
783 // CurrentCulture can never be invariant or null
784 return InternalToUpper (CultureInfo.CurrentCulture);
787 public String ToUpper (CultureInfo culture)
790 throw new ArgumentNullException ("culture");
792 if (culture.LCID == 0x007F) { // Invariant
793 return ToUpperInvariant ();
795 return InternalToUpper (culture);
798 internal unsafe String ToUpperInvariant ()
800 string tmp = InternalAllocateStr (length);
801 fixed (char* source = &start_char, dest = tmp) {
803 char* destPtr = (char*)dest;
804 char* sourcePtr = (char*)source;
806 for (int n = 0; n < length; n++) {
807 *destPtr = Char.ToUpperInvariant (*sourcePtr);
815 public override String ToString ()
820 public String ToString (IFormatProvider provider)
825 public String Trim ()
830 public static String Format (String format, Object arg0)
832 return Format (null, format, new Object[] {arg0});
835 public static String Format (String format, Object arg0, Object arg1)
837 return Format (null, format, new Object[] {arg0, arg1});
840 public static String Format (String format, Object arg0, Object arg1, Object arg2)
842 return Format (null, format, new Object[] {arg0, arg1, arg2});
845 public static string Format (string format, params object[] args)
847 return Format (null, format, args);
850 public static string Format (IFormatProvider provider, string format, params object[] args)
852 StringBuilder b = new StringBuilder ();
853 FormatHelper (b, provider, format, args);
854 return b.ToString ();
857 internal static void FormatHelper (StringBuilder result, IFormatProvider provider, string format, params object[] args)
859 if (format == null || args == null)
860 throw new ArgumentNullException ();
864 while (ptr < format.length) {
865 char c = format[ptr ++];
868 result.Append (format, start, ptr - start - 1);
870 // check for escaped open bracket
872 if (format[ptr] == '{') {
883 ParseFormatSpecifier (format, ref ptr, out n, out width, out left_align, out arg_format);
884 if (n >= args.Length)
885 throw new FormatException ("Index (zero based) must be greater than or equal to zero and less than the size of the argument list.");
889 object arg = args[n];
894 else if (arg is IFormattable)
895 str = ((IFormattable)arg).ToString (arg_format, provider);
897 str = arg.ToString ();
899 // pad formatted string and append to result
901 if (width > str.length) {
902 string pad = new String (' ', width - str.length);
918 else if (c == '}' && ptr < format.length && format[ptr] == '}') {
919 result.Append (format, start, ptr - start - 1);
923 throw new FormatException ("Input string was not in a correct format.");
927 if (start < format.length)
928 result.Append (format.Substring (start));
931 public static String Copy (String str)
934 throw new ArgumentNullException ("str");
936 int length = str.length;
938 String tmp = InternalAllocateStr (length);
939 InternalStrcpy (tmp, 0, str);
943 public static String Concat (Object obj)
948 return obj.ToString ();
951 public static String Concat (Object obj1, Object obj2)
955 s1 = (obj1 != null) ? obj1.ToString () : null;
956 s2 = (obj2 != null) ? obj2.ToString () : null;
963 } else if (s2 == null)
966 String tmp = InternalAllocateStr (s1.Length + s2.Length);
967 InternalStrcpy (tmp, 0, s1);
968 InternalStrcpy (tmp, s1.length, s2);
973 public static String Concat (Object obj1, Object obj2, Object obj3)
979 s1 = obj1.ToString ();
984 s2 = obj2.ToString ();
989 s3 = obj3.ToString ();
991 return Concat (s1, s2, s3);
994 #if ! BOOTSTRAP_WITH_OLDLIB
995 [CLSCompliant(false)]
996 public static String Concat (Object obj1, Object obj2, Object obj3,
997 Object obj4, __arglist)
999 string s1, s2, s3, s4;
1004 s1 = obj1.ToString ();
1009 s2 = obj2.ToString ();
1014 s3 = obj3.ToString ();
1016 ArgIterator iter = new ArgIterator (__arglist);
1017 int argCount = iter.GetRemainingCount();
1019 StringBuilder sb = new StringBuilder ();
1021 sb.Append (obj4.ToString ());
1023 for (int i = 0; i < argCount; i++) {
1024 TypedReference typedRef = iter.GetNextArg ();
1025 sb.Append (TypedReference.ToObject (typedRef));
1028 s4 = sb.ToString ();
1030 return Concat (s1, s2, s3, s4);
1034 public static String Concat (String s1, String s2)
1038 return String.Empty;
1045 String tmp = InternalAllocateStr (s1.length + s2.length);
1047 InternalStrcpy (tmp, 0, s1);
1048 InternalStrcpy (tmp, s1.length, s2);
1053 public static String Concat (String s1, String s2, String s3)
1058 return String.Empty;
1077 //return InternalConcat (s1, s2, s3);
1078 String tmp = InternalAllocateStr (s1.length + s2.length + s3.length);
1080 InternalStrcpy (tmp, 0, s1);
1081 InternalStrcpy (tmp, s1.length, s2);
1082 InternalStrcpy (tmp, s1.length + s2.length, s3);
1087 public static String Concat (String s1, String s2, String s3, String s4)
1089 if (s1 == null && s2 == null && s3 == null && s4 == null)
1090 return String.Empty;
1101 String tmp = InternalAllocateStr (s1.length + s2.length + s3.length + s4.length);
1103 InternalStrcpy (tmp, 0, s1);
1104 InternalStrcpy (tmp, s1.length, s2);
1105 InternalStrcpy (tmp, s1.length + s2.length, s3);
1106 InternalStrcpy (tmp, s1.length + s2.length + s3.length, s4);
1111 public static String Concat (params Object[] args)
1114 throw new ArgumentNullException ("args");
1116 int i = args.Length;
1118 return String.Empty;
1120 string [] strings = new string [i];
1123 foreach (object arg in args) {
1125 strings[i] = String.Empty;
1127 strings[i] = arg.ToString ();
1128 len += strings[i].length;
1134 return String.Empty;
1136 return InternalJoin (String.Empty, strings, 0, strings.Length);
1139 public static String Concat (params String[] values)
1142 throw new ArgumentNullException ("values");
1144 return InternalJoin (String.Empty, values, 0, values.Length);
1147 public String Insert (int startIndex, String value)
1150 throw new ArgumentNullException ("value");
1152 if (startIndex < 0 || startIndex > this.length)
1153 throw new ArgumentOutOfRangeException ();
1155 return InternalInsert (startIndex, value);
1159 public static string Intern (string str)
1162 throw new ArgumentNullException ("str");
1164 return InternalIntern (str);
1167 public static string IsInterned (string str)
1170 throw new ArgumentNullException ("str");
1172 return InternalIsInterned (str);
1175 public static string Join (string separator, string [] value)
1178 throw new ArgumentNullException ("value");
1180 return Join (separator, value, 0, value.Length);
1183 public static string Join (string separator, string[] value, int startIndex, int count)
1186 throw new ArgumentNullException ("value");
1188 throw new ArgumentOutOfRangeException ("startIndex", "< 0");
1190 throw new ArgumentOutOfRangeException ("count", "< 0");
1191 // re-ordered to avoid possible integer overflow
1192 if (startIndex > value.Length - count)
1193 throw new ArgumentOutOfRangeException ("startIndex + count > value.length");
1195 if (startIndex == value.Length)
1196 return String.Empty;
1197 if (separator == null)
1198 separator = String.Empty;
1200 return InternalJoin (separator, value, startIndex, count);
1203 bool IConvertible.ToBoolean (IFormatProvider provider)
1205 return Convert.ToBoolean (this, provider);
1208 byte IConvertible.ToByte (IFormatProvider provider)
1210 return Convert.ToByte (this, provider);
1213 char IConvertible.ToChar (IFormatProvider provider)
1215 return Convert.ToChar (this, provider);
1218 DateTime IConvertible.ToDateTime (IFormatProvider provider)
1220 return Convert.ToDateTime (this, provider);
1223 decimal IConvertible.ToDecimal (IFormatProvider provider)
1225 return Convert.ToDecimal (this, provider);
1228 double IConvertible.ToDouble (IFormatProvider provider)
1230 return Convert.ToDouble (this, provider);
1233 short IConvertible.ToInt16 (IFormatProvider provider)
1235 return Convert.ToInt16 (this, provider);
1238 int IConvertible.ToInt32 (IFormatProvider provider)
1240 return Convert.ToInt32 (this, provider);
1243 long IConvertible.ToInt64 (IFormatProvider provider)
1245 return Convert.ToInt64 (this, provider);
1248 sbyte IConvertible.ToSByte (IFormatProvider provider)
1250 return Convert.ToSByte (this, provider);
1253 float IConvertible.ToSingle (IFormatProvider provider)
1255 return Convert.ToSingle (this, provider);
1258 string IConvertible.ToString (IFormatProvider format)
1263 object IConvertible.ToType (Type conversionType, IFormatProvider provider)
1265 return Convert.ToType (this, conversionType, provider);
1268 ushort IConvertible.ToUInt16 (IFormatProvider provider)
1270 return Convert.ToUInt16 (this, provider);
1273 uint IConvertible.ToUInt32 (IFormatProvider provider)
1275 return Convert.ToUInt32 (this, provider);
1278 ulong IConvertible.ToUInt64 (IFormatProvider provider)
1280 return Convert.ToUInt64 (this, provider);
1283 TypeCode IConvertible.GetTypeCode ()
1285 return TypeCode.String;
1294 public CharEnumerator GetEnumerator ()
1296 return new CharEnumerator (this);
1299 IEnumerator IEnumerable.GetEnumerator ()
1301 return new CharEnumerator (this);
1304 private static void ParseFormatSpecifier (string str, ref int ptr, out int n, out int width,
1305 out bool left_align, out string format)
1307 // parses format specifier of form:
1313 // N = argument number (non-negative integer)
1315 n = ParseDecimal (str, ref ptr);
1317 throw new FormatException ("Input string was not in a correct format.");
1319 // M = width (non-negative integer)
1321 if (str[ptr] == ',') {
1322 // White space between ',' and number or sign.
1324 while (Char.IsWhiteSpace (str [ptr]))
1327 format = str.Substring (start, ptr - start);
1329 left_align = (str [ptr] == '-');
1333 width = ParseDecimal (str, ref ptr);
1335 throw new FormatException ("Input string was not in a correct format.");
1343 // F = argument format (string)
1345 if (str[ptr] == ':') {
1347 while (str[ptr] != '}')
1350 format += str.Substring (start, ptr - start);
1355 if (str[ptr ++] != '}')
1356 throw new FormatException ("Input string was not in a correct format.");
1358 catch (IndexOutOfRangeException) {
1359 throw new FormatException ("Input string was not in a correct format.");
1363 private static int ParseDecimal (string str, ref int ptr)
1369 if (c < '0' || '9' < c)
1372 n = n * 10 + c - '0';
1383 internal unsafe void InternalSetChar (int idx, char val)
1385 if ((uint) idx >= (uint) Length)
1386 throw new ArgumentOutOfRangeException ("idx");
1388 fixed (char * pStr = &start_char)
1394 internal unsafe void InternalSetLength (int newLength)
1396 if (newLength > length)
1397 throw new ArgumentOutOfRangeException ("newLength", "newLength as to be <= length");
1401 // zero terminate, we can pass string objects directly via pinvoke
1402 fixed (char * pStr = &start_char) {
1403 pStr [length] = '\0';
1407 public unsafe override int GetHashCode ()
1409 fixed (char * c = this) {
1411 char * end = cc + length - 1;
1413 for (;cc < end; cc += 2) {
1414 h = (h << 5) - h + *cc;
1415 h = (h << 5) - h + cc [1];
1419 h = (h << 5) - h + *cc;
1424 [CLSCompliant (false), MethodImplAttribute (MethodImplOptions.InternalCall)]
1425 unsafe public extern String (char *value);
1427 [CLSCompliant (false), MethodImplAttribute (MethodImplOptions.InternalCall)]
1428 unsafe public extern String (char *value, int startIndex, int length);
1430 [CLSCompliant (false), MethodImplAttribute (MethodImplOptions.InternalCall)]
1431 unsafe public extern String (sbyte *value);
1433 [CLSCompliant (false), MethodImplAttribute (MethodImplOptions.InternalCall)]
1434 unsafe public extern String (sbyte *value, int startIndex, int length);
1436 [CLSCompliant (false), MethodImplAttribute (MethodImplOptions.InternalCall)]
1437 unsafe public extern String (sbyte *value, int startIndex, int length, Encoding enc);
1439 [MethodImplAttribute (MethodImplOptions.InternalCall)]
1440 public extern String (char [] val, int startIndex, int length);
1442 [MethodImplAttribute (MethodImplOptions.InternalCall)]
1443 public extern String (char [] val);
1445 [MethodImplAttribute (MethodImplOptions.InternalCall)]
1446 public extern String (char c, int count);
1448 [MethodImplAttribute (MethodImplOptions.InternalCall)]
1449 private extern static string InternalJoin (string separator, string[] value, int sIndex, int count);
1451 [MethodImplAttribute (MethodImplOptions.InternalCall)]
1452 private extern String InternalInsert (int sourceIndex, String value);
1454 [MethodImplAttribute (MethodImplOptions.InternalCall)]
1455 private extern String InternalReplace (char oldChar, char newChar);
1457 [MethodImplAttribute (MethodImplOptions.InternalCall)]
1458 private extern String InternalReplace (String oldValue, string newValue, CompareInfo comp);
1460 [MethodImplAttribute (MethodImplOptions.InternalCall)]
1461 private extern String InternalRemove (int sIndex, int count);
1463 [MethodImplAttribute (MethodImplOptions.InternalCall)]
1464 private extern void InternalCopyTo (int sIndex, char[] dest, int destIndex, int count);
1466 [MethodImplAttribute (MethodImplOptions.InternalCall)]
1467 private extern String[] InternalSplit (char[] separator, int count);
1469 [MethodImplAttribute (MethodImplOptions.InternalCall)]
1470 private extern String InternalTrim (char[] chars, int typ);
1472 [MethodImplAttribute (MethodImplOptions.InternalCall)]
1473 private extern int InternalIndexOfAny (char [] arr, int sIndex, int count);
1475 [MethodImplAttribute (MethodImplOptions.InternalCall)]
1476 private extern int InternalLastIndexOfAny (char [] anyOf, int sIndex, int count);
1478 [MethodImplAttribute (MethodImplOptions.InternalCall)]
1479 private extern String InternalPad (int width, char chr, bool right);
1481 [MethodImplAttribute (MethodImplOptions.InternalCall)]
1482 private extern String InternalToLower (CultureInfo culture);
1484 [MethodImplAttribute (MethodImplOptions.InternalCall)]
1485 private extern String InternalToUpper (CultureInfo culture);
1487 [MethodImplAttribute (MethodImplOptions.InternalCall)]
1488 internal extern static String InternalAllocateStr (int length);
1490 [MethodImplAttribute (MethodImplOptions.InternalCall)]
1491 internal extern static void InternalStrcpy (String dest, int destPos, String src);
1493 [MethodImplAttribute (MethodImplOptions.InternalCall)]
1494 internal extern static void InternalStrcpy (String dest, int destPos, char[] chars);
1496 [MethodImplAttribute (MethodImplOptions.InternalCall)]
1497 internal extern static void InternalStrcpy (String dest, int destPos, String src, int sPos, int count);
1499 [MethodImplAttribute (MethodImplOptions.InternalCall)]
1500 internal extern static void InternalStrcpy (String dest, int destPos, char[] chars, int sPos, int count);
1502 [MethodImplAttribute (MethodImplOptions.InternalCall)]
1503 private extern static string InternalIntern (string str);
1505 [MethodImplAttribute (MethodImplOptions.InternalCall)]
1506 private extern static string InternalIsInterned (string str);