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-2005 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.
34 using System.Collections;
35 using System.Globalization;
36 using System.Runtime.CompilerServices;
39 using System.Runtime.ConstrainedExecution;
40 using System.Runtime.InteropServices;
48 public sealed class String : IConvertible, ICloneable, IEnumerable, IComparable, IComparable<String>, IEquatable <String>
50 public sealed class String : IConvertible, ICloneable, IEnumerable, IComparable
53 [NonSerialized] private int length;
54 [NonSerialized] private char start_char;
56 private const int COMPARE_CASE = 0;
57 private const int COMPARE_INCASE = 1;
58 private const int COMPARE_ORDINAL = 2;
60 public static readonly String Empty = "";
62 public static unsafe bool Equals (string a, string b)
64 if ((a as object) == (b as object))
67 if (a == null || b == null)
78 fixed (char * s1 = &a.start_char, s2 = &b.start_char) {
79 // it must be one char, because 0 len is done above
84 int * sint1 = (int *) s1, sint2 = (int *) s2;
87 if (*sint1++ != *sint2++)
96 return *(char *) sint1 == *(char *) sint2;
100 public static bool operator == (String a, String b)
102 return Equals (a, b);
105 public static bool operator != (String a, String b)
107 return !Equals (a, b);
111 [ReliabilityContractAttribute (Consistency.WillNotCorruptState, Cer.MayFail)]
113 public override bool Equals (Object obj)
115 return Equals (this, obj as String);
119 [ReliabilityContractAttribute (Consistency.WillNotCorruptState, Cer.MayFail)]
121 public bool Equals (String value)
123 return Equals (this, value);
126 [IndexerName ("Chars")]
127 public extern char this [int index] {
128 [MethodImplAttribute (MethodImplOptions.InternalCall)]
132 public Object Clone ()
137 public TypeCode GetTypeCode ()
139 return TypeCode.String;
142 public void CopyTo (int sourceIndex, char[] destination, int destinationIndex, int count)
144 // LAMESPEC: should I null-terminate?
145 if (destination == null)
146 throw new ArgumentNullException ("destination");
148 if (sourceIndex < 0 || destinationIndex < 0 || count < 0)
149 throw new ArgumentOutOfRangeException ();
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");
158 InternalCopyTo (sourceIndex, destination, destinationIndex, count);
161 public char[] ToCharArray ()
163 return ToCharArray (0, length);
166 public char[] ToCharArray (int startIndex, int length)
169 throw new ArgumentOutOfRangeException ("startIndex", "< 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");
176 char[] tmp = new char [length];
178 InternalCopyTo (startIndex, tmp, 0, length);
183 public String [] Split (params char [] separator)
185 return Split (separator, Int32.MaxValue);
188 public String[] Split (char[] separator, int count)
190 if (separator == null || separator.Length == 0)
191 separator = WhiteChars;
194 throw new ArgumentOutOfRangeException ("count");
197 return new String[0];
200 return new String[1] { ToString() };
202 return InternalSplit (separator, count);
208 public String[] Split (char[] separator, int count, StringSplitOptions options)
210 if (separator == null || separator.Length == 0)
211 return Split (WhiteChars, count, options);
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");
218 bool removeEmpty = (options & StringSplitOptions.RemoveEmptyEntries) == StringSplitOptions.RemoveEmptyEntries;
221 return Split (separator, count);
223 throw new NotImplementedException ();
227 public String[] Split (string[] separator, int count, StringSplitOptions options)
229 if (separator == null || separator.Length == 0)
230 return Split (WhiteChars, count, options);
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");
237 bool removeEmpty = (options & StringSplitOptions.RemoveEmptyEntries) == StringSplitOptions.RemoveEmptyEntries;
239 if (count == 0 || (this == String.Empty && removeEmpty))
240 return new String [0];
242 ArrayList arr = new ArrayList ();
245 while (pos < this.Length) {
247 int matchPos = Int32.MaxValue;
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)
255 int match = IndexOf (sep, pos);
256 if (match > -1 && match < matchPos) {
262 if (matchIndex == -1)
265 if (matchPos == pos && removeEmpty) {
266 pos = matchPos + separator [matchIndex].Length;
269 arr.Add (this.Substring (pos, matchPos - pos));
271 pos = matchPos + separator [matchIndex].Length;
273 if (arr.Count == count - 1) {
280 return new String [] { this };
282 if (removeEmpty && pos == this.Length) {
283 String[] res = new String [arr.Count];
284 arr.CopyTo (0, res, 0, arr.Count);
289 String[] res = new String [arr.Count + 1];
290 arr.CopyTo (0, res, 0, arr.Count);
291 res [arr.Count] = this.Substring (pos);
299 public String[] Split (char[] separator, StringSplitOptions options)
301 return Split (separator, Int32.MaxValue, options);
305 public String[] Split (String[] separator, StringSplitOptions options)
307 return Split (separator, Int32.MaxValue, options);
311 public unsafe String Substring (int startIndex)
313 if (startIndex < 0 || startIndex > this.length)
314 throw new ArgumentOutOfRangeException ("startIndex");
316 int newlen = this.length - startIndex;
317 string tmp = InternalAllocateStr (newlen);
319 fixed (char *dest = tmp, src = this) {
320 memcpy ((byte*)dest, (byte*)(src + startIndex), newlen * 2);
326 public unsafe String Substring (int startIndex, int length)
329 throw new ArgumentOutOfRangeException ("length", "< 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");
339 string tmp = InternalAllocateStr (length);
340 fixed (char *dest = tmp, src = this) {
341 memcpy ((byte*)dest, (byte*)(src + startIndex), length * 2);
347 private static readonly char[] WhiteChars = { (char) 0x9, (char) 0xA, (char) 0xB, (char) 0xC, (char) 0xD,
349 (char) 0x85, (char) 0x1680, (char) 0x2028, (char) 0x2029,
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 };
355 public String Trim ()
357 return InternalTrim (WhiteChars, 0);
360 public String Trim (params char[] trimChars)
362 if (trimChars == null || trimChars.Length == 0)
363 trimChars = WhiteChars;
365 return InternalTrim (trimChars, 0);
368 public String TrimStart (params char[] trimChars)
370 if (trimChars == null || trimChars.Length == 0)
371 trimChars = WhiteChars;
373 return InternalTrim (trimChars, 1);
376 public String TrimEnd (params char[] trimChars)
378 if (trimChars == null || trimChars.Length == 0)
379 trimChars = WhiteChars;
381 return InternalTrim (trimChars, 2);
384 public static int Compare (String strA, String strB)
386 return Compare (strA, strB, false, CultureInfo.CurrentCulture);
389 public static int Compare (String strA, String strB, bool ignoreCase)
391 return Compare (strA, strB, ignoreCase, CultureInfo.CurrentCulture);
394 public static int Compare (String strA, String strB, bool ignoreCase, CultureInfo culture)
397 throw new ArgumentNullException ("culture");
406 else if (strB == null) {
410 CompareOptions compopts;
413 compopts = CompareOptions.IgnoreCase;
415 compopts = CompareOptions.None;
417 return culture.CompareInfo.Compare (strA, strB, compopts);
420 public static int Compare (String strA, int indexA, String strB, int indexB, int length)
422 return Compare (strA, indexA, strB, indexB, length, false, CultureInfo.CurrentCulture);
425 public static int Compare (String strA, int indexA, String strB, int indexB, int length, bool ignoreCase)
427 return Compare (strA, indexA, strB, indexB, length, ignoreCase, CultureInfo.CurrentCulture);
430 public static int Compare (String strA, int indexA, String strB, int indexB, int length, bool ignoreCase, CultureInfo culture)
433 throw new ArgumentNullException ("culture");
435 if ((indexA > strA.Length) || (indexB > strB.Length) || (indexA < 0) || (indexB < 0) || (length < 0))
436 throw new ArgumentOutOfRangeException ();
448 else if (strB == null) {
452 CompareOptions compopts;
455 compopts = CompareOptions.IgnoreCase;
457 compopts = CompareOptions.None;
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)
467 if (length > (strA.Length - indexA)) {
468 len1 = strA.Length - indexA;
471 if (length > (strB.Length - indexB)) {
472 len2 = strB.Length - indexB;
475 return culture.CompareInfo.Compare (strA, indexA, len1, strB, indexB, len2, compopts);
478 public int CompareTo (Object value)
483 if (!(value is String))
484 throw new ArgumentException ();
486 return String.Compare (this, (String) value, false);
489 public int CompareTo (String strB)
494 return Compare (this, strB, false);
497 public static int CompareOrdinal (String strA, String strB)
505 else if (strB == null) {
509 /* Invariant, because that is cheaper to
510 * instantiate (and chances are it already has
513 return CultureInfo.InvariantCulture.CompareInfo.Compare (strA, strB, CompareOptions.Ordinal);
516 public static int CompareOrdinal (String strA, int indexA, String strB, int indexB, int length)
518 if ((indexA > strA.Length) || (indexB > strB.Length) || (indexA < 0) || (indexB < 0) || (length < 0))
519 throw new ArgumentOutOfRangeException ();
527 else if (strB == null) {
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)
539 if (length > (strA.Length - indexA)) {
540 len1 = strA.Length - indexA;
543 if (length > (strB.Length - indexB)) {
544 len2 = strB.Length - indexB;
547 return CultureInfo.InvariantCulture.CompareInfo.Compare (strA, indexA, len1, strB, indexB, len2, CompareOptions.Ordinal);
550 public bool EndsWith (String value)
552 return EndsWith (value, false, CultureInfo.CurrentCulture);
560 bool EndsWith (String value, bool ignoreCase, CultureInfo culture)
562 return (culture.CompareInfo.IsSuffix (this, value,
563 ignoreCase ? CompareOptions.IgnoreCase :
564 CompareOptions.None));
567 public int IndexOfAny (char [] anyOf)
570 throw new ArgumentNullException ("anyOf");
572 return InternalIndexOfAny (anyOf, 0, this.length);
575 public int IndexOfAny (char [] anyOf, int startIndex)
578 throw new ArgumentNullException ("anyOf");
579 if (startIndex < 0 || startIndex > this.length)
580 throw new ArgumentOutOfRangeException ("startIndex");
582 return InternalIndexOfAny (anyOf, startIndex, this.length - startIndex);
585 public int IndexOfAny (char [] anyOf, int startIndex, int count)
588 throw new ArgumentNullException ("anyOf");
590 throw new ArgumentOutOfRangeException ("startIndex", "< 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");
597 return InternalIndexOfAny (anyOf, startIndex, count);
600 public int IndexOf (char value)
602 return IndexOf (value, 0, this.length);
605 public int IndexOf (String value)
607 return IndexOf (value, 0, this.length);
610 public int IndexOf (char value, int startIndex)
612 return IndexOf (value, startIndex, this.length - startIndex);
615 public int IndexOf (String value, int startIndex)
617 return IndexOf (value, startIndex, this.length - startIndex);
620 /* This method is culture-insensitive */
621 public int IndexOf (char value, int startIndex, int count)
624 throw new ArgumentOutOfRangeException ("startIndex", "< 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");
631 if ((startIndex == 0 && this.length == 0) || (startIndex == this.length) || (count == 0))
634 for (int pos = startIndex; pos < startIndex + count; pos++) {
635 if (this[pos] == value)
641 /* But this one is culture-sensitive */
642 public int IndexOf (String value, int startIndex, int count)
645 throw new ArgumentNullException ("value");
647 throw new ArgumentOutOfRangeException ("startIndex", "< 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");
654 if (value.length == 0)
657 if (startIndex == 0 && this.length == 0)
663 return CultureInfo.CurrentCulture.CompareInfo.IndexOf (this, value, startIndex, count);
666 public int LastIndexOfAny (char [] anyOf)
669 throw new ArgumentNullException ("anyOf");
671 return InternalLastIndexOfAny (anyOf, this.length - 1, this.length);
674 public int LastIndexOfAny (char [] anyOf, int startIndex)
677 throw new ArgumentNullException ("anyOf");
679 if (startIndex < 0 || startIndex >= this.length)
680 throw new ArgumentOutOfRangeException ();
682 if (this.length == 0)
685 return InternalLastIndexOfAny (anyOf, startIndex, startIndex + 1);
688 public int LastIndexOfAny (char [] anyOf, int startIndex, int count)
691 throw new ArgumentNullException ("anyOf");
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");
700 if (this.length == 0)
703 return InternalLastIndexOfAny (anyOf, startIndex, count);
706 public int LastIndexOf (char value)
708 if (this.length == 0)
711 return LastIndexOf (value, this.length - 1, this.length);
714 public int LastIndexOf (String value)
716 if (this.length == 0)
717 /* This overload does additional checking */
718 return LastIndexOf (value, 0, 0);
720 return LastIndexOf (value, this.length - 1, this.length);
723 public int LastIndexOf (char value, int startIndex)
725 return LastIndexOf (value, startIndex, startIndex + 1);
728 public int LastIndexOf (String value, int startIndex)
731 throw new ArgumentNullException ("value");
732 int max = startIndex;
733 if (max < this.Length)
735 return LastIndexOf (value, startIndex, max);
738 /* This method is culture-insensitive */
739 public int LastIndexOf (char value, int startIndex, int count)
741 if (startIndex == 0 && this.length == 0)
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");
752 for(int pos = startIndex; pos > startIndex - count; pos--) {
753 if (this [pos] == value)
759 /* But this one is culture-sensitive */
760 public int LastIndexOf (String value, int startIndex, int count)
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");
772 if (value.Length == 0)
775 if (startIndex == 0 && this.length == 0)
778 // This check is needed to match undocumented MS behaviour
779 if (this.length == 0 && value.length > 0)
782 if (value.length > startIndex)
788 if (startIndex == this.Length)
790 return CultureInfo.CurrentCulture.CompareInfo.LastIndexOf (this, value, startIndex, count);
794 public bool Contains (String value)
796 return IndexOf (value) != -1;
799 public static bool IsNullOrEmpty (String value)
801 return (value == null) || (value.Length == 0);
804 public string Remove (int startIndex)
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");
811 return Remove (startIndex, this.length - startIndex);
815 public String PadLeft (int totalWidth)
817 return PadLeft (totalWidth, ' ');
820 public String PadLeft (int totalWidth, char paddingChar)
823 throw new ArgumentOutOfRangeException ("totalWidth", "< 0");
825 if (totalWidth < this.length)
826 return String.Copy (this);
828 return InternalPad (totalWidth, paddingChar, false);
831 public String PadRight (int totalWidth)
833 return PadRight (totalWidth, ' ');
836 public String PadRight (int totalWidth, char paddingChar)
839 throw new ArgumentOutOfRangeException ("totalWidth", "< 0");
841 if (totalWidth < this.length)
842 return String.Copy (this);
844 return InternalPad (totalWidth, paddingChar, true);
847 public bool StartsWith (String value)
849 return StartsWith (value, false, CultureInfo.CurrentCulture);
857 bool StartsWith (String value, bool ignoreCase, CultureInfo culture)
859 return (culture.CompareInfo.IsPrefix (this, value,
860 ignoreCase ? CompareOptions.IgnoreCase :
861 CompareOptions.None));
864 /* This method is culture insensitive */
865 public String Replace (char oldChar, char newChar)
867 return InternalReplace (oldChar, newChar);
870 /* This method is culture sensitive */
871 public String Replace (String oldValue, String newValue)
873 if (oldValue == null)
874 throw new ArgumentNullException ("oldValue");
876 if (oldValue.Length == 0)
877 throw new ArgumentException ("oldValue is the empty string.");
879 if (this.Length == 0)
882 if (newValue == null)
883 newValue = String.Empty;
885 return InternalReplace (oldValue, newValue, CultureInfo.CurrentCulture.CompareInfo);
888 public unsafe String Remove (int startIndex, int count)
891 throw new ArgumentOutOfRangeException ("startIndex", "< 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");
898 String tmp = InternalAllocateStr (this.length - count);
900 fixed (char *dest = tmp, src = this) {
902 memcpy ((byte*)dst, (byte*)src, startIndex * 2);
903 int skip = startIndex + count;
905 memcpy ((byte*)dst, (byte*)(src + skip), (length - skip) * 2);
910 public String ToLower ()
912 return ToLower (CultureInfo.CurrentCulture);
915 public String ToLower (CultureInfo culture)
918 throw new ArgumentNullException ("culture");
920 if (culture.LCID == 0x007F) { // Invariant
921 return ToLowerInvariant ();
923 return culture.TextInfo.ToLower (this);
927 public unsafe String ToLowerInvariant ()
929 internal unsafe String ToLowerInvariant ()
932 string tmp = InternalAllocateStr (length);
933 fixed (char* source = &start_char, dest = tmp) {
935 char* destPtr = (char*)dest;
936 char* sourcePtr = (char*)source;
938 for (int n = 0; n < length; n++) {
939 *destPtr = Char.ToLowerInvariant (*sourcePtr);
947 public String ToUpper ()
949 return ToUpper (CultureInfo.CurrentCulture);
952 public String ToUpper (CultureInfo culture)
955 throw new ArgumentNullException ("culture");
957 if (culture.LCID == 0x007F) { // Invariant
958 return ToUpperInvariant ();
960 return culture.TextInfo.ToUpper (this);
964 public unsafe String ToUpperInvariant ()
966 internal unsafe String ToUpperInvariant ()
969 string tmp = InternalAllocateStr (length);
970 fixed (char* source = &start_char, dest = tmp) {
972 char* destPtr = (char*)dest;
973 char* sourcePtr = (char*)source;
975 for (int n = 0; n < length; n++) {
976 *destPtr = Char.ToUpperInvariant (*sourcePtr);
984 public override String ToString ()
989 public String ToString (IFormatProvider provider)
994 public static String Format (String format, Object arg0)
996 return Format (null, format, new Object[] {arg0});
999 public static String Format (String format, Object arg0, Object arg1)
1001 return Format (null, format, new Object[] {arg0, arg1});
1004 public static String Format (String format, Object arg0, Object arg1, Object arg2)
1006 return Format (null, format, new Object[] {arg0, arg1, arg2});
1009 public static string Format (string format, params object[] args)
1011 return Format (null, format, args);
1014 public static string Format (IFormatProvider provider, string format, params object[] args)
1016 StringBuilder b = new StringBuilder ();
1017 FormatHelper (b, provider, format, args);
1018 return b.ToString ();
1021 internal static void FormatHelper (StringBuilder result, IFormatProvider provider, string format, params object[] args)
1023 if (format == null || args == null)
1024 throw new ArgumentNullException ();
1028 while (ptr < format.length) {
1029 char c = format[ptr ++];
1032 result.Append (format, start, ptr - start - 1);
1034 // check for escaped open bracket
1036 if (format[ptr] == '{') {
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.");
1053 object arg = args[n];
1058 else if (arg is IFormattable)
1059 str = ((IFormattable)arg).ToString (arg_format, provider);
1061 str = arg.ToString ();
1063 // pad formatted string and append to result
1065 if (width > str.length) {
1066 const char padchar = ' ';
1067 int padlen = width - str.length;
1070 result.Append (str);
1071 result.Append (padchar, padlen);
1074 result.Append (padchar, padlen);
1075 result.Append (str);
1079 result.Append (str);
1083 else if (c == '}' && ptr < format.length && format[ptr] == '}') {
1084 result.Append (format, start, ptr - start - 1);
1087 else if (c == '}') {
1088 throw new FormatException ("Input string was not in a correct format.");
1092 if (start < format.length)
1093 result.Append (format, start, format.Length - start);
1096 public unsafe static String Copy (String str)
1099 throw new ArgumentNullException ("str");
1101 int length = str.length;
1103 String tmp = InternalAllocateStr (length);
1105 fixed (char *dest = tmp, src = str) {
1106 memcpy ((byte*)dest, (byte*)src, length * 2);
1112 public static String Concat (Object obj)
1115 return String.Empty;
1117 return obj.ToString ();
1120 public unsafe static String Concat (Object obj1, Object obj2)
1124 s1 = (obj1 != null) ? obj1.ToString () : null;
1125 s2 = (obj2 != null) ? obj2.ToString () : null;
1129 return String.Empty;
1132 } else if (s2 == null)
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);
1141 if (s2.Length != 0) {
1142 fixed (char *dest = tmp, src = s2) {
1143 memcpy ((byte*)(dest + s1.Length), (byte*)src, s2.length * 2);
1150 public static String Concat (Object obj1, Object obj2, Object obj3)
1156 s1 = obj1.ToString ();
1161 s2 = obj2.ToString ();
1166 s3 = obj3.ToString ();
1168 return Concat (s1, s2, s3);
1171 #if ! BOOTSTRAP_WITH_OLDLIB
1172 [CLSCompliant(false)]
1173 public static String Concat (Object obj1, Object obj2, Object obj3,
1174 Object obj4, __arglist)
1176 string s1, s2, s3, s4;
1181 s1 = obj1.ToString ();
1186 s2 = obj2.ToString ();
1191 s3 = obj3.ToString ();
1193 ArgIterator iter = new ArgIterator (__arglist);
1194 int argCount = iter.GetRemainingCount();
1196 StringBuilder sb = new StringBuilder ();
1198 sb.Append (obj4.ToString ());
1200 for (int i = 0; i < argCount; i++) {
1201 TypedReference typedRef = iter.GetNextArg ();
1202 sb.Append (TypedReference.ToObject (typedRef));
1205 s4 = sb.ToString ();
1207 return Concat (s1, s2, s3, s4);
1211 public unsafe static String Concat (String s1, String s2)
1215 return String.Empty;
1222 String tmp = InternalAllocateStr (s1.length + s2.length);
1224 if (s1.Length != 0) {
1225 fixed (char *dest = tmp, src = s1) {
1226 memcpy ((byte*)dest, (byte*)src, s1.length * 2);
1229 if (s2.Length != 0) {
1230 fixed (char *dest = tmp, src = s2) {
1231 memcpy ((byte*)(dest + s1.Length), (byte*)src, s2.length * 2);
1238 public unsafe static String Concat (String s1, String s2, String s3)
1243 return String.Empty;
1262 //return InternalConcat (s1, s2, s3);
1263 String tmp = InternalAllocateStr (s1.length + s2.length + s3.length);
1265 if (s1.Length != 0) {
1266 fixed (char *dest = tmp, src = s1) {
1267 memcpy ((byte*)dest, (byte*)src, s1.length * 2);
1270 if (s2.Length != 0) {
1271 fixed (char *dest = tmp, src = s2) {
1272 memcpy ((byte*)(dest + s1.Length), (byte*)src, s2.length * 2);
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);
1284 public unsafe static String Concat (String s1, String s2, String s3, String s4)
1286 if (s1 == null && s2 == null && s3 == null && s4 == null)
1287 return String.Empty;
1298 String tmp = InternalAllocateStr (s1.length + s2.length + s3.length + s4.length);
1300 if (s1.Length != 0) {
1301 fixed (char *dest = tmp, src = s1) {
1302 memcpy ((byte*)dest, (byte*)src, s1.length * 2);
1305 if (s2.Length != 0) {
1306 fixed (char *dest = tmp, src = s2) {
1307 memcpy ((byte*)(dest + s1.Length), (byte*)src, s2.length * 2);
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);
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);
1324 public static String Concat (params Object[] args)
1327 throw new ArgumentNullException ("args");
1329 int i = args.Length;
1331 return String.Empty;
1333 string [] strings = new string [i];
1336 foreach (object arg in args) {
1338 strings[i] = String.Empty;
1340 strings[i] = arg.ToString ();
1341 len += strings[i].length;
1347 return String.Empty;
1349 return InternalJoin (String.Empty, strings, 0, strings.Length);
1352 public static String Concat (params String[] values)
1355 throw new ArgumentNullException ("values");
1357 return InternalJoin (String.Empty, values, 0, values.Length);
1360 public unsafe String Insert (int startIndex, String value)
1363 throw new ArgumentNullException ("value");
1365 if (startIndex < 0 || startIndex > this.length)
1366 throw new ArgumentOutOfRangeException ();
1368 if (value.Length == 0)
1370 if (this.Length == 0)
1372 String tmp = InternalAllocateStr (this.length + value.length);
1374 fixed (char *dest = tmp, src = this, val = value) {
1376 memcpy ((byte*)dst, (byte*)src, startIndex * 2);
1378 memcpy ((byte*)dst, (byte*)val, value.length * 2);
1379 dst += value.length;
1380 memcpy ((byte*)dst, (byte*)(src + startIndex), (length - startIndex) * 2);
1386 public static string Intern (string str)
1389 throw new ArgumentNullException ("str");
1391 return InternalIntern (str);
1394 public static string IsInterned (string str)
1397 throw new ArgumentNullException ("str");
1399 return InternalIsInterned (str);
1402 public static string Join (string separator, string [] value)
1405 throw new ArgumentNullException ("value");
1407 return Join (separator, value, 0, value.Length);
1410 public static string Join (string separator, string[] value, int startIndex, int count)
1413 throw new ArgumentNullException ("value");
1415 throw new ArgumentOutOfRangeException ("startIndex", "< 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");
1422 if (startIndex == value.Length)
1423 return String.Empty;
1424 if (separator == null)
1425 separator = String.Empty;
1427 return InternalJoin (separator, value, startIndex, count);
1430 bool IConvertible.ToBoolean (IFormatProvider provider)
1432 return Convert.ToBoolean (this, provider);
1435 byte IConvertible.ToByte (IFormatProvider provider)
1437 return Convert.ToByte (this, provider);
1440 char IConvertible.ToChar (IFormatProvider provider)
1442 return Convert.ToChar (this, provider);
1445 DateTime IConvertible.ToDateTime (IFormatProvider provider)
1447 return Convert.ToDateTime (this, provider);
1450 decimal IConvertible.ToDecimal (IFormatProvider provider)
1452 return Convert.ToDecimal (this, provider);
1455 double IConvertible.ToDouble (IFormatProvider provider)
1457 return Convert.ToDouble (this, provider);
1460 short IConvertible.ToInt16 (IFormatProvider provider)
1462 return Convert.ToInt16 (this, provider);
1465 int IConvertible.ToInt32 (IFormatProvider provider)
1467 return Convert.ToInt32 (this, provider);
1470 long IConvertible.ToInt64 (IFormatProvider provider)
1472 return Convert.ToInt64 (this, provider);
1475 sbyte IConvertible.ToSByte (IFormatProvider provider)
1477 return Convert.ToSByte (this, provider);
1480 float IConvertible.ToSingle (IFormatProvider provider)
1482 return Convert.ToSingle (this, provider);
1485 string IConvertible.ToString (IFormatProvider format)
1490 object IConvertible.ToType (Type conversionType, IFormatProvider provider)
1492 return Convert.ToType (this, conversionType, provider);
1495 ushort IConvertible.ToUInt16 (IFormatProvider provider)
1497 return Convert.ToUInt16 (this, provider);
1500 uint IConvertible.ToUInt32 (IFormatProvider provider)
1502 return Convert.ToUInt32 (this, provider);
1505 ulong IConvertible.ToUInt64 (IFormatProvider provider)
1507 return Convert.ToUInt64 (this, provider);
1510 TypeCode IConvertible.GetTypeCode ()
1512 return TypeCode.String;
1521 public CharEnumerator GetEnumerator ()
1523 return new CharEnumerator (this);
1526 IEnumerator IEnumerable.GetEnumerator ()
1528 return new CharEnumerator (this);
1531 private static void ParseFormatSpecifier (string str, ref int ptr, out int n, out int width,
1532 out bool left_align, out string format)
1534 // parses format specifier of form:
1540 // N = argument number (non-negative integer)
1542 n = ParseDecimal (str, ref ptr);
1544 throw new FormatException ("Input string was not in a correct format.");
1546 // M = width (non-negative integer)
1548 if (str[ptr] == ',') {
1549 // White space between ',' and number or sign.
1551 while (Char.IsWhiteSpace (str [ptr]))
1555 format = str.Substring (start, ptr - start);
1557 left_align = (str [ptr] == '-');
1561 width = ParseDecimal (str, ref ptr);
1563 throw new FormatException ("Input string was not in a correct format.");
1571 // F = argument format (string)
1573 if (str[ptr] == ':') {
1575 while (str[ptr] != '}')
1578 format += str.Substring (start, ptr - start);
1583 if (str[ptr ++] != '}')
1584 throw new FormatException ("Input string was not in a correct format.");
1586 catch (IndexOutOfRangeException) {
1587 throw new FormatException ("Input string was not in a correct format.");
1591 private static int ParseDecimal (string str, ref int ptr)
1597 if (c < '0' || '9' < c)
1600 n = n * 10 + c - '0';
1611 internal unsafe void InternalSetChar (int idx, char val)
1613 if ((uint) idx >= (uint) Length)
1614 throw new ArgumentOutOfRangeException ("idx");
1616 fixed (char * pStr = &start_char)
1622 internal unsafe void InternalSetLength (int newLength)
1624 if (newLength > length)
1625 throw new ArgumentOutOfRangeException ("newLength", "newLength as to be <= length");
1629 // zero terminate, we can pass string objects directly via pinvoke
1630 fixed (char * pStr = &start_char) {
1631 pStr [length] = '\0';
1636 [ReliabilityContractAttribute (Consistency.WillNotCorruptState, Cer.MayFail)]
1638 public unsafe override int GetHashCode ()
1640 fixed (char * c = this) {
1642 char * end = cc + length - 1;
1644 for (;cc < end; cc += 2) {
1645 h = (h << 5) - h + *cc;
1646 h = (h << 5) - h + cc [1];
1650 h = (h << 5) - h + *cc;
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)
1667 val = val | (val << 8);
1668 val = val | (val << 16);
1671 int rest = (int)dest & 3;
1679 } while (rest != 0);
1682 ((int*)dest) [0] = val;
1683 ((int*)dest) [1] = val;
1684 ((int*)dest) [2] = val;
1685 ((int*)dest) [3] = val;
1690 ((int*)dest) [0] = val;
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];
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];
1725 ((int*)dest) [0] = ((int*)src) [0];
1731 ((byte*)dest) [0] = ((byte*)src) [0];
1737 static unsafe void memcpy2 (byte *dest, byte *src, int size) {
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];
1748 ((short*)dest) [0] = ((short*)src) [0];
1754 ((byte*)dest) [0] = ((byte*)src) [0];
1756 static unsafe void memcpy1 (byte *dest, byte *src, int size) {
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];
1771 ((byte*)dest) [0] = ((byte*)src) [0];
1772 ((byte*)dest) [1] = ((byte*)src) [1];
1778 ((byte*)dest) [0] = ((byte*)src) [0];
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) {
1792 if (((int)dest & 2) != 0 && ((int)src & 2) != 0 && size >= 2) {
1793 ((short*)dest) [0] = ((short*)src) [0];
1798 if ((((int)dest | (int)src) & 1) != 0) {
1799 memcpy1 (dest, src, size);
1802 if ((((int)dest | (int)src) & 2) != 0) {
1803 memcpy2 (dest, src, size);
1807 memcpy4 (dest, src, size);
1810 [CLSCompliant (false), MethodImplAttribute (MethodImplOptions.InternalCall)]
1811 unsafe public extern String (char *value);
1813 [CLSCompliant (false), MethodImplAttribute (MethodImplOptions.InternalCall)]
1814 unsafe public extern String (char *value, int startIndex, int length);
1816 [CLSCompliant (false), MethodImplAttribute (MethodImplOptions.InternalCall)]
1817 unsafe public extern String (sbyte *value);
1819 [CLSCompliant (false), MethodImplAttribute (MethodImplOptions.InternalCall)]
1820 unsafe public extern String (sbyte *value, int startIndex, int length);
1822 [CLSCompliant (false), MethodImplAttribute (MethodImplOptions.InternalCall)]
1823 unsafe public extern String (sbyte *value, int startIndex, int length, Encoding enc);
1825 [MethodImplAttribute (MethodImplOptions.InternalCall)]
1826 public extern String (char [] val, int startIndex, int length);
1828 [MethodImplAttribute (MethodImplOptions.InternalCall)]
1829 public extern String (char [] val);
1831 [MethodImplAttribute (MethodImplOptions.InternalCall)]
1832 public extern String (char c, int count);
1834 [MethodImplAttribute (MethodImplOptions.InternalCall)]
1835 private extern static string InternalJoin (string separator, string[] value, int sIndex, int count);
1837 [MethodImplAttribute (MethodImplOptions.InternalCall)]
1838 private extern String InternalReplace (char oldChar, char newChar);
1840 [MethodImplAttribute (MethodImplOptions.InternalCall)]
1841 private extern String InternalReplace (String oldValue, string newValue, CompareInfo comp);
1843 [MethodImplAttribute (MethodImplOptions.InternalCall)]
1844 private extern void InternalCopyTo (int sIndex, char[] dest, int destIndex, int count);
1846 [MethodImplAttribute (MethodImplOptions.InternalCall)]
1847 private extern String[] InternalSplit (char[] separator, int count);
1849 [MethodImplAttribute (MethodImplOptions.InternalCall)]
1850 private extern String InternalTrim (char[] chars, int typ);
1852 [MethodImplAttribute (MethodImplOptions.InternalCall)]
1853 private extern int InternalIndexOfAny (char [] arr, int sIndex, int count);
1855 [MethodImplAttribute (MethodImplOptions.InternalCall)]
1856 private extern int InternalLastIndexOfAny (char [] anyOf, int sIndex, int count);
1858 [MethodImplAttribute (MethodImplOptions.InternalCall)]
1859 private extern String InternalPad (int width, char chr, bool right);
1861 [MethodImplAttribute (MethodImplOptions.InternalCall)]
1862 internal extern static String InternalAllocateStr (int length);
1864 [MethodImplAttribute (MethodImplOptions.InternalCall)]
1865 internal extern static void InternalStrcpy (String dest, int destPos, String src);
1867 [MethodImplAttribute (MethodImplOptions.InternalCall)]
1868 internal extern static void InternalStrcpy (String dest, int destPos, char[] chars);
1870 [MethodImplAttribute (MethodImplOptions.InternalCall)]
1871 internal extern static void InternalStrcpy (String dest, int destPos, String src, int sPos, int count);
1873 [MethodImplAttribute (MethodImplOptions.InternalCall)]
1874 internal extern static void InternalStrcpy (String dest, int destPos, char[] chars, int sPos, int count);
1876 [MethodImplAttribute (MethodImplOptions.InternalCall)]
1877 private extern static string InternalIntern (string str);
1879 [MethodImplAttribute (MethodImplOptions.InternalCall)]
1880 private extern static string InternalIsInterned (string str);