6 // Jeffrey Stedfast (fejj@ximian.com)
7 // Dan Lewis (dihlewis@yahoo.co.uk)
8 // Sebastien Pouliot <sebastien@ximian.com>
9 // Marek Safar (marek.safar@seznam.cz)
11 // (C) 2001 Ximian, Inc. http://www.ximian.com
12 // Copyright (C) 2004-2005 Novell (http://www.novell.com)
14 // Permission is hereby granted, free of charge, to any person obtaining
15 // a copy of this software and associated documentation files (the
16 // "Software"), to deal in the Software without restriction, including
17 // without limitation the rights to use, copy, modify, merge, publish,
18 // distribute, sublicense, and/or sell copies of the Software, and to
19 // permit persons to whom the Software is furnished to do so, subject to
20 // the following conditions:
22 // The above copyright notice and this permission notice shall be
23 // included in all copies or substantial portions of the Software.
25 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
26 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
27 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
28 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
29 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
30 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
31 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
35 using System.Collections;
36 using System.Globalization;
37 using System.Runtime.CompilerServices;
40 using System.Collections.Generic;
41 using System.Runtime.ConstrainedExecution;
42 using System.Runtime.InteropServices;
43 using Mono.Globalization.Unicode;
51 public sealed class String : IConvertible, ICloneable, IEnumerable, IComparable, IComparable<String>, IEquatable <String>, IEnumerable<char>
53 public sealed class String : IConvertible, ICloneable, IEnumerable, IComparable
56 [NonSerialized] private int length;
57 [NonSerialized] private char start_char;
59 private const int COMPARE_CASE = 0;
60 private const int COMPARE_INCASE = 1;
61 private const int COMPARE_ORDINAL = 2;
63 public static readonly String Empty = "";
65 public static unsafe bool Equals (string a, string b)
67 if ((a as object) == (b as object))
70 if (a == null || b == null)
78 fixed (char* s1 = &a.start_char, s2 = &b.start_char) {
83 if (((int*)s1_ptr)[0] != ((int*)s2_ptr)[0] ||
84 ((int*)s1_ptr)[1] != ((int*)s2_ptr)[1] ||
85 ((int*)s1_ptr)[2] != ((int*)s2_ptr)[2] ||
86 ((int*)s1_ptr)[3] != ((int*)s2_ptr)[3])
95 if (((int*)s1_ptr)[0] != ((int*)s2_ptr)[0] ||
96 ((int*)s1_ptr)[1] != ((int*)s2_ptr)[1])
105 if (((int*)s1_ptr)[0] != ((int*)s2_ptr)[0])
113 return len == 0 || *s1_ptr == *s2_ptr;
117 public static bool operator == (String a, String b)
119 return Equals (a, b);
122 public static bool operator != (String a, String b)
124 return !Equals (a, b);
128 [ReliabilityContractAttribute (Consistency.WillNotCorruptState, Cer.MayFail)]
130 public override bool Equals (Object obj)
132 return Equals (this, obj as String);
136 [ReliabilityContractAttribute (Consistency.WillNotCorruptState, Cer.MayFail)]
138 public bool Equals (String value)
140 return Equals (this, value);
143 [IndexerName ("Chars")]
144 public extern char this [int index] {
145 [MethodImplAttribute (MethodImplOptions.InternalCall)]
149 public Object Clone ()
154 public TypeCode GetTypeCode ()
156 return TypeCode.String;
159 public void CopyTo (int sourceIndex, char[] destination, int destinationIndex, int count)
161 // LAMESPEC: should I null-terminate?
162 if (destination == null)
163 throw new ArgumentNullException ("destination");
165 if (sourceIndex < 0 || destinationIndex < 0 || count < 0)
166 throw new ArgumentOutOfRangeException ();
168 // re-ordered to avoid possible integer overflow
169 if (sourceIndex > Length - count)
170 throw new ArgumentOutOfRangeException ("sourceIndex + count > Length");
171 // re-ordered to avoid possible integer overflow
172 if (destinationIndex > destination.Length - count)
173 throw new ArgumentOutOfRangeException ("destinationIndex + count > destination.Length");
175 InternalCopyTo (sourceIndex, destination, destinationIndex, count);
178 public char[] ToCharArray ()
180 return ToCharArray (0, length);
183 public char[] ToCharArray (int startIndex, int length)
186 throw new ArgumentOutOfRangeException ("startIndex", "< 0");
188 throw new ArgumentOutOfRangeException ("length", "< 0");
189 // re-ordered to avoid possible integer overflow
190 if (startIndex > this.length - length)
191 throw new ArgumentOutOfRangeException ("startIndex + length > this.length");
193 char[] tmp = new char [length];
195 InternalCopyTo (startIndex, tmp, 0, length);
200 public String [] Split (params char [] separator)
202 return Split (separator, Int32.MaxValue);
205 public String[] Split (char[] separator, int count)
207 if (separator == null || separator.Length == 0)
208 separator = WhiteChars;
211 throw new ArgumentOutOfRangeException ("count");
214 return new String[0];
217 return new String[1] { ToString() };
219 return InternalSplit (separator, count, 0);
224 [MonoDocumentationNote ("code should be moved to managed")]
225 public String[] Split (char[] separator, int count, StringSplitOptions options)
227 if (separator == null || separator.Length == 0)
228 return Split (WhiteChars, count, options);
231 throw new ArgumentOutOfRangeException ("count", "Count cannot be less than zero.");
232 if ((options != StringSplitOptions.None) && (options != StringSplitOptions.RemoveEmptyEntries))
233 throw new ArgumentException ("options must be one of the values in the StringSplitOptions enumeration", "options");
236 return new string [0];
238 return InternalSplit (separator, count, (int)options);
242 public String[] Split (string[] separator, int count, StringSplitOptions options)
244 if (separator == null || separator.Length == 0)
245 return Split (WhiteChars, count, options);
248 throw new ArgumentOutOfRangeException ("count", "Count cannot be less than zero.");
249 if ((options != StringSplitOptions.None) && (options != StringSplitOptions.RemoveEmptyEntries))
250 throw new ArgumentException ("Illegal enum value: " + options + ".", "options");
252 bool removeEmpty = (options & StringSplitOptions.RemoveEmptyEntries) == StringSplitOptions.RemoveEmptyEntries;
254 if (count == 0 || (this == String.Empty && removeEmpty))
255 return new String [0];
257 ArrayList arr = new ArrayList ();
260 while (pos < this.Length) {
262 int matchPos = Int32.MaxValue;
264 // Find the first position where any of the separators matches
265 for (int i = 0; i < separator.Length; ++i) {
266 string sep = separator [i];
267 if (sep == null || sep == String.Empty)
270 int match = IndexOf (sep, pos);
271 if (match > -1 && match < matchPos) {
277 if (matchIndex == -1)
280 if (matchPos == pos && removeEmpty) {
281 pos = matchPos + separator [matchIndex].Length;
284 arr.Add (this.Substring (pos, matchPos - pos));
286 pos = matchPos + separator [matchIndex].Length;
288 if (arr.Count == count - 1) {
295 return new String [] { this };
297 if (removeEmpty && pos == this.Length) {
298 String[] res = new String [arr.Count];
299 arr.CopyTo (0, res, 0, arr.Count);
304 String[] res = new String [arr.Count + 1];
305 arr.CopyTo (0, res, 0, arr.Count);
306 res [arr.Count] = this.Substring (pos);
314 public String[] Split (char[] separator, StringSplitOptions options)
316 return Split (separator, Int32.MaxValue, options);
320 public String[] Split (String[] separator, StringSplitOptions options)
322 return Split (separator, Int32.MaxValue, options);
326 public unsafe String Substring (int startIndex)
331 if (startIndex < 0 || startIndex > this.length)
332 throw new ArgumentOutOfRangeException ("startIndex");
334 int newlen = this.length - startIndex;
335 string tmp = InternalAllocateStr (newlen);
337 fixed (char *dest = tmp, src = this) {
338 memcpy ((byte*)dest, (byte*)(src + startIndex), newlen * 2);
344 public unsafe String Substring (int startIndex, int length)
347 throw new ArgumentOutOfRangeException ("length", "< 0");
349 throw new ArgumentOutOfRangeException ("startIndex", "< 0");
350 // re-ordered to avoid possible integer overflow
351 if (startIndex > this.length - length)
352 throw new ArgumentOutOfRangeException ("startIndex + length > this.length");
357 string tmp = InternalAllocateStr (length);
358 fixed (char *dest = tmp, src = this) {
359 memcpy ((byte*)dest, (byte*)(src + startIndex), length * 2);
365 private static readonly char[] WhiteChars = { (char) 0x9, (char) 0xA, (char) 0xB, (char) 0xC, (char) 0xD,
367 (char) 0x85, (char) 0x1680, (char) 0x2028, (char) 0x2029,
369 (char) 0x20, (char) 0xA0, (char) 0x2000, (char) 0x2001, (char) 0x2002, (char) 0x2003, (char) 0x2004,
370 (char) 0x2005, (char) 0x2006, (char) 0x2007, (char) 0x2008, (char) 0x2009, (char) 0x200A, (char) 0x200B,
371 (char) 0x3000, (char) 0xFEFF };
373 public String Trim ()
375 return InternalTrim (WhiteChars, 0);
378 public String Trim (params char[] trimChars)
380 if (trimChars == null || trimChars.Length == 0)
381 trimChars = WhiteChars;
383 return InternalTrim (trimChars, 0);
386 public String TrimStart (params char[] trimChars)
388 if (trimChars == null || trimChars.Length == 0)
389 trimChars = WhiteChars;
391 return InternalTrim (trimChars, 1);
394 public String TrimEnd (params char[] trimChars)
396 if (trimChars == null || trimChars.Length == 0)
397 trimChars = WhiteChars;
399 return InternalTrim (trimChars, 2);
402 public static int Compare (String strA, String strB)
404 return Compare (strA, strB, false, CultureInfo.CurrentCulture);
407 public static int Compare (String strA, String strB, bool ignoreCase)
409 return Compare (strA, strB, ignoreCase, CultureInfo.CurrentCulture);
412 public static int Compare (String strA, String strB, bool ignoreCase, CultureInfo culture)
415 throw new ArgumentNullException ("culture");
424 else if (strB == null) {
428 CompareOptions compopts;
431 compopts = CompareOptions.IgnoreCase;
433 compopts = CompareOptions.None;
435 return culture.CompareInfo.Compare (strA, strB, compopts);
438 public static int Compare (String strA, int indexA, String strB, int indexB, int length)
440 return Compare (strA, indexA, strB, indexB, length, false, CultureInfo.CurrentCulture);
443 public static int Compare (String strA, int indexA, String strB, int indexB, int length, bool ignoreCase)
445 return Compare (strA, indexA, strB, indexB, length, ignoreCase, CultureInfo.CurrentCulture);
448 public static int Compare (String strA, int indexA, String strB, int indexB, int length, bool ignoreCase, CultureInfo culture)
451 throw new ArgumentNullException ("culture");
453 if ((indexA > strA.Length) || (indexB > strB.Length) || (indexA < 0) || (indexB < 0) || (length < 0))
454 throw new ArgumentOutOfRangeException ();
466 else if (strB == null) {
470 CompareOptions compopts;
473 compopts = CompareOptions.IgnoreCase;
475 compopts = CompareOptions.None;
477 /* Need to cap the requested length to the
478 * length of the string, because
479 * CompareInfo.Compare will insist that length
480 * <= (string.Length - offset)
485 if (length > (strA.Length - indexA)) {
486 len1 = strA.Length - indexA;
489 if (length > (strB.Length - indexB)) {
490 len2 = strB.Length - indexB;
493 return culture.CompareInfo.Compare (strA, indexA, len1, strB, indexB, len2, compopts);
496 public static int Compare (string strA, string strB, StringComparison comparisonType)
498 switch (comparisonType) {
499 case StringComparison.CurrentCulture:
500 return Compare (strA, strB, false, CultureInfo.CurrentCulture);
501 case StringComparison.CurrentCultureIgnoreCase:
502 return Compare (strA, strB, true, CultureInfo.CurrentCulture);
503 case StringComparison.InvariantCulture:
504 return Compare (strA, strB, false, CultureInfo.InvariantCulture);
505 case StringComparison.InvariantCultureIgnoreCase:
506 return Compare (strA, strB, true, CultureInfo.InvariantCulture);
507 case StringComparison.Ordinal:
508 return CompareOrdinal (strA, strB, CompareOptions.Ordinal);
509 case StringComparison.OrdinalIgnoreCase:
510 return CompareOrdinal (strA, strB, CompareOptions.Ordinal | CompareOptions.IgnoreCase);
512 string msg = Locale.GetText ("Invalid value '{0}' for StringComparison", comparisonType);
513 throw new ArgumentException ("comparisonType", msg);
517 public static int Compare (string strA, int indexA, string strB, int indexB, int length, StringComparison comparisonType)
519 switch (comparisonType) {
520 case StringComparison.CurrentCulture:
521 return Compare (strA, indexA, strB, indexB, length, false, CultureInfo.CurrentCulture);
522 case StringComparison.CurrentCultureIgnoreCase:
523 return Compare (strA, indexA, strB, indexB, length, true, CultureInfo.CurrentCulture);
524 case StringComparison.InvariantCulture:
525 return Compare (strA, indexA, strB, indexB, length, false, CultureInfo.InvariantCulture);
526 case StringComparison.InvariantCultureIgnoreCase:
527 return Compare (strA, indexA, strB, indexB, length, true, CultureInfo.InvariantCulture);
528 case StringComparison.Ordinal:
529 return CompareOrdinal (strA, indexA, strB, indexB, length, CompareOptions.Ordinal);
530 case StringComparison.OrdinalIgnoreCase:
531 return CompareOrdinal (strA, indexA, strB, indexB, length, CompareOptions.Ordinal | CompareOptions.IgnoreCase);
533 string msg = Locale.GetText ("Invalid value '{0}' for StringComparison", comparisonType);
534 throw new ArgumentException ("comparisonType", msg);
538 public static bool Equals (string a, string b, StringComparison comparisonType)
540 return String.Compare (a, b, comparisonType) == 0;
543 public bool Equals (string value, StringComparison comparisonType)
545 return String.Equals (this, value, comparisonType);
548 public int CompareTo (Object value)
553 if (!(value is String))
554 throw new ArgumentException ();
556 return String.Compare (this, (String) value, false);
559 public int CompareTo (String strB)
564 return Compare (this, strB, false);
567 public static unsafe int CompareOrdinal (String strA, String strB)
574 } else if (strB == null) {
577 fixed (char* aptr = strA, bptr = strB) {
579 char* end = ap + Math.Min (strA.Length, strB.Length);
587 return strA.Length - strB.Length;
591 internal static int CompareOrdinal (String strA, String strB, CompareOptions options)
598 } else if (strB == null) {
602 /* Invariant, because that is cheaper to
603 * instantiate (and chances are it already has
606 return CultureInfo.InvariantCulture.CompareInfo.Compare (strA, strB, options);
609 public static int CompareOrdinal (String strA, int indexA, String strB, int indexB, int length)
611 return CompareOrdinal (strA, indexA, strB, indexB, length, CompareOptions.Ordinal);
614 internal static int CompareOrdinal (String strA, int indexA, String strB, int indexB, int length, CompareOptions options)
616 if ((indexA > strA.Length) || (indexB > strB.Length) || (indexA < 0) || (indexB < 0) || (length < 0))
617 throw new ArgumentOutOfRangeException ();
625 else if (strB == null) {
629 /* Need to cap the requested length to the
630 * length of the string, because
631 * CompareInfo.Compare will insist that length
632 * <= (string.Length - offset)
637 if (length > (strA.Length - indexA)) {
638 len1 = strA.Length - indexA;
641 if (length > (strB.Length - indexB)) {
642 len2 = strB.Length - indexB;
645 return CultureInfo.InvariantCulture.CompareInfo.Compare (strA, indexA, len1, strB, indexB, len2, options);
648 public bool EndsWith (String value)
650 return EndsWith (value, false, CultureInfo.CurrentCulture);
658 bool EndsWith (String value, bool ignoreCase, CultureInfo culture)
660 return (culture.CompareInfo.IsSuffix (this, value,
661 ignoreCase ? CompareOptions.IgnoreCase :
662 CompareOptions.None));
665 public int IndexOfAny (char [] anyOf)
668 throw new ArgumentNullException ("anyOf");
669 if (this.length == 0)
672 return InternalIndexOfAny (anyOf, 0, this.length);
675 public int IndexOfAny (char [] anyOf, int startIndex)
678 throw new ArgumentNullException ("anyOf");
679 if (startIndex < 0 || startIndex > this.length)
680 throw new ArgumentOutOfRangeException ("startIndex");
682 return InternalIndexOfAny (anyOf, startIndex, this.length - startIndex);
685 public int IndexOfAny (char [] anyOf, int startIndex, int count)
688 throw new ArgumentNullException ("anyOf");
690 throw new ArgumentOutOfRangeException ("startIndex", "< 0");
692 throw new ArgumentOutOfRangeException ("count", "< 0");
693 // re-ordered to avoid possible integer overflow
694 if (startIndex > this.length - count)
695 throw new ArgumentOutOfRangeException ("startIndex + count > this.length");
697 return InternalIndexOfAny (anyOf, startIndex, count);
700 unsafe int InternalIndexOfAny (char[] anyOf, int startIndex, int count)
702 if (anyOf.Length == 0)
705 if (anyOf.Length == 1)
706 return IndexOfImpl(anyOf[0], startIndex, count);
708 fixed (char* any = anyOf) {
712 char* end_any_ptr = any + anyOf.Length;
714 while (++any_ptr != end_any_ptr) {
715 if (*any_ptr > highest) {
720 if (*any_ptr < lowest)
724 fixed (char* start = &start_char) {
725 char* ptr = start + startIndex;
726 char* end_ptr = ptr + count;
728 while (ptr != end_ptr) {
729 if (*ptr > highest || *ptr < lowest) {
735 return (int)(ptr - start);
738 while (++any_ptr != end_any_ptr) {
739 if (*ptr == *any_ptr)
740 return (int)(ptr - start);
752 public int IndexOf (string value, StringComparison comparison)
754 return IndexOf (value, 0, this.Length, comparison);
757 public int IndexOf (string value, int startIndex, StringComparison comparison)
759 return IndexOf (value, startIndex, this.Length - startIndex, comparison);
762 public int IndexOf (string value, int startIndex, int count, StringComparison comparison)
764 switch (comparison) {
765 case StringComparison.CurrentCulture:
766 return CultureInfo.CurrentCulture.CompareInfo.IndexOf (this, value, startIndex, count, CompareOptions.None);
767 case StringComparison.CurrentCultureIgnoreCase:
768 return CultureInfo.CurrentCulture.CompareInfo.IndexOf (this, value, startIndex, count, CompareOptions.IgnoreCase);
769 case StringComparison.InvariantCulture:
770 return CultureInfo.InvariantCulture.CompareInfo.IndexOf (this, value, startIndex, count, CompareOptions.None);
771 case StringComparison.InvariantCultureIgnoreCase:
772 return CultureInfo.InvariantCulture.CompareInfo.IndexOf (this, value, startIndex, count, CompareOptions.IgnoreCase);
773 case StringComparison.Ordinal:
774 return CultureInfo.InvariantCulture.CompareInfo.IndexOf (this, value, startIndex, count, CompareOptions.Ordinal);
775 case StringComparison.OrdinalIgnoreCase:
776 return CultureInfo.InvariantCulture.CompareInfo.IndexOf (this, value, startIndex, count, CompareOptions.OrdinalIgnoreCase);
778 throw new SystemException ("INTERNAL ERROR: should not reach here ...");
781 public int LastIndexOf (string value, StringComparison comparison)
783 return LastIndexOf (value, value.Length - 1, value.Length, comparison);
786 public int LastIndexOf (string value, int startIndex, StringComparison comparison)
788 return LastIndexOf (value, startIndex, startIndex + 1, comparison);
791 public int LastIndexOf (string value, int startIndex, int count, StringComparison comparison)
793 switch (comparison) {
794 case StringComparison.CurrentCulture:
795 return CultureInfo.CurrentCulture.CompareInfo.LastIndexOf (this, value, startIndex, count, CompareOptions.None);
796 case StringComparison.CurrentCultureIgnoreCase:
797 return CultureInfo.CurrentCulture.CompareInfo.LastIndexOf (this, value, startIndex, count, CompareOptions.IgnoreCase);
798 case StringComparison.InvariantCulture:
799 return CultureInfo.InvariantCulture.CompareInfo.LastIndexOf (this, value, startIndex, count, CompareOptions.None);
800 case StringComparison.InvariantCultureIgnoreCase:
801 return CultureInfo.InvariantCulture.CompareInfo.LastIndexOf (this, value, startIndex, count, CompareOptions.IgnoreCase);
802 case StringComparison.Ordinal:
803 return CultureInfo.InvariantCulture.CompareInfo.LastIndexOf (this, value, startIndex, count, CompareOptions.Ordinal);
804 case StringComparison.OrdinalIgnoreCase:
805 return CultureInfo.InvariantCulture.CompareInfo.LastIndexOf (this, value, startIndex, count, CompareOptions.OrdinalIgnoreCase);
807 throw new SystemException ("INTERNAL ERROR: should not reach here ...");
811 public int IndexOf (char value)
813 if (this.length == 0)
816 return IndexOfImpl (value, 0, this.length);
819 public int IndexOf (String value)
821 return IndexOf (value, 0, this.length);
824 public int IndexOf (char value, int startIndex)
826 return IndexOf (value, startIndex, this.length - startIndex);
829 public int IndexOf (String value, int startIndex)
831 return IndexOf (value, startIndex, this.length - startIndex);
834 /* This method is culture-insensitive */
835 public int IndexOf (char value, int startIndex, int count)
838 throw new ArgumentOutOfRangeException ("startIndex", "< 0");
840 throw new ArgumentOutOfRangeException ("count", "< 0");
841 // re-ordered to avoid possible integer overflow
842 if (startIndex > this.length - count)
843 throw new ArgumentOutOfRangeException ("startIndex + count > this.length");
845 if ((startIndex == 0 && this.length == 0) || (startIndex == this.length) || (count == 0))
848 return IndexOfImpl (value, startIndex, count);
851 unsafe int IndexOfImpl (char value, int startIndex, int count)
853 // It helps JIT compiler to optimize comparison
854 int value_32 = (int)value;
856 fixed (char* start = &start_char) {
857 char* ptr = start + startIndex;
858 char* end_ptr = ptr + (count >> 3 << 3);
860 while (ptr != end_ptr) {
861 if (*ptr == value_32)
862 return (int)(ptr - start);
863 if (ptr[1] == value_32)
864 return (int)(ptr - start + 1);
865 if (ptr[2] == value_32)
866 return (int)(ptr - start + 2);
867 if (ptr[3] == value_32)
868 return (int)(ptr - start + 3);
869 if (ptr[4] == value_32)
870 return (int)(ptr - start + 4);
871 if (ptr[5] == value_32)
872 return (int)(ptr - start + 5);
873 if (ptr[6] == value_32)
874 return (int)(ptr - start + 6);
875 if (ptr[7] == value_32)
876 return (int)(ptr - start + 7);
881 end_ptr += count & 0x07;
882 while (ptr != end_ptr) {
883 if (*ptr == value_32)
884 return (int)(ptr - start);
892 /* But this one is culture-sensitive */
893 public int IndexOf (String value, int startIndex, int count)
896 throw new ArgumentNullException ("value");
898 throw new ArgumentOutOfRangeException ("startIndex", "< 0");
900 throw new ArgumentOutOfRangeException ("count", "< 0");
901 // re-ordered to avoid possible integer overflow
902 if (startIndex > this.length - count)
903 throw new ArgumentOutOfRangeException ("startIndex + count > this.length");
905 if (value.length == 0)
908 if (startIndex == 0 && this.length == 0)
914 return CultureInfo.CurrentCulture.CompareInfo.IndexOf (this, value, startIndex, count);
917 public int LastIndexOfAny (char [] anyOf)
920 throw new ArgumentNullException ("anyOf");
922 return InternalLastIndexOfAny (anyOf, this.length - 1, this.length);
925 public int LastIndexOfAny (char [] anyOf, int startIndex)
928 throw new ArgumentNullException ("anyOf");
930 if (startIndex < 0 || startIndex >= this.length)
931 throw new ArgumentOutOfRangeException ();
933 if (this.length == 0)
936 return InternalLastIndexOfAny (anyOf, startIndex, startIndex + 1);
939 public int LastIndexOfAny (char [] anyOf, int startIndex, int count)
942 throw new ArgumentNullException ("anyOf");
944 if ((startIndex < 0) || (startIndex >= this.Length))
945 throw new ArgumentOutOfRangeException ("startIndex", "< 0 || > this.Length");
946 if ((count < 0) || (count > this.Length))
947 throw new ArgumentOutOfRangeException ("count", "< 0 || > this.Length");
948 if (startIndex - count + 1 < 0)
949 throw new ArgumentOutOfRangeException ("startIndex - count + 1 < 0");
951 if (this.length == 0)
954 return InternalLastIndexOfAny (anyOf, startIndex, count);
957 public int LastIndexOf (char value)
959 if (this.length == 0)
962 return LastIndexOfImpl (value, this.length - 1, this.length);
965 public int LastIndexOf (String value)
967 if (this.length == 0)
968 /* This overload does additional checking */
969 return LastIndexOf (value, 0, 0);
971 return LastIndexOf (value, this.length - 1, this.length);
974 public int LastIndexOf (char value, int startIndex)
976 return LastIndexOf (value, startIndex, startIndex + 1);
979 public int LastIndexOf (String value, int startIndex)
982 throw new ArgumentNullException ("value");
983 int max = startIndex;
984 if (max < this.Length)
986 return LastIndexOf (value, startIndex, max);
989 /* This method is culture-insensitive */
990 public int LastIndexOf (char value, int startIndex, int count)
992 if (startIndex == 0 && this.length == 0)
995 // >= for char (> for string)
996 if ((startIndex < 0) || (startIndex >= this.Length))
997 throw new ArgumentOutOfRangeException ("startIndex", "< 0 || >= this.Length");
998 if ((count < 0) || (count > this.Length))
999 throw new ArgumentOutOfRangeException ("count", "< 0 || > this.Length");
1000 if (startIndex - count + 1 < 0)
1001 throw new ArgumentOutOfRangeException ("startIndex - count + 1 < 0");
1003 return LastIndexOfImpl (value, startIndex, count);
1006 /* This method is culture-insensitive */
1007 unsafe int LastIndexOfImpl (char value, int startIndex, int count)
1009 // It helps JIT compiler to optimize comparison
1010 int value_32 = (int)value;
1012 fixed (char* start = &start_char) {
1013 char* ptr = start + startIndex;
1014 char* end_ptr = ptr - (count >> 3 << 3);
1016 while (ptr != end_ptr) {
1017 if (*ptr == value_32)
1018 return (int)(ptr - start);
1019 if (ptr[-1] == value_32)
1020 return (int)(ptr - start) - 1;
1021 if (ptr[-2] == value_32)
1022 return (int)(ptr - start) - 2;
1023 if (ptr[-3] == value_32)
1024 return (int)(ptr - start) - 3;
1025 if (ptr[-4] == value_32)
1026 return (int)(ptr - start) - 4;
1027 if (ptr[-5] == value_32)
1028 return (int)(ptr - start) - 5;
1029 if (ptr[-6] == value_32)
1030 return (int)(ptr - start) - 6;
1031 if (ptr[-7] == value_32)
1032 return (int)(ptr - start) - 7;
1037 end_ptr -= count & 0x07;
1038 while (ptr != end_ptr) {
1039 if (*ptr == value_32)
1040 return (int)(ptr - start);
1048 /* But this one is culture-sensitive */
1049 public int LastIndexOf (String value, int startIndex, int count)
1052 throw new ArgumentNullException ("value");
1053 // -1 > startIndex > for string (0 > startIndex >= for char)
1054 if ((startIndex < -1) || (startIndex > this.Length))
1055 throw new ArgumentOutOfRangeException ("startIndex", "< 0 || > this.Length");
1056 if ((count < 0) || (count > this.Length))
1057 throw new ArgumentOutOfRangeException ("count", "< 0 || > this.Length");
1058 if (startIndex - count + 1 < 0)
1059 throw new ArgumentOutOfRangeException ("startIndex - count + 1 < 0");
1061 if (value.Length == 0)
1064 if (startIndex == 0 && this.length == 0)
1067 // This check is needed to match undocumented MS behaviour
1068 if (this.length == 0 && value.length > 0)
1074 if (startIndex == this.Length)
1076 return CultureInfo.CurrentCulture.CompareInfo.LastIndexOf (this, value, startIndex, count);
1080 public bool Contains (String value)
1082 return IndexOf (value) != -1;
1085 public static bool IsNullOrEmpty (String value)
1087 return (value == null) || (value.Length == 0);
1090 public string Normalize ()
1092 return Normalize (NormalizationForm.FormC);
1095 public string Normalize (NormalizationForm form)
1099 return Normalization.Normalize (this, 0);
1100 case NormalizationForm.FormD:
1101 return Normalization.Normalize (this, 1);
1102 case NormalizationForm.FormKC:
1103 return Normalization.Normalize (this, 2);
1104 case NormalizationForm.FormKD:
1105 return Normalization.Normalize (this, 3);
1109 public bool IsNormalized ()
1111 return IsNormalized (NormalizationForm.FormC);
1114 public bool IsNormalized (NormalizationForm form)
1118 return Normalization.IsNormalized (this, 0);
1119 case NormalizationForm.FormD:
1120 return Normalization.IsNormalized (this, 1);
1121 case NormalizationForm.FormKC:
1122 return Normalization.IsNormalized (this, 2);
1123 case NormalizationForm.FormKD:
1124 return Normalization.IsNormalized (this, 3);
1128 public string Remove (int startIndex)
1131 throw new ArgumentOutOfRangeException ("startIndex", "StartIndex can not be less than zero");
1132 if (startIndex >= this.length)
1133 throw new ArgumentOutOfRangeException ("startIndex", "StartIndex must be less than the length of the string");
1135 return Remove (startIndex, this.length - startIndex);
1139 public String PadLeft (int totalWidth)
1141 return PadLeft (totalWidth, ' ');
1144 public String PadLeft (int totalWidth, char paddingChar)
1147 throw new ArgumentOutOfRangeException ("totalWidth", "< 0");
1149 if (totalWidth < this.length)
1150 return String.Copy (this);
1152 return InternalPad (totalWidth, paddingChar, false);
1155 public String PadRight (int totalWidth)
1157 return PadRight (totalWidth, ' ');
1160 public String PadRight (int totalWidth, char paddingChar)
1163 throw new ArgumentOutOfRangeException ("totalWidth", "< 0");
1165 if (totalWidth < this.length)
1166 return String.Copy (this);
1168 return InternalPad (totalWidth, paddingChar, true);
1171 public bool StartsWith (String value)
1173 return StartsWith (value, false, CultureInfo.CurrentCulture);
1177 [ComVisible (false)]
1178 public bool StartsWith (string value, StringComparison comparisonType)
1180 switch (comparisonType) {
1181 case StringComparison.CurrentCulture:
1182 return CultureInfo.CurrentCulture.CompareInfo.IsPrefix (this, value, CompareOptions.None);
1183 case StringComparison.CurrentCultureIgnoreCase:
1184 return CultureInfo.CurrentCulture.CompareInfo.IsPrefix (this, value, CompareOptions.IgnoreCase);
1185 case StringComparison.InvariantCulture:
1186 return CultureInfo.InvariantCulture.CompareInfo.IsPrefix (this, value, CompareOptions.None);
1187 case StringComparison.InvariantCultureIgnoreCase:
1188 return CultureInfo.InvariantCulture.CompareInfo.IsPrefix (this, value, CompareOptions.IgnoreCase);
1189 case StringComparison.Ordinal:
1190 return CultureInfo.CurrentCulture.CompareInfo.IsPrefix (this, value, CompareOptions.Ordinal);
1191 case StringComparison.OrdinalIgnoreCase:
1192 return CultureInfo.CurrentCulture.CompareInfo.IsPrefix (this, value, CompareOptions.OrdinalIgnoreCase);
1198 [ComVisible (false)]
1199 public bool EndsWith (string value, StringComparison comparisonType)
1201 switch (comparisonType) {
1202 case StringComparison.CurrentCulture:
1203 return CultureInfo.CurrentCulture.CompareInfo.IsSuffix (this, value, CompareOptions.None);
1204 case StringComparison.CurrentCultureIgnoreCase:
1205 return CultureInfo.CurrentCulture.CompareInfo.IsSuffix (this, value, CompareOptions.IgnoreCase);
1206 case StringComparison.InvariantCulture:
1207 return CultureInfo.InvariantCulture.CompareInfo.IsSuffix (this, value, CompareOptions.None);
1208 case StringComparison.InvariantCultureIgnoreCase:
1209 return CultureInfo.InvariantCulture.CompareInfo.IsSuffix (this, value, CompareOptions.IgnoreCase);
1210 case StringComparison.Ordinal:
1211 return CultureInfo.CurrentCulture.CompareInfo.IsSuffix (this, value, CompareOptions.Ordinal);
1212 case StringComparison.OrdinalIgnoreCase:
1213 return CultureInfo.CurrentCulture.CompareInfo.IsSuffix (this, value, CompareOptions.OrdinalIgnoreCase);
1226 bool StartsWith (String value, bool ignoreCase, CultureInfo culture)
1228 if (culture == null)
1229 culture = CultureInfo.CurrentCulture;
1231 return (culture.CompareInfo.IsPrefix (this, value,
1232 ignoreCase ? CompareOptions.IgnoreCase :
1233 CompareOptions.None));
1236 /* This method is culture insensitive */
1237 public unsafe String Replace (char oldChar, char newChar)
1239 if (this.length == 0 || oldChar == newChar)
1242 int start_pos = IndexOfImpl (oldChar, 0, this.length);
1243 if (start_pos == -1)
1249 string tmp = InternalAllocateStr(length);
1250 fixed (char* dest = tmp, src = &start_char) {
1252 memcpy((byte*)dest, (byte*)src, start_pos * 2);
1254 char* end_ptr = dest + length;
1255 char* dest_ptr = dest + start_pos;
1256 char* src_ptr = src + start_pos;
1258 while (dest_ptr != end_ptr) {
1259 if (*src_ptr == oldChar)
1260 *dest_ptr = newChar;
1262 *dest_ptr = *src_ptr;
1271 /* This method is culture sensitive */
1272 public String Replace (String oldValue, String newValue)
1274 if (oldValue == null)
1275 throw new ArgumentNullException ("oldValue");
1277 if (oldValue.Length == 0)
1278 throw new ArgumentException ("oldValue is the empty string.");
1280 if (this.Length == 0)
1283 if (newValue == null)
1284 newValue = String.Empty;
1286 return InternalReplace (oldValue, newValue, CultureInfo.CurrentCulture.CompareInfo);
1289 public unsafe String Remove (int startIndex, int count)
1292 throw new ArgumentOutOfRangeException ("startIndex", "< 0");
1294 throw new ArgumentOutOfRangeException ("count", "< 0");
1295 // re-ordered to avoid possible integer overflow
1296 if (startIndex > this.length - count)
1297 throw new ArgumentOutOfRangeException ("startIndex + count > this.length");
1299 String tmp = InternalAllocateStr (this.length - count);
1301 fixed (char *dest = tmp, src = this) {
1303 memcpy ((byte*)dst, (byte*)src, startIndex * 2);
1304 int skip = startIndex + count;
1306 memcpy ((byte*)dst, (byte*)(src + skip), (length - skip) * 2);
1311 public String ToLower ()
1313 return ToLower (CultureInfo.CurrentCulture);
1316 public String ToLower (CultureInfo culture)
1318 if (culture == null)
1319 throw new ArgumentNullException ("culture");
1321 if (culture.LCID == 0x007F) { // Invariant
1322 return ToLowerInvariant ();
1324 return culture.TextInfo.ToLower (this);
1328 public unsafe String ToLowerInvariant ()
1330 internal unsafe String ToLowerInvariant ()
1333 string tmp = InternalAllocateStr (length);
1334 fixed (char* source = &start_char, dest = tmp) {
1336 char* destPtr = (char*)dest;
1337 char* sourcePtr = (char*)source;
1339 for (int n = 0; n < length; n++) {
1340 *destPtr = Char.ToLowerInvariant (*sourcePtr);
1348 public String ToUpper ()
1350 return ToUpper (CultureInfo.CurrentCulture);
1353 public String ToUpper (CultureInfo culture)
1355 if (culture == null)
1356 throw new ArgumentNullException ("culture");
1358 if (culture.LCID == 0x007F) { // Invariant
1359 return ToUpperInvariant ();
1361 return culture.TextInfo.ToUpper (this);
1365 public unsafe String ToUpperInvariant ()
1367 internal unsafe String ToUpperInvariant ()
1370 string tmp = InternalAllocateStr (length);
1371 fixed (char* source = &start_char, dest = tmp) {
1373 char* destPtr = (char*)dest;
1374 char* sourcePtr = (char*)source;
1376 for (int n = 0; n < length; n++) {
1377 *destPtr = Char.ToUpperInvariant (*sourcePtr);
1385 public override String ToString ()
1390 public String ToString (IFormatProvider provider)
1395 public static String Format (String format, Object arg0)
1397 return Format (null, format, new Object[] {arg0});
1400 public static String Format (String format, Object arg0, Object arg1)
1402 return Format (null, format, new Object[] {arg0, arg1});
1405 public static String Format (String format, Object arg0, Object arg1, Object arg2)
1407 return Format (null, format, new Object[] {arg0, arg1, arg2});
1410 public static string Format (string format, params object[] args)
1412 return Format (null, format, args);
1415 public static string Format (IFormatProvider provider, string format, params object[] args)
1417 StringBuilder b = new StringBuilder ();
1418 FormatHelper (b, provider, format, args);
1419 return b.ToString ();
1422 internal static void FormatHelper (StringBuilder result, IFormatProvider provider, string format, params object[] args)
1424 if (format == null || args == null)
1425 throw new ArgumentNullException ();
1429 while (ptr < format.length) {
1430 char c = format[ptr ++];
1433 result.Append (format, start, ptr - start - 1);
1435 // check for escaped open bracket
1437 if (format[ptr] == '{') {
1448 ParseFormatSpecifier (format, ref ptr, out n, out width, out left_align, out arg_format);
1449 if (n >= args.Length)
1450 throw new FormatException ("Index (zero based) must be greater than or equal to zero and less than the size of the argument list.");
1454 object arg = args[n];
1457 ICustomFormatter formatter = null;
1458 if (provider != null)
1459 formatter = provider.GetFormat (typeof (ICustomFormatter))
1460 as ICustomFormatter;
1463 else if (formatter != null)
1464 str = formatter.Format (arg_format, arg, provider);
1465 else if (arg is IFormattable)
1466 str = ((IFormattable)arg).ToString (arg_format, provider);
1468 str = arg.ToString ();
1470 // pad formatted string and append to result
1472 if (width > str.length) {
1473 const char padchar = ' ';
1474 int padlen = width - str.length;
1477 result.Append (str);
1478 result.Append (padchar, padlen);
1481 result.Append (padchar, padlen);
1482 result.Append (str);
1486 result.Append (str);
1490 else if (c == '}' && ptr < format.length && format[ptr] == '}') {
1491 result.Append (format, start, ptr - start - 1);
1494 else if (c == '}') {
1495 throw new FormatException ("Input string was not in a correct format.");
1499 if (start < format.length)
1500 result.Append (format, start, format.Length - start);
1503 public unsafe static String Copy (String str)
1506 throw new ArgumentNullException ("str");
1508 int length = str.length;
1510 String tmp = InternalAllocateStr (length);
1512 fixed (char *dest = tmp, src = str) {
1513 memcpy ((byte*)dest, (byte*)src, length * 2);
1519 public static String Concat (Object obj)
1522 return String.Empty;
1524 return obj.ToString ();
1527 public unsafe static String Concat (Object obj1, Object obj2)
1531 s1 = (obj1 != null) ? obj1.ToString () : null;
1532 s2 = (obj2 != null) ? obj2.ToString () : null;
1536 return String.Empty;
1539 } else if (s2 == null)
1542 String tmp = InternalAllocateStr (s1.Length + s2.Length);
1543 if (s1.Length != 0) {
1544 fixed (char *dest = tmp, src = s1) {
1545 memcpy ((byte*)dest, (byte*)src, s1.length * 2);
1548 if (s2.Length != 0) {
1549 fixed (char *dest = tmp, src = s2) {
1550 memcpy ((byte*)(dest + s1.Length), (byte*)src, s2.length * 2);
1557 public static String Concat (Object obj1, Object obj2, Object obj3)
1563 s1 = obj1.ToString ();
1568 s2 = obj2.ToString ();
1573 s3 = obj3.ToString ();
1575 return Concat (s1, s2, s3);
1578 #if ! BOOTSTRAP_WITH_OLDLIB
1579 [CLSCompliant(false)]
1580 public static String Concat (Object obj1, Object obj2, Object obj3,
1581 Object obj4, __arglist)
1583 string s1, s2, s3, s4;
1588 s1 = obj1.ToString ();
1593 s2 = obj2.ToString ();
1598 s3 = obj3.ToString ();
1600 ArgIterator iter = new ArgIterator (__arglist);
1601 int argCount = iter.GetRemainingCount();
1603 StringBuilder sb = new StringBuilder ();
1605 sb.Append (obj4.ToString ());
1607 for (int i = 0; i < argCount; i++) {
1608 TypedReference typedRef = iter.GetNextArg ();
1609 sb.Append (TypedReference.ToObject (typedRef));
1612 s4 = sb.ToString ();
1614 return Concat (s1, s2, s3, s4);
1618 public unsafe static String Concat (String s1, String s2)
1620 if (s1 == null || s1.Length == 0) {
1621 if (s2 == null || s2.Length == 0)
1622 return String.Empty;
1626 if (s2 == null || s2.Length == 0)
1629 String tmp = InternalAllocateStr (s1.length + s2.length);
1631 if (s1.Length != 0) {
1632 fixed (char *dest = tmp, src = s1) {
1633 memcpy ((byte*)dest, (byte*)src, s1.length * 2);
1636 if (s2.Length != 0) {
1637 fixed (char *dest = tmp, src = s2) {
1638 memcpy ((byte*)(dest + s1.Length), (byte*)src, s2.length * 2);
1645 public unsafe static String Concat (String s1, String s2, String s3)
1647 if (s1 == null || s1.Length == 0){
1648 if (s2 == null || s2.Length == 0){
1649 if (s3 == null || s3.Length == 0)
1650 return String.Empty;
1653 if (s3 == null || s3.Length == 0)
1658 if (s2 == null || s2.Length == 0){
1659 if (s3 == null || s3.Length == 0)
1664 if (s3 == null || s3.Length == 0)
1669 //return InternalConcat (s1, s2, s3);
1670 String tmp = InternalAllocateStr (s1.length + s2.length + s3.length);
1672 if (s1.Length != 0) {
1673 fixed (char *dest = tmp, src = s1) {
1674 memcpy ((byte*)dest, (byte*)src, s1.length * 2);
1677 if (s2.Length != 0) {
1678 fixed (char *dest = tmp, src = s2) {
1679 memcpy ((byte*)(dest + s1.Length), (byte*)src, s2.length * 2);
1682 if (s3.Length != 0) {
1683 fixed (char *dest = tmp, src = s3) {
1684 memcpy ((byte*)(dest + s1.Length + s2.Length), (byte*)src, s3.length * 2);
1691 public unsafe static String Concat (String s1, String s2, String s3, String s4)
1693 if (s1 == null && s2 == null && s3 == null && s4 == null)
1694 return String.Empty;
1705 String tmp = InternalAllocateStr (s1.length + s2.length + s3.length + s4.length);
1707 if (s1.Length != 0) {
1708 fixed (char *dest = tmp, src = s1) {
1709 memcpy ((byte*)dest, (byte*)src, s1.length * 2);
1712 if (s2.Length != 0) {
1713 fixed (char *dest = tmp, src = s2) {
1714 memcpy ((byte*)(dest + s1.Length), (byte*)src, s2.length * 2);
1717 if (s3.Length != 0) {
1718 fixed (char *dest = tmp, src = s3) {
1719 memcpy ((byte*)(dest + s1.Length + s2.Length), (byte*)src, s3.length * 2);
1722 if (s4.Length != 0) {
1723 fixed (char *dest = tmp, src = s4) {
1724 memcpy ((byte*)(dest + s1.Length + s2.Length + s3.Length), (byte*)src, s4.length * 2);
1731 public static String Concat (params Object[] args)
1734 throw new ArgumentNullException ("args");
1736 int i = args.Length;
1738 return String.Empty;
1740 string [] strings = new string [i];
1743 foreach (object arg in args) {
1745 strings[i] = String.Empty;
1747 strings[i] = arg.ToString ();
1748 len += strings[i].length;
1754 return String.Empty;
1756 return InternalJoin (String.Empty, strings, 0, strings.Length);
1759 public static String Concat (params String[] values)
1762 throw new ArgumentNullException ("values");
1764 return InternalJoin (String.Empty, values, 0, values.Length);
1767 public unsafe String Insert (int startIndex, String value)
1770 throw new ArgumentNullException ("value");
1772 if (startIndex < 0 || startIndex > this.length)
1773 throw new ArgumentOutOfRangeException ();
1775 if (value.Length == 0)
1777 if (this.Length == 0)
1779 String tmp = InternalAllocateStr (this.length + value.length);
1781 fixed (char *dest = tmp, src = this, val = value) {
1783 memcpy ((byte*)dst, (byte*)src, startIndex * 2);
1785 memcpy ((byte*)dst, (byte*)val, value.length * 2);
1786 dst += value.length;
1787 memcpy ((byte*)dst, (byte*)(src + startIndex), (length - startIndex) * 2);
1793 public static string Intern (string str)
1796 throw new ArgumentNullException ("str");
1798 return InternalIntern (str);
1801 public static string IsInterned (string str)
1804 throw new ArgumentNullException ("str");
1806 return InternalIsInterned (str);
1809 public static string Join (string separator, string [] value)
1812 throw new ArgumentNullException ("value");
1814 return Join (separator, value, 0, value.Length);
1817 public static string Join (string separator, string[] value, int startIndex, int count)
1820 throw new ArgumentNullException ("value");
1822 throw new ArgumentOutOfRangeException ("startIndex", "< 0");
1824 throw new ArgumentOutOfRangeException ("count", "< 0");
1825 // re-ordered to avoid possible integer overflow
1826 if (startIndex > value.Length - count)
1827 throw new ArgumentOutOfRangeException ("startIndex + count > value.length");
1829 if (startIndex == value.Length)
1830 return String.Empty;
1831 if (separator == null)
1832 separator = String.Empty;
1834 return InternalJoin (separator, value, startIndex, count);
1837 bool IConvertible.ToBoolean (IFormatProvider provider)
1839 return Convert.ToBoolean (this, provider);
1842 byte IConvertible.ToByte (IFormatProvider provider)
1844 return Convert.ToByte (this, provider);
1847 char IConvertible.ToChar (IFormatProvider provider)
1849 return Convert.ToChar (this, provider);
1852 DateTime IConvertible.ToDateTime (IFormatProvider provider)
1854 return Convert.ToDateTime (this, provider);
1857 decimal IConvertible.ToDecimal (IFormatProvider provider)
1859 return Convert.ToDecimal (this, provider);
1862 double IConvertible.ToDouble (IFormatProvider provider)
1864 return Convert.ToDouble (this, provider);
1867 short IConvertible.ToInt16 (IFormatProvider provider)
1869 return Convert.ToInt16 (this, provider);
1872 int IConvertible.ToInt32 (IFormatProvider provider)
1874 return Convert.ToInt32 (this, provider);
1877 long IConvertible.ToInt64 (IFormatProvider provider)
1879 return Convert.ToInt64 (this, provider);
1882 sbyte IConvertible.ToSByte (IFormatProvider provider)
1884 return Convert.ToSByte (this, provider);
1887 float IConvertible.ToSingle (IFormatProvider provider)
1889 return Convert.ToSingle (this, provider);
1892 object IConvertible.ToType (Type conversionType, IFormatProvider provider)
1894 return Convert.ToType (this, conversionType, provider);
1897 ushort IConvertible.ToUInt16 (IFormatProvider provider)
1899 return Convert.ToUInt16 (this, provider);
1902 uint IConvertible.ToUInt32 (IFormatProvider provider)
1904 return Convert.ToUInt32 (this, provider);
1907 ulong IConvertible.ToUInt64 (IFormatProvider provider)
1909 return Convert.ToUInt64 (this, provider);
1918 public CharEnumerator GetEnumerator ()
1920 return new CharEnumerator (this);
1924 IEnumerator<char> IEnumerable<char>.GetEnumerator ()
1926 return GetEnumerator ();
1930 IEnumerator IEnumerable.GetEnumerator ()
1932 return new CharEnumerator (this);
1935 private static void ParseFormatSpecifier (string str, ref int ptr, out int n, out int width,
1936 out bool left_align, out string format)
1938 // parses format specifier of form:
1944 // N = argument number (non-negative integer)
1946 n = ParseDecimal (str, ref ptr);
1948 throw new FormatException ("Input string was not in a correct format.");
1950 // M = width (non-negative integer)
1952 if (str[ptr] == ',') {
1953 // White space between ',' and number or sign.
1955 while (Char.IsWhiteSpace (str [ptr]))
1959 format = str.Substring (start, ptr - start);
1961 left_align = (str [ptr] == '-');
1965 width = ParseDecimal (str, ref ptr);
1967 throw new FormatException ("Input string was not in a correct format.");
1972 format = String.Empty;
1975 // F = argument format (string)
1977 if (str[ptr] == ':') {
1979 while (str[ptr] != '}')
1982 format += str.Substring (start, ptr - start);
1987 if (str[ptr ++] != '}')
1988 throw new FormatException ("Input string was not in a correct format.");
1990 catch (IndexOutOfRangeException) {
1991 throw new FormatException ("Input string was not in a correct format.");
1995 private static int ParseDecimal (string str, ref int ptr)
2001 if (c < '0' || '9' < c)
2004 n = n * 10 + c - '0';
2015 internal unsafe void InternalSetChar (int idx, char val)
2017 if ((uint) idx >= (uint) Length)
2018 throw new ArgumentOutOfRangeException ("idx");
2020 fixed (char * pStr = &start_char)
2026 internal unsafe void InternalSetLength (int newLength)
2028 if (newLength > length)
2029 throw new ArgumentOutOfRangeException ("newLength", "newLength as to be <= length");
2031 // zero terminate, we can pass string objects directly via pinvoke
2032 // we also zero the rest of the string, since the new GC needs to be
2033 // able to handle the changing size (it will skip the 0 bytes).
2034 fixed (char * pStr = &start_char) {
2035 char *p = pStr + newLength;
2036 char *end = pStr + length;
2046 [ReliabilityContractAttribute (Consistency.WillNotCorruptState, Cer.MayFail)]
2048 // When modifying it, GetCaseInsensitiveHashCode() should be modified as well.
2049 public unsafe override int GetHashCode ()
2051 fixed (char * c = this) {
2053 char * end = cc + length - 1;
2055 for (;cc < end; cc += 2) {
2056 h = (h << 5) - h + *cc;
2057 h = (h << 5) - h + cc [1];
2061 h = (h << 5) - h + *cc;
2066 internal unsafe int GetCaseInsensitiveHashCode ()
2068 TextInfo ti = CultureInfo.InvariantCulture.TextInfo;
2069 fixed (char * c = this) {
2071 char * end = cc + length - 1;
2073 for (;cc < end; cc += 2) {
2074 h = (h << 5) - h + ti.ToUpper (*cc);
2075 h = (h << 5) - h + ti.ToUpper (cc [1]);
2079 h = (h << 5) - h + ti.ToUpper (*cc);
2084 // Certain constructors are redirected to CreateString methods with
2085 // matching argument list. The this pointer should not be used.
2087 private unsafe String CreateString (sbyte* value)
2090 return String.Empty;
2092 byte* bytes = (byte*) value;
2096 while (bytes++ [0] != 0)
2098 } catch (NullReferenceException) {
2099 throw new ArgumentOutOfRangeException ("value", "Value does not refer to a valid string.");
2101 } catch (AccessViolationException) {
2102 throw new ArgumentOutOfRangeException ("value", "Value does not refer to a valid string.");
2106 return CreateString (value, 0, length, null);
2109 private unsafe String CreateString (sbyte* value, int startIndex, int length)
2111 return CreateString (value, startIndex, length, null);
2114 private unsafe String CreateString (sbyte* value, int startIndex, int length, Encoding enc)
2117 throw new ArgumentOutOfRangeException ("length", "Non-negative number required.");
2119 throw new ArgumentOutOfRangeException ("startIndex", "Non-negative number required.");
2120 if (value + startIndex < value)
2121 throw new ArgumentOutOfRangeException ("startIndex", "Value, startIndex and length do not refer to a valid string.");
2123 bool isDefaultEncoding;
2125 if (isDefaultEncoding = (enc == null)) {
2128 throw new ArgumentNullException ("value");
2131 if (value == null || length == 0)
2133 return String.Empty;
2135 enc = Encoding.Default;
2138 byte [] bytes = new byte [length];
2141 fixed (byte* bytePtr = bytes)
2143 memcpy (bytePtr, (byte*) (value + startIndex), length);
2144 } catch (NullReferenceException) {
2146 if (!isDefaultEncoding)
2150 throw new ArgumentOutOfRangeException ("value", "Value, startIndex and length do not refer to a valid string.");
2152 } catch (AccessViolationException) {
2153 if (!isDefaultEncoding)
2156 throw new ArgumentOutOfRangeException ("value", "Value, startIndex and length do not refer to a valid string.");
2160 // GetString () is called even when length == 0
2161 return enc.GetString (bytes);
2164 unsafe string CreateString (char *value)
2167 return string.Empty;
2174 string result = InternalAllocateStr (i);
2177 fixed (char *dest = result) {
2178 memcpy ((byte*)dest, (byte*)value, i * 2);
2184 unsafe string CreateString (char *value, int startIndex, int length)
2187 return string.Empty;
2189 throw new ArgumentNullException ("value");
2191 throw new ArgumentOutOfRangeException ("startIndex");
2193 throw new ArgumentOutOfRangeException ("length");
2195 string result = InternalAllocateStr (length);
2197 fixed (char *dest = result) {
2198 memcpy ((byte*)dest, (byte*)(value + startIndex), length * 2);
2203 unsafe string CreateString (char [] val, int startIndex, int length)
2206 throw new ArgumentNullException ("val");
2208 throw new ArgumentOutOfRangeException ("startIndex");
2210 throw new ArgumentOutOfRangeException ("length");
2211 if (startIndex > val.Length - length)
2212 throw new ArgumentOutOfRangeException ("Out of range");
2214 return string.Empty;
2216 string result = InternalAllocateStr (length);
2218 fixed (char *dest = result, src = val) {
2219 memcpy ((byte*)dest, (byte*)(src + startIndex), length * 2);
2224 unsafe string CreateString (char [] val)
2227 return string.Empty;
2228 if (val.Length == 0)
2229 return string.Empty;
2230 string result = InternalAllocateStr (val.Length);
2232 fixed (char *dest = result, src = val) {
2233 memcpy ((byte*)dest, (byte*)src, val.Length * 2);
2238 unsafe string CreateString (char c, int count)
2241 throw new ArgumentOutOfRangeException ("count");
2243 return string.Empty;
2244 string result = InternalAllocateStr (count);
2245 fixed (char *dest = result) {
2247 char *end = p + count;
2256 /* helpers used by the runtime as well as above or eslewhere in corlib */
2257 internal static unsafe void memset (byte *dest, int val, int len)
2268 val = val | (val << 8);
2269 val = val | (val << 16);
2272 int rest = (int)dest & 3;
2280 } while (rest != 0);
2283 ((int*)dest) [0] = val;
2284 ((int*)dest) [1] = val;
2285 ((int*)dest) [2] = val;
2286 ((int*)dest) [3] = val;
2291 ((int*)dest) [0] = val;
2303 static unsafe void memcpy4 (byte *dest, byte *src, int size) {
2304 /*while (size >= 32) {
2305 // using long is better than int and slower than double
2306 // FIXME: enable this only on correct alignment or on platforms
2307 // that can tolerate unaligned reads/writes of doubles
2308 ((double*)dest) [0] = ((double*)src) [0];
2309 ((double*)dest) [1] = ((double*)src) [1];
2310 ((double*)dest) [2] = ((double*)src) [2];
2311 ((double*)dest) [3] = ((double*)src) [3];
2316 while (size >= 16) {
2317 ((int*)dest) [0] = ((int*)src) [0];
2318 ((int*)dest) [1] = ((int*)src) [1];
2319 ((int*)dest) [2] = ((int*)src) [2];
2320 ((int*)dest) [3] = ((int*)src) [3];
2326 ((int*)dest) [0] = ((int*)src) [0];
2332 ((byte*)dest) [0] = ((byte*)src) [0];
2338 static unsafe void memcpy2 (byte *dest, byte *src, int size) {
2340 ((short*)dest) [0] = ((short*)src) [0];
2341 ((short*)dest) [1] = ((short*)src) [1];
2342 ((short*)dest) [2] = ((short*)src) [2];
2343 ((short*)dest) [3] = ((short*)src) [3];
2349 ((short*)dest) [0] = ((short*)src) [0];
2355 ((byte*)dest) [0] = ((byte*)src) [0];
2357 static unsafe void memcpy1 (byte *dest, byte *src, int size) {
2359 ((byte*)dest) [0] = ((byte*)src) [0];
2360 ((byte*)dest) [1] = ((byte*)src) [1];
2361 ((byte*)dest) [2] = ((byte*)src) [2];
2362 ((byte*)dest) [3] = ((byte*)src) [3];
2363 ((byte*)dest) [4] = ((byte*)src) [4];
2364 ((byte*)dest) [5] = ((byte*)src) [5];
2365 ((byte*)dest) [6] = ((byte*)src) [6];
2366 ((byte*)dest) [7] = ((byte*)src) [7];
2372 ((byte*)dest) [0] = ((byte*)src) [0];
2373 ((byte*)dest) [1] = ((byte*)src) [1];
2379 ((byte*)dest) [0] = ((byte*)src) [0];
2382 internal static unsafe void memcpy (byte *dest, byte *src, int size) {
2383 // FIXME: if pointers are not aligned, try to align them
2384 // so a faster routine can be used. Handle the case where
2385 // the pointers can't be reduced to have the same alignment
2386 // (just ignore the issue on x86?)
2387 if ((((int)dest | (int)src) & 3) != 0) {
2388 if (((int)dest & 1) != 0 && ((int)src & 1) != 0 && size >= 1) {
2394 if (((int)dest & 2) != 0 && ((int)src & 2) != 0 && size >= 2) {
2395 ((short*)dest) [0] = ((short*)src) [0];
2400 if ((((int)dest | (int)src) & 1) != 0) {
2401 memcpy1 (dest, src, size);
2404 if ((((int)dest | (int)src) & 2) != 0) {
2405 memcpy2 (dest, src, size);
2409 memcpy4 (dest, src, size);
2412 [CLSCompliant (false), MethodImplAttribute (MethodImplOptions.InternalCall)]
2413 unsafe public extern String (char *value);
2415 [CLSCompliant (false), MethodImplAttribute (MethodImplOptions.InternalCall)]
2416 unsafe public extern String (char *value, int startIndex, int length);
2418 [CLSCompliant (false), MethodImplAttribute (MethodImplOptions.InternalCall)]
2419 unsafe public extern String (sbyte *value);
2421 [CLSCompliant (false), MethodImplAttribute (MethodImplOptions.InternalCall)]
2422 unsafe public extern String (sbyte *value, int startIndex, int length);
2424 [CLSCompliant (false), MethodImplAttribute (MethodImplOptions.InternalCall)]
2425 unsafe public extern String (sbyte *value, int startIndex, int length, Encoding enc);
2427 [MethodImplAttribute (MethodImplOptions.InternalCall)]
2428 public extern String (char [] val, int startIndex, int length);
2430 [MethodImplAttribute (MethodImplOptions.InternalCall)]
2431 public extern String (char [] val);
2433 [MethodImplAttribute (MethodImplOptions.InternalCall)]
2434 public extern String (char c, int count);
2436 [MethodImplAttribute (MethodImplOptions.InternalCall)]
2437 private extern static string InternalJoin (string separator, string[] value, int sIndex, int count);
2439 [MethodImplAttribute (MethodImplOptions.InternalCall)]
2440 private extern String InternalReplace (String oldValue, string newValue, CompareInfo comp);
2442 [MethodImplAttribute (MethodImplOptions.InternalCall)]
2443 private extern void InternalCopyTo (int sIndex, char[] dest, int destIndex, int count);
2445 [MethodImplAttribute (MethodImplOptions.InternalCall)]
2446 private extern String[] InternalSplit (char[] separator, int count, int options);
2448 [MethodImplAttribute (MethodImplOptions.InternalCall)]
2449 private extern String InternalTrim (char[] chars, int typ);
2451 [MethodImplAttribute (MethodImplOptions.InternalCall)]
2452 private extern int InternalLastIndexOfAny (char [] anyOf, int sIndex, int count);
2454 [MethodImplAttribute (MethodImplOptions.InternalCall)]
2455 private extern String InternalPad (int width, char chr, bool right);
2457 [MethodImplAttribute (MethodImplOptions.InternalCall)]
2458 internal extern static String InternalAllocateStr (int length);
2460 [MethodImplAttribute (MethodImplOptions.InternalCall)]
2461 internal extern static void InternalStrcpy (String dest, int destPos, String src);
2463 [MethodImplAttribute (MethodImplOptions.InternalCall)]
2464 internal extern static void InternalStrcpy (String dest, int destPos, char[] chars);
2466 [MethodImplAttribute (MethodImplOptions.InternalCall)]
2467 internal extern static void InternalStrcpy (String dest, int destPos, String src, int sPos, int count);
2469 [MethodImplAttribute (MethodImplOptions.InternalCall)]
2470 internal extern static void InternalStrcpy (String dest, int destPos, char[] chars, int sPos, int count);
2472 [MethodImplAttribute (MethodImplOptions.InternalCall)]
2473 private extern static string InternalIntern (string str);
2475 [MethodImplAttribute (MethodImplOptions.InternalCall)]
2476 private extern static string InternalIsInterned (string str);