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, IComparable, ICloneable, IEnumerable
44 [NonSerialized] private int length;
45 [NonSerialized] private char start_char;
47 private const int COMPARE_CASE = 0;
48 private const int COMPARE_INCASE = 1;
49 private const int COMPARE_ORDINAL = 2;
51 public static readonly String Empty = "";
53 public static unsafe bool Equals (string a, string b)
55 if ((a as object) == (b as object))
58 if (a == null || b == null)
69 fixed (char * s1 = &a.start_char, s2 = &b.start_char) {
70 // it must be one char, because 0 len is done above
75 int * sint1 = (int *) s1, sint2 = (int *) s2;
78 if (*sint1++ != *sint2++)
87 return *(char *) sint1 == *(char *) sint2;
91 public static bool operator == (String a, String b)
96 public static bool operator != (String a, String b)
98 return !Equals (a, b);
101 public override bool Equals (Object obj)
103 return Equals (this, obj as String);
106 public bool Equals (String value)
108 return Equals (this, value);
111 [IndexerName ("Chars")]
112 public extern char this [int index] {
113 [MethodImplAttribute (MethodImplOptions.InternalCall)]
117 public Object Clone ()
122 public TypeCode GetTypeCode ()
124 return TypeCode.String;
127 public void CopyTo (int sourceIndex, char[] destination, int destinationIndex, int count)
129 // LAMESPEC: should I null-terminate?
130 if (destination == null)
131 throw new ArgumentNullException ("destination");
133 if (sourceIndex < 0 || destinationIndex < 0 || count < 0)
134 throw new ArgumentOutOfRangeException ();
136 // re-ordered to avoid possible integer overflow
137 if (sourceIndex > Length - count)
138 throw new ArgumentOutOfRangeException ("sourceIndex + count > Length");
139 // re-ordered to avoid possible integer overflow
140 if (destinationIndex > destination.Length - count)
141 throw new ArgumentOutOfRangeException ("destinationIndex + count > destination.Length");
143 InternalCopyTo (sourceIndex, destination, destinationIndex, count);
146 public char[] ToCharArray ()
148 return ToCharArray (0, length);
151 public char[] ToCharArray (int startIndex, int length)
154 throw new ArgumentOutOfRangeException ("startIndex", "< 0");
156 throw new ArgumentOutOfRangeException ("length", "< 0");
157 // re-ordered to avoid possible integer overflow
158 if (startIndex > this.length - length)
159 throw new ArgumentOutOfRangeException ("startIndex + length > this.length");
161 char[] tmp = new char [length];
163 InternalCopyTo (startIndex, tmp, 0, length);
168 public String [] Split (params char [] separator)
170 return Split (separator, Int32.MaxValue);
173 public String[] Split (char[] separator, int count)
175 if (separator == null || separator.Length == 0)
176 separator = WhiteChars;
179 throw new ArgumentOutOfRangeException ("count");
182 return new String[0];
185 return new String[1] { ToString() };
187 return InternalSplit (separator, count);
190 public String Substring (int startIndex)
192 if (startIndex < 0 || startIndex > this.length)
193 throw new ArgumentOutOfRangeException ("startIndex");
195 string tmp = InternalAllocateStr (this.length - startIndex);
196 InternalStrcpy (tmp, 0, this, startIndex, length - startIndex);
201 public String Substring (int startIndex, int length)
204 throw new ArgumentOutOfRangeException ("length", "< 0");
206 throw new ArgumentOutOfRangeException ("startIndex", "< 0");
207 // re-ordered to avoid possible integer overflow
208 if (startIndex > this.length - length)
209 throw new ArgumentOutOfRangeException ("startIndex + length > this.length");
214 string tmp = InternalAllocateStr (length);
215 InternalStrcpy (tmp, 0, this, startIndex, length);
220 private static readonly char[] WhiteChars = { (char) 0x9, (char) 0xA, (char) 0xB, (char) 0xC, (char) 0xD,
221 (char) 0x20, (char) 0xA0, (char) 0x2000, (char) 0x2001, (char) 0x2002, (char) 0x2003, (char) 0x2004,
222 (char) 0x2005, (char) 0x2006, (char) 0x2007, (char) 0x2008, (char) 0x2009, (char) 0x200A, (char) 0x200B,
223 (char) 0x3000, (char) 0xFEFF };
225 public String Trim (params char[] trimChars)
227 if (trimChars == null || trimChars.Length == 0)
228 trimChars = WhiteChars;
230 return InternalTrim (trimChars, 0);
233 public String TrimStart (params char[] trimChars)
235 if (trimChars == null || trimChars.Length == 0)
236 trimChars = WhiteChars;
238 return InternalTrim (trimChars, 1);
241 public String TrimEnd (params char[] trimChars)
243 if (trimChars == null || trimChars.Length == 0)
244 trimChars = WhiteChars;
246 return InternalTrim (trimChars, 2);
249 public static int Compare (String strA, String strB)
251 return Compare (strA, strB, false, CultureInfo.CurrentCulture);
254 public static int Compare (String strA, String strB, bool ignoreCase)
256 return Compare (strA, strB, ignoreCase, CultureInfo.CurrentCulture);
259 public static int Compare (String strA, String strB, bool ignoreCase, CultureInfo culture)
262 throw new ArgumentNullException ("culture");
271 else if (strB == null) {
275 CompareOptions compopts;
278 compopts = CompareOptions.IgnoreCase;
280 compopts = CompareOptions.None;
282 return culture.CompareInfo.Compare (strA, strB, compopts);
285 public static int Compare (String strA, int indexA, String strB, int indexB, int length)
287 return Compare (strA, indexA, strB, indexB, length, false, CultureInfo.CurrentCulture);
290 public static int Compare (String strA, int indexA, String strB, int indexB, int length, bool ignoreCase)
292 return Compare (strA, indexA, strB, indexB, length, ignoreCase, CultureInfo.CurrentCulture);
295 public static int Compare (String strA, int indexA, String strB, int indexB, int length, bool ignoreCase, CultureInfo culture)
298 throw new ArgumentNullException ("culture");
300 if ((indexA > strA.Length) || (indexB > strB.Length) || (indexA < 0) || (indexB < 0) || (length < 0))
301 throw new ArgumentOutOfRangeException ();
313 else if (strB == null) {
317 CompareOptions compopts;
320 compopts = CompareOptions.IgnoreCase;
322 compopts = CompareOptions.None;
324 /* Need to cap the requested length to the
325 * length of the string, because
326 * CompareInfo.Compare will insist that length
327 * <= (string.Length - offset)
332 if (length > (strA.Length - indexA)) {
333 len1 = strA.Length - indexA;
336 if (length > (strB.Length - indexB)) {
337 len2 = strB.Length - indexB;
340 return culture.CompareInfo.Compare (strA, indexA, len1, strB, indexB, len2, compopts);
343 public int CompareTo (Object value)
348 if (!(value is String))
349 throw new ArgumentException ();
351 return String.Compare (this, (String) value, false);
354 public int CompareTo (String strB)
359 return Compare (this, strB, false);
362 public static int CompareOrdinal (String strA, String strB)
370 else if (strB == null) {
374 /* Invariant, because that is cheaper to
375 * instantiate (and chances are it already has
378 return CultureInfo.InvariantCulture.CompareInfo.Compare (strA, strB, CompareOptions.Ordinal);
381 public static int CompareOrdinal (String strA, int indexA, String strB, int indexB, int length)
383 if ((indexA > strA.Length) || (indexB > strB.Length) || (indexA < 0) || (indexB < 0) || (length < 0))
384 throw new ArgumentOutOfRangeException ();
392 else if (strB == null) {
396 /* Need to cap the requested length to the
397 * length of the string, because
398 * CompareInfo.Compare will insist that length
399 * <= (string.Length - offset)
404 if (length > (strA.Length - indexA)) {
405 len1 = strA.Length - indexA;
408 if (length > (strB.Length - indexB)) {
409 len2 = strB.Length - indexB;
412 return CultureInfo.InvariantCulture.CompareInfo.Compare (strA, indexA, len1, strB, indexB, len2, CompareOptions.Ordinal);
415 public bool EndsWith (String value)
418 throw new ArgumentNullException ("value");
420 if (value == String.Empty)
423 if (value.length > this.length)
426 return (0 == Compare (this, length - value.length, value, 0, value.length));
429 public int IndexOfAny (char [] anyOf)
432 throw new ArgumentNullException ("anyOf");
434 return InternalIndexOfAny (anyOf, 0, this.length);
437 public int IndexOfAny (char [] anyOf, int startIndex)
440 throw new ArgumentNullException ("anyOf");
441 if (startIndex < 0 || startIndex >= this.length)
442 throw new ArgumentOutOfRangeException ("sourceIndex");
444 return InternalIndexOfAny (anyOf, startIndex, this.length - startIndex);
447 public int IndexOfAny (char [] anyOf, int startIndex, int count)
450 throw new ArgumentNullException ("anyOf");
452 throw new ArgumentOutOfRangeException ("startIndex", "< 0");
454 throw new ArgumentOutOfRangeException ("count", "< 0");
455 // re-ordered to avoid possible integer overflow
456 if (startIndex > this.length - count)
457 throw new ArgumentOutOfRangeException ("startIndex + count > this.length");
459 return InternalIndexOfAny (anyOf, startIndex, count);
462 public int IndexOf (char value)
464 return IndexOf (value, 0, this.length);
467 public int IndexOf (String value)
469 return IndexOf (value, 0, this.length);
472 public int IndexOf (char value, int startIndex)
474 return IndexOf (value, startIndex, this.length - startIndex);
477 public int IndexOf (String value, int startIndex)
479 return IndexOf (value, startIndex, this.length - startIndex);
482 /* This method is culture-insensitive */
483 public int IndexOf (char value, int startIndex, int count)
486 throw new ArgumentOutOfRangeException ("startIndex", "< 0");
488 throw new ArgumentOutOfRangeException ("count", "< 0");
489 // re-ordered to avoid possible integer overflow
490 if (startIndex > this.length - count)
491 throw new ArgumentOutOfRangeException ("startIndex + count > this.length");
493 if ((startIndex == 0 && this.length == 0) || (startIndex == this.length) || (count == 0))
496 for (int pos = startIndex; pos < startIndex + count; pos++) {
497 if (this[pos] == value)
503 /* But this one is culture-sensitive */
504 public int IndexOf (String value, int startIndex, int count)
507 throw new ArgumentNullException ("value");
509 throw new ArgumentOutOfRangeException ("startIndex", "< 0");
511 throw new ArgumentOutOfRangeException ("count", "< 0");
512 // re-ordered to avoid possible integer overflow
513 if (startIndex > this.length - count)
514 throw new ArgumentOutOfRangeException ("startIndex + count > this.length");
516 if (value.length == 0)
519 if (startIndex == 0 && this.length == 0)
525 return CultureInfo.CurrentCulture.CompareInfo.IndexOf (this, value, startIndex, count);
528 public int LastIndexOfAny (char [] anyOf)
531 throw new ArgumentNullException ("anyOf");
533 return InternalLastIndexOfAny (anyOf, this.length - 1, this.length);
536 public int LastIndexOfAny (char [] anyOf, int startIndex)
539 throw new ArgumentNullException ("anyOf");
541 if (startIndex < 0 || startIndex > this.length)
542 throw new ArgumentOutOfRangeException ();
544 if (this.length == 0)
547 return InternalLastIndexOfAny (anyOf, startIndex, startIndex + 1);
550 public int LastIndexOfAny (char [] anyOf, int startIndex, int count)
553 throw new ArgumentNullException ("anyOf");
555 if ((startIndex < 0) || (startIndex > this.Length))
556 throw new ArgumentOutOfRangeException ("startIndex", "< 0 || > this.Length");
557 if ((count < 0) || (count > this.Length))
558 throw new ArgumentOutOfRangeException ("count", "< 0 || > this.Length");
559 if (startIndex - count + 1 < 0)
560 throw new ArgumentOutOfRangeException ("startIndex - count + 1 < 0");
562 if (this.length == 0)
565 return InternalLastIndexOfAny (anyOf, startIndex, count);
568 public int LastIndexOf (char value)
570 if (this.length == 0)
573 return LastIndexOf (value, this.length - 1, this.length);
576 public int LastIndexOf (String value)
578 if (this.length == 0)
579 /* This overload does additional checking */
580 return LastIndexOf (value, 0, 0);
582 return LastIndexOf (value, this.length - 1, this.length);
585 public int LastIndexOf (char value, int startIndex)
587 return LastIndexOf (value, startIndex, startIndex + 1);
590 public int LastIndexOf (String value, int startIndex)
593 throw new ArgumentNullException ("value");
594 int max = startIndex;
595 if (max < this.Length)
597 return LastIndexOf (value, startIndex, max);
600 /* This method is culture-insensitive */
601 public int LastIndexOf (char value, int startIndex, int count)
603 if (startIndex == 0 && this.length == 0)
606 // >= for char (> for string)
607 if ((startIndex < 0) || (startIndex >= this.Length))
608 throw new ArgumentOutOfRangeException ("startIndex", "< 0 || >= this.Length");
609 if ((count < 0) || (count > this.Length))
610 throw new ArgumentOutOfRangeException ("count", "< 0 || > this.Length");
611 if (startIndex - count + 1 < 0)
612 throw new ArgumentOutOfRangeException ("startIndex - count + 1 < 0");
614 for(int pos = startIndex; pos > startIndex - count; pos--) {
615 if (this [pos] == value)
621 /* But this one is culture-sensitive */
622 public int LastIndexOf (String value, int startIndex, int count)
625 throw new ArgumentNullException ("value");
626 // -1 > startIndex > for string (0 > startIndex >= for char)
627 if ((startIndex < -1) || (startIndex > this.Length))
628 throw new ArgumentOutOfRangeException ("startIndex", "< 0 || > this.Length");
629 if ((count < 0) || (count > this.Length))
630 throw new ArgumentOutOfRangeException ("count", "< 0 || > this.Length");
631 if (startIndex - count + 1 < 0)
632 throw new ArgumentOutOfRangeException ("startIndex - count + 1 < 0");
634 if (value == String.Empty)
637 if (startIndex == 0 && this.length == 0)
640 // This check is needed to match undocumented MS behaviour
641 if (this.length == 0 && value.length > 0)
644 if (value.length > startIndex)
650 if (startIndex == this.Length)
652 return CultureInfo.CurrentCulture.CompareInfo.LastIndexOf (this, value, startIndex, count);
655 public String PadLeft (int totalWidth)
657 return PadLeft (totalWidth, ' ');
660 public String PadLeft (int totalWidth, char paddingChar)
663 throw new ArgumentOutOfRangeException ("totalWidth", "< 0");
665 if (totalWidth < this.length)
666 return String.Copy (this);
668 return InternalPad (totalWidth, paddingChar, false);
671 public String PadRight (int totalWidth)
673 return PadRight (totalWidth, ' ');
676 public String PadRight (int totalWidth, char paddingChar)
679 throw new ArgumentOutOfRangeException ("totalWidth", "< 0");
681 if (totalWidth < this.length)
682 return String.Copy (this);
684 return InternalPad (totalWidth, paddingChar, true);
687 public bool StartsWith (String value)
690 throw new ArgumentNullException ("value");
692 if (value == String.Empty)
695 if (this.length < value.length)
698 return (0 == Compare (this, 0, value, 0 , value.length));
701 /* This method is culture insensitive */
702 public String Replace (char oldChar, char newChar)
704 return InternalReplace (oldChar, newChar);
707 /* This method is culture sensitive */
708 public String Replace (String oldValue, String newValue)
710 if (oldValue == null)
711 throw new ArgumentNullException ("oldValue");
713 if (oldValue == String.Empty)
714 throw new ArgumentException ("oldValue is the empty string.");
716 if (this == String.Empty)
719 if (oldValue.Length == 0) {
723 if (newValue == null)
724 newValue = String.Empty;
726 return InternalReplace (oldValue, newValue, CultureInfo.CurrentCulture.CompareInfo);
729 public String Remove (int startIndex, int count)
732 throw new ArgumentOutOfRangeException ("startIndex", "< 0");
734 throw new ArgumentOutOfRangeException ("count", "< 0");
735 // re-ordered to avoid possible integer overflow
736 if (startIndex > this.length - count)
737 throw new ArgumentOutOfRangeException ("startIndex + count > this.length");
739 return InternalRemove (startIndex, count);
742 public String ToLower ()
744 // CurrentCulture can never be invariant or null
745 return InternalToLower (CultureInfo.CurrentCulture);
748 public String ToLower (CultureInfo culture)
751 throw new ArgumentNullException ("culture");
753 if (culture.LCID == 0x007F) { // Invariant
754 return ToLowerInvariant ();
756 return InternalToLower (culture);
759 internal unsafe String ToLowerInvariant ()
761 string tmp = InternalAllocateStr (length);
762 fixed (char* source = &start_char, dest = tmp) {
764 char* destPtr = (char*)dest;
765 char* sourcePtr = (char*)source;
767 for (int n = 0; n < length; n++) {
768 *destPtr = Char.ToLowerInvariant (*sourcePtr);
776 public String ToUpper ()
778 // CurrentCulture can never be invariant or null
779 return InternalToUpper (CultureInfo.CurrentCulture);
782 public String ToUpper (CultureInfo culture)
785 throw new ArgumentNullException ("culture");
787 if (culture.LCID == 0x007F) { // Invariant
788 return ToUpperInvariant ();
790 return InternalToUpper (culture);
793 internal unsafe String ToUpperInvariant ()
795 string tmp = InternalAllocateStr (length);
796 fixed (char* source = &start_char, dest = tmp) {
798 char* destPtr = (char*)dest;
799 char* sourcePtr = (char*)source;
801 for (int n = 0; n < length; n++) {
802 *destPtr = Char.ToUpperInvariant (*sourcePtr);
810 public override String ToString ()
815 public String ToString (IFormatProvider provider)
820 public String Trim ()
825 public static String Format (String format, Object arg0)
827 return Format (null, format, new Object[] {arg0});
830 public static String Format (String format, Object arg0, Object arg1)
832 return Format (null, format, new Object[] {arg0, arg1});
835 public static String Format (String format, Object arg0, Object arg1, Object arg2)
837 return Format (null, format, new Object[] {arg0, arg1, arg2});
840 public static string Format (string format, params object[] args)
842 return Format (null, format, args);
845 public static string Format (IFormatProvider provider, string format, params object[] args)
847 StringBuilder b = new StringBuilder ();
848 FormatHelper (b, provider, format, args);
849 return b.ToString ();
852 internal static void FormatHelper (StringBuilder result, IFormatProvider provider, string format, params object[] args)
854 if (format == null || args == null)
855 throw new ArgumentNullException ();
859 while (ptr < format.length) {
860 char c = format[ptr ++];
863 result.Append (format, start, ptr - start - 1);
865 // check for escaped open bracket
867 if (format[ptr] == '{') {
878 ParseFormatSpecifier (format, ref ptr, out n, out width, out left_align, out arg_format);
879 if (n >= args.Length)
880 throw new FormatException ("Index (zero based) must be greater than or equal to zero and less than the size of the argument list.");
884 object arg = args[n];
889 else if (arg is IFormattable)
890 str = ((IFormattable)arg).ToString (arg_format, provider);
892 str = arg.ToString ();
894 // pad formatted string and append to result
896 if (width > str.length) {
897 string pad = new String (' ', width - str.length);
913 else if (c == '}' && ptr < format.length && format[ptr] == '}') {
914 result.Append (format, start, ptr - start - 1);
918 throw new FormatException ("Input string was not in a correct format.");
922 if (start < format.length)
923 result.Append (format.Substring (start));
926 public static String Copy (String str)
929 throw new ArgumentNullException ("str");
931 int length = str.length;
933 String tmp = InternalAllocateStr (length);
934 InternalStrcpy (tmp, 0, str);
938 public static String Concat (Object obj)
943 return obj.ToString ();
946 public static String Concat (Object obj1, Object obj2)
950 s1 = (obj1 != null) ? obj1.ToString () : null;
951 s2 = (obj2 != null) ? obj2.ToString () : null;
958 } else if (s2 == null)
961 String tmp = InternalAllocateStr (s1.Length + s2.Length);
962 InternalStrcpy (tmp, 0, s1);
963 InternalStrcpy (tmp, s1.length, s2);
968 public static String Concat (Object obj1, Object obj2, Object obj3)
974 s1 = obj1.ToString ();
979 s2 = obj2.ToString ();
984 s3 = obj3.ToString ();
986 return Concat (s1, s2, s3);
989 #if ! BOOTSTRAP_WITH_OLDLIB
990 [CLSCompliant(false)]
991 public static String Concat (Object obj1, Object obj2, Object obj3,
992 Object obj4, __arglist)
994 string s1, s2, s3, s4;
999 s1 = obj1.ToString ();
1004 s2 = obj2.ToString ();
1009 s3 = obj3.ToString ();
1011 ArgIterator iter = new ArgIterator (__arglist);
1012 int argCount = iter.GetRemainingCount();
1014 StringBuilder sb = new StringBuilder ();
1016 sb.Append (obj4.ToString ());
1018 for (int i = 0; i < argCount; i++) {
1019 TypedReference typedRef = iter.GetNextArg ();
1020 sb.Append (TypedReference.ToObject (typedRef));
1023 s4 = sb.ToString ();
1025 return Concat (s1, s2, s3, s4);
1029 public static String Concat (String s1, String s2)
1033 return String.Empty;
1040 String tmp = InternalAllocateStr (s1.length + s2.length);
1042 InternalStrcpy (tmp, 0, s1);
1043 InternalStrcpy (tmp, s1.length, s2);
1048 public static String Concat (String s1, String s2, String s3)
1053 return String.Empty;
1072 //return InternalConcat (s1, s2, s3);
1073 String tmp = InternalAllocateStr (s1.length + s2.length + s3.length);
1075 InternalStrcpy (tmp, 0, s1);
1076 InternalStrcpy (tmp, s1.length, s2);
1077 InternalStrcpy (tmp, s1.length + s2.length, s3);
1082 public static String Concat (String s1, String s2, String s3, String s4)
1084 if (s1 == null && s2 == null && s3 == null && s4 == null)
1085 return String.Empty;
1096 String tmp = InternalAllocateStr (s1.length + s2.length + s3.length + s4.length);
1098 InternalStrcpy (tmp, 0, s1);
1099 InternalStrcpy (tmp, s1.length, s2);
1100 InternalStrcpy (tmp, s1.length + s2.length, s3);
1101 InternalStrcpy (tmp, s1.length + s2.length + s3.length, s4);
1106 public static String Concat (params Object[] args)
1109 throw new ArgumentNullException ("args");
1111 int i = args.Length;
1113 return String.Empty;
1115 string [] strings = new string [i];
1118 foreach (object arg in args) {
1120 strings[i] = String.Empty;
1122 strings[i] = arg.ToString ();
1123 len += strings[i].length;
1129 return String.Empty;
1131 return InternalJoin (String.Empty, strings, 0, strings.Length);
1134 public static String Concat (params String[] values)
1137 throw new ArgumentNullException ("values");
1139 return InternalJoin (String.Empty, values, 0, values.Length);
1142 public String Insert (int startIndex, String value)
1145 throw new ArgumentNullException ("value");
1147 if (startIndex < 0 || startIndex > this.length)
1148 throw new ArgumentOutOfRangeException ();
1150 return InternalInsert (startIndex, value);
1154 public static string Intern (string str)
1157 throw new ArgumentNullException ("str");
1159 return InternalIntern (str);
1162 public static string IsInterned (string str)
1165 throw new ArgumentNullException ("str");
1167 return InternalIsInterned (str);
1170 public static string Join (string separator, string [] value)
1173 throw new ArgumentNullException ("value");
1175 return Join (separator, value, 0, value.Length);
1178 public static string Join (string separator, string[] value, int startIndex, int count)
1181 throw new ArgumentNullException ("value");
1183 throw new ArgumentOutOfRangeException ("startIndex", "< 0");
1185 throw new ArgumentOutOfRangeException ("count", "< 0");
1186 // re-ordered to avoid possible integer overflow
1187 if (startIndex > value.Length - count)
1188 throw new ArgumentOutOfRangeException ("startIndex + count > value.length");
1190 if (startIndex == value.Length)
1191 return String.Empty;
1192 if (separator == null)
1193 separator = String.Empty;
1195 return InternalJoin (separator, value, startIndex, count);
1198 bool IConvertible.ToBoolean (IFormatProvider provider)
1200 return Convert.ToBoolean (this, provider);
1203 byte IConvertible.ToByte (IFormatProvider provider)
1205 return Convert.ToByte (this, provider);
1208 char IConvertible.ToChar (IFormatProvider provider)
1210 return Convert.ToChar (this, provider);
1213 DateTime IConvertible.ToDateTime (IFormatProvider provider)
1215 return Convert.ToDateTime (this, provider);
1218 decimal IConvertible.ToDecimal (IFormatProvider provider)
1220 return Convert.ToDecimal (this, provider);
1223 double IConvertible.ToDouble (IFormatProvider provider)
1225 return Convert.ToDouble (this, provider);
1228 short IConvertible.ToInt16 (IFormatProvider provider)
1230 return Convert.ToInt16 (this, provider);
1233 int IConvertible.ToInt32 (IFormatProvider provider)
1235 return Convert.ToInt32 (this, provider);
1238 long IConvertible.ToInt64 (IFormatProvider provider)
1240 return Convert.ToInt64 (this, provider);
1243 sbyte IConvertible.ToSByte (IFormatProvider provider)
1245 return Convert.ToSByte (this, provider);
1248 float IConvertible.ToSingle (IFormatProvider provider)
1250 return Convert.ToSingle (this, provider);
1253 string IConvertible.ToString (IFormatProvider format)
1258 object IConvertible.ToType (Type conversionType, IFormatProvider provider)
1260 return Convert.ToType (this, conversionType, provider);
1263 ushort IConvertible.ToUInt16 (IFormatProvider provider)
1265 return Convert.ToUInt16 (this, provider);
1268 uint IConvertible.ToUInt32 (IFormatProvider provider)
1270 return Convert.ToUInt32 (this, provider);
1273 ulong IConvertible.ToUInt64 (IFormatProvider provider)
1275 return Convert.ToUInt64 (this, provider);
1278 TypeCode IConvertible.GetTypeCode ()
1280 return TypeCode.String;
1289 public CharEnumerator GetEnumerator ()
1291 return new CharEnumerator (this);
1294 IEnumerator IEnumerable.GetEnumerator ()
1296 return new CharEnumerator (this);
1299 private static void ParseFormatSpecifier (string str, ref int ptr, out int n, out int width,
1300 out bool left_align, out string format)
1302 // parses format specifier of form:
1308 // N = argument number (non-negative integer)
1310 n = ParseDecimal (str, ref ptr);
1312 throw new FormatException ("Input string was not in a correct format.");
1314 // M = width (non-negative integer)
1316 if (str[ptr] == ',') {
1317 // White space between ',' and number or sign.
1319 while (Char.IsWhiteSpace (str [ptr]))
1322 format = str.Substring (start, ptr - start);
1324 left_align = (str [ptr] == '-');
1328 width = ParseDecimal (str, ref ptr);
1330 throw new FormatException ("Input string was not in a correct format.");
1338 // F = argument format (string)
1340 if (str[ptr] == ':') {
1342 while (str[ptr] != '}')
1345 format += str.Substring (start, ptr - start);
1350 if (str[ptr ++] != '}')
1351 throw new FormatException ("Input string was not in a correct format.");
1353 catch (IndexOutOfRangeException) {
1354 throw new FormatException ("Input string was not in a correct format.");
1358 private static int ParseDecimal (string str, ref int ptr)
1364 if (c < '0' || '9' < c)
1367 n = n * 10 + c - '0';
1378 internal unsafe void InternalSetChar (int idx, char val)
1380 if ((uint) idx >= (uint) Length)
1381 throw new ArgumentOutOfRangeException ("idx");
1383 fixed (char * pStr = &start_char)
1389 internal unsafe void InternalSetLength (int newLength)
1391 if (newLength > length)
1392 throw new ArgumentOutOfRangeException ("newLength", "newLength as to be <= length");
1396 // zero terminate, we can pass string objects directly via pinvoke
1397 fixed (char * pStr = &start_char) {
1398 pStr [length] = '\0';
1402 [CLSCompliant (false), MethodImplAttribute (MethodImplOptions.InternalCall)]
1403 unsafe public extern String (char *value);
1405 [CLSCompliant (false), MethodImplAttribute (MethodImplOptions.InternalCall)]
1406 unsafe public extern String (char *value, int startIndex, int length);
1408 [CLSCompliant (false), MethodImplAttribute (MethodImplOptions.InternalCall)]
1409 unsafe public extern String (sbyte *value);
1411 [CLSCompliant (false), MethodImplAttribute (MethodImplOptions.InternalCall)]
1412 unsafe public extern String (sbyte *value, int startIndex, int length);
1414 [CLSCompliant (false), MethodImplAttribute (MethodImplOptions.InternalCall)]
1415 unsafe public extern String (sbyte *value, int startIndex, int length, Encoding enc);
1417 [MethodImplAttribute (MethodImplOptions.InternalCall)]
1418 public extern String (char [] val, int startIndex, int length);
1420 [MethodImplAttribute (MethodImplOptions.InternalCall)]
1421 public extern String (char [] val);
1423 [MethodImplAttribute (MethodImplOptions.InternalCall)]
1424 public extern String (char c, int count);
1426 [MethodImplAttribute (MethodImplOptions.InternalCall)]
1427 public extern override int GetHashCode ();
1429 [MethodImplAttribute (MethodImplOptions.InternalCall)]
1430 private extern static string InternalJoin (string separator, string[] value, int sIndex, int count);
1432 [MethodImplAttribute (MethodImplOptions.InternalCall)]
1433 private extern String InternalInsert (int sourceIndex, String value);
1435 [MethodImplAttribute (MethodImplOptions.InternalCall)]
1436 private extern String InternalReplace (char oldChar, char newChar);
1438 [MethodImplAttribute (MethodImplOptions.InternalCall)]
1439 private extern String InternalReplace (String oldValue, string newValue, CompareInfo comp);
1441 [MethodImplAttribute (MethodImplOptions.InternalCall)]
1442 private extern String InternalRemove (int sIndex, int count);
1444 [MethodImplAttribute (MethodImplOptions.InternalCall)]
1445 private extern void InternalCopyTo (int sIndex, char[] dest, int destIndex, int count);
1447 [MethodImplAttribute (MethodImplOptions.InternalCall)]
1448 private extern String[] InternalSplit (char[] separator, int count);
1450 [MethodImplAttribute (MethodImplOptions.InternalCall)]
1451 private extern String InternalTrim (char[] chars, int typ);
1453 [MethodImplAttribute (MethodImplOptions.InternalCall)]
1454 private extern int InternalIndexOfAny (char [] arr, int sIndex, int count);
1456 [MethodImplAttribute (MethodImplOptions.InternalCall)]
1457 private extern int InternalLastIndexOfAny (char [] anyOf, int sIndex, int count);
1459 [MethodImplAttribute (MethodImplOptions.InternalCall)]
1460 private extern String InternalPad (int width, char chr, bool right);
1462 [MethodImplAttribute (MethodImplOptions.InternalCall)]
1463 private extern String InternalToLower (CultureInfo culture);
1465 [MethodImplAttribute (MethodImplOptions.InternalCall)]
1466 private extern String InternalToUpper (CultureInfo culture);
1468 [MethodImplAttribute (MethodImplOptions.InternalCall)]
1469 internal extern static String InternalAllocateStr (int length);
1471 [MethodImplAttribute (MethodImplOptions.InternalCall)]
1472 internal extern static void InternalStrcpy (String dest, int destPos, String src);
1474 [MethodImplAttribute (MethodImplOptions.InternalCall)]
1475 internal extern static void InternalStrcpy (String dest, int destPos, char[] chars);
1477 [MethodImplAttribute (MethodImplOptions.InternalCall)]
1478 internal extern static void InternalStrcpy (String dest, int destPos, String src, int sPos, int count);
1480 [MethodImplAttribute (MethodImplOptions.InternalCall)]
1481 internal extern static void InternalStrcpy (String dest, int destPos, char[] chars, int sPos, int count);
1483 [MethodImplAttribute (MethodImplOptions.InternalCall)]
1484 private extern static string InternalIntern (string str);
1486 [MethodImplAttribute (MethodImplOptions.InternalCall)]
1487 private extern static string InternalIsInterned (string str);